DEV Community

zigzagoon1
zigzagoon1

Posted on

Using Phaser.js In Your React/Rails Webapp

Before We Start

When I was researching how to do this for my game, I noticed a lot of blogs about it didn't specify their setup or versions, which meant that their solutions might not work but I didn't know until I tried. So: I'm using React version 18.2.0, Phaser 3.70.0, and Ruby 2.7.4. We'll go through setup, EventEmitters, and some other small things. This method does not use Redux, although using Redux is a useful way for managing state that is shared by Phaser and React.

Still Here

So, you got through the fun setup for creating a webapp with a React frontend and Ruby on Rails backend- congrats! I like this combination for managing websites as well.

Now you want to add a game made with Phaser.js to your site. I had to do that myself, so I thought I'd share my process in case it can help others get their games on their websites. Fortunately, it's not too difficult.

I'll also go over setting up a Phaser EventEmitter that can be used to communicate between your React components and your Phaser game, so you can save data like user scores and other things in your backend. But first things first!

Installing Phaser.js Into Your React/Rails Webapp

Your setup may be slightly different than mine, so I'll show what mine looks like to help clarify where things go.

Root
  -app
    -channels
    -controllers
    -jobs
    -mailers
    -models
    -serializers
    -views
  -client
    -node_modules
    -public
    -src
    -package-lock.json
    -package.json
  -config 
    -environments
    ...
    -routes
  -db
  -lib
  -node_modules
  -storage
  -tmp
  -config.ru, Gemfile, package-lock.json, package.json,
    Procfile, Rakefile, webpack.config.js are all on this
    level too, among others.
Enter fullscreen mode Exit fullscreen mode

Note that all Rails API related folders are contained within the app folder, and all frontend-related folders are in client. The only backend things that are separate from app are the db (database) and config folders; db is where you would have your migrations and schema and seeds.rb file, and config contains your routes.rb file.

In order to use phaser, you need to install it using npm install phaser in your terminal. Make sure to do this within the client folder, as Phaser will run in the frontend, or alternatively you can run the command while in your root folder by doing npm install --prefix client. Next, navigate to your index.html file (you can also add this next bit to your App.js if you'd prefer). Once there, create a div. This will be the container for your Phaser game, so give it an id of "phaser-container". You'll need to reference this element by it's id later to tell Phaser which element is its parent element.

Next we can set up our Phaser game config. I chose to make my game config a React component, so I imported React and useEffect. You'll also need to import Phaser, and any game scenes that you need. This is what my Phaser game config looks like in my project:

const PhaserGameConfig = ({gameType, onGamePlayed}) => {
    useEffect(() => {
      const config = {
        type: Phaser.AUTO,
        width: 700,
        height: 500,
        backgroundColor: '#ffffff',

        physics: {
            default: 'arcade',
            arcade: {
                gravity: {y: 0}, 
                debug: true,
            },
        },
        parent: 'phaser-game', 
        scene: 'TicTacToe' //or, in my case, getScene()
      };
    new Phaser.Game(config);
    }, []);

    return <div id="phaser-game"></div>;
}

export default PhaserGameConfig;
Enter fullscreen mode Exit fullscreen mode

Now, you may have noticed that I passed the prop gameType to my component (there's also another prop, onGamePlayed, which I'll get to shortly). In my case, my site has multiple games on it, so I created a getScene method within my component, which returned a different Phaser scene depending on the value of gameType.

Now, wherever you want your Phaser game or games to be displayed, you can simply return a PhaserGameConfig component, and in my case, pass the game's name as a prop to the component.

To have access to any assets, such as images or audio, in your Phaser scene, simply put the assets into the public folder that's within the client folder.

The communication between Phaser and React that I used involves the use of Phaser's EventEmitter. The example I'm going to show worked for my game, but it may take some tinkering to get it working in yours depending on what your needs are for communicating between the two. I hope showing how I did it is helpful. If anyone has an idea for a better way of solving the problem below, I'd love to hear about it in the comments!

For me, I simply needed a way to detect when a user had played an entire game of TicTacToe so that I could add to a count of the number of games the user has played so far.

In my project, I added a few lines to my PhaserGameConfig component that allowed me to achieve this:

//PhaserGameConfig component as shown above...
//after the useEffect 

const handler = () => {
    onGamePlayed();

eventEmitter.on("gameEnd", handler);

return <div id="phaser-game"></div>
} //bracket marking the end of the PhaserGameConfig component

export const eventEmitter = new Phaser.Events.EventEmitter();

export default PhaserGameConfig;
} 
Enter fullscreen mode Exit fullscreen mode

Now, in my Phaser scene for TicTacToe, I import that eventEmitter like this: import {eventEmitter} from '../PhaserGameConfig' (in my case, my phaser scenes are not in the same folder as my components). Then, at the start of create(), I added this.eventEmitter = eventEmitter. Voila! Now, after a game finishes in my TicTacToe scene, I can simply emit my event like this:

this.eventEmitter.emit("gameEnd);
Enter fullscreen mode Exit fullscreen mode

And my PhaserGameConfig component calls the callback function passed as a prop to the component, adding to the count of the number of games played by the user.

Now, for more complicated event handling where you have more than one or two events, or events that need to be handled differently depending on the game, I would create my eventEmitter as a context to be used with useContext. This helps to avoid lots of prop drilling, and decouples the event handling from the PhaserGameConfig component itself. This would probably require the use of useMemo, useCallback and React.memo where appropriate to optimize a bit and avoid a lot of unnecessary re-renders.

Have a better idea for how to handle communication between React and Phaser? Let me know in the comments!

Top comments (0)