This is part 2 of a 3 part series. To view part 1, click here. If you get lost at any time during this tutorial, you can always refer to the solution code here.
In part 1, we completed our boilerplate including an express server, a Phaser 3 front end, and hooked up our socket.io. In part 2, we will create scenes, add entities, and introduce logic for sockets and multiple private game rooms.
Create the MainScene:
Inside of your src folder, create another folder called scenes. Inside of scenes, create a file called Mainscene.js. Inside of Mainscene setup your class:
Hop over to src/index.js and lets import our Mainscene in, add it, and start it. We add all of our scenes into this index.js, but will only be starting Mainscene from it as it’s the first scene we want to load when our game begins.
Back in Mainscene, let’s start filling it out with some code. We will be dancing between Mainscene and our socket/index.js so I’d recommend having them both open.
Inside of the preload method, load the assets that you wish to use for your players and the background image. Inside of create, we safe the context of ‘this’ to the variable scene, and we add the image for the background. Lastly, we create our socket on the Mainscene class. Be sure make a folder called assets where you will add the images for your background and players.
If you npm run start-dev you should now see the background we added, and in your terminal you should see “A socket connection to the server has been made: (socket number)”.
Create Private Game Rooms:
Moving to our socket/index.js lets start writing what we want to happen when we connect. Because this is a live-multiplayer game, we need to have state that comes from the server and unifies game data between clients. At the top of this index.js add the following gameRooms object:
Since this is an Among us inspired game, we want players to be able to join private game rooms with their friends using a game code. Let’s create a waiting room where they can retrieve and enter a game code. Inside of src/scenes create a new scene file called WaitingRoom.js. Inside of Mainscene, we will launch this WaitingRoom scene from the create method, so that it appears right when we begin our game. The reason we do this instead of starting it on the game instance is because we can pass it our socket so that we don’t create a new one on WaitingRoom (creating a new socket io would tell the server we have another player instead of connect the same one), and also so that we can show the Mainscene image running in the background.
Since we passed our socket to waiting room, inside src/scenes/WaitingRoom.js we must initialize it with phasers init() method.
Add the following graphics to WaitingRoom:
The codeform that we preload is available in the solution code’s assets/text here. It is a simple html document that includes an input field which we can extract input from, from inside our input element. Add the input field:
Here we are putting a click event listener on our entire codeform, and inside of the click function, we are checking to see if what was clicked was our submit button, named “enterRoom” in our codeform html. Then, we save the input that the user has entered to a variable called input. We “emit” the input.value(the text the user entered) from our socket, with the identifier ‘isKeyValid’. Currently, we don’t have a listener for this emit method, so let’s add one to our server/socket/index.js:
When we say Socket.on, and pass it the “isKeyValid” string, we are listening for an event when that specific key is emitted, when our socket.on hears it, it has a call back function that takes the input that we passed this emit inside out waiting room. then, we check our state object keys to see if this game exists. If it exists, we create a new socket.emit called keyIsValid, if it doesn’t exist, we create a new socket.emit called keyNotValid. Back in our WaitingRoom, we need to listen for this emits now. Noticing a pattern?
Notice on the fourth line we have a new socket.emit called ‘joinRoom’. You can ignore this for now, we will return to it in a few steps. Let’s add functionality to our request button, and create the notValidText that we just referenced, you can put these above the socket listeners we just created.
Notice the socket.emit ‘getRoomCode’, lets create a listener on our server/socket/index.js
Inside our listener, we create a variable that stores a random code generated by the function we add below our sockets called codeGenerator. After creating this variable, we check to see if this code already exists as a room on our state object, if it does we generate a new code and save it to the variable, if not we keep the one we have. We then tell our gameRooms state object to create a new key that is our game room code and make the value an object that contains all the data we will need for out game. Lastly, we emit a new socket saying the room is created, with the roomKey attached to it. Now we must add a listener for this back in our WaitingRoom.
When we hear ‘roomCreated’, we save the roomKey to our local state (on the WaitingRoom scene) and set the roomKeyText on our interface to the room key we get from the callback function.
The user flow here is that a user can generate a game room key, enter it into the input field, and submit it to enter the game. Now we must add the logic for joining the game.
Following our ‘joinRoom’ socket emit, that includes the room key in the emit, let’s create a listener back in our server/socket/index.js. At the top of our io.on function, but below our socket connection console.log, include our ‘joinRoom’ listener. Inside of the listener, we use the socket.join method and pass it the room key, which will add us to the room!
Now when you npm run start-dev you should be able to generate a room key, submit it, and enter into Mainscene.
This concludes part 2 of this tutorial. Continue to part three here, where we will create our players and add our game functionality.