Create A Basic Multiplayer Game In Phaser 3 With Socket.io – Part 2 (original) (raw)

In part one of this tutorial, we created our Node.js server, set up a basic Phaser game, and added Socket.io to allow communication between the two. If you missed it, you can find part one here. In this tutorial, we are going to focus on adding the client-side code that will: add and remove players from the game, handle player input, and allow players to pick up collectibles.

You can see what we will be completed below:

2018 04 22 1732 - Create A Basic Multiplayer Game In Phaser 3 With Socket.io - Part 2

Let’s get started!

Course Files and Versions

If you didn’t complete part one and would like to continue from here, you can find the code for part one here.

You can download all of the files associated with the source code for this part here.

At the time this tutorial was written, the following versions were used. You may need to use these versions to have the same results from this tutorial.

Did you come across any errors in this tutorial? Please let us know by completing this form and we’ll look into it!

Python Blog Image - Create A Basic Multiplayer Game In Phaser 3 With Socket.io - Part 2

Adding players – Client

With our server code for adding players in place, we will now work on the client side code. The first thing we need to do is load the asset that will be used for the player. For this tutorial, we will be using some images from Kenny’s Space Shooter Redux asset pack. The asset for the game can be downloaded here.

In the public folder, create a new folder called assets and place the image there. To load the image in our game, you will need to add the following line inside the preload function in game.js:

this.load.image('ship', 'assets/spaceShips_001.png');

With the ship image loaded, we can now create the player in our game. In part one of this tutorial, we set up the Socket.io connection to emit a currentPlayers event anytime a new player connected to the game, and in this event, we also passed a players object that contains all of the current players. We will use this event to create our player.

Update the create function in game.js to match the following:

function create() { var self = this; this.socket = io(); this.socket.on('currentPlayers', function (players) { Object.keys(players).forEach(function (id) { if (players[id].playerId === self.socket.id) { addPlayer(self, players[id]); } }); }); }

Let’s review the code we just added:

Now, let’s add the addPlayer function to game.js. Add the following code to the bottom of the file:

function addPlayer(self, playerInfo) { self.ship = self.physics.add.image(playerInfo.x, playerInfo.y, 'ship').setOrigin(0.5, 0.5).setDisplaySize(53, 40); if (playerInfo.team === 'blue') { self.ship.setTint(0x0000ff); } else { self.ship.setTint(0xff0000); } self.ship.setDrag(100); self.ship.setAngularDrag(100); self.ship.setMaxVelocity(200); }

In the code above we:

If you refresh your browser, you should see your player’s ship appear on the screen. If your server is not running, you can start it by navigating to your project folder in the command line, and run the following command: node server.js.

2018 04 20 2141 - Create A Basic Multiplayer Game In Phaser 3 With Socket.io - Part 2
Also, if you refresh your game, you should see your ship appear in different locations, and it should randomly be red or blue.

Adding other players

Now that we have our player appearing in the game, we will add the logic for displaying other players in our game. In part one of the tutorial, we also set up Socket.io to emit a newPlayer and a disconnect event. We will use these two events, and our current logic for the currentPlayers event to add and remove other players from our game. To do this, update the create function to match the following:

function create() { var self = this; this.socket = io(); this.otherPlayers = this.physics.add.group(); this.socket.on('currentPlayers', function (players) { Object.keys(players).forEach(function (id) { if (players[id].playerId === self.socket.id) { addPlayer(self, players[id]); } else { addOtherPlayers(self, players[id]); } }); }); this.socket.on('newPlayer', function (playerInfo) { addOtherPlayers(self, playerInfo); }); this.socket.on('disconnect', function (playerId) { self.otherPlayers.getChildren().forEach(function (otherPlayer) { if (playerId === otherPlayer.playerId) { otherPlayer.destroy(); } }); }); }

Let’s review the code we just added:

Now, let’s add the addOtherPlayers function to our game. Add the following code to the bottom of game.js:

function addOtherPlayers(self, playerInfo) { const otherPlayer = self.add.sprite(playerInfo.x, playerInfo.y, 'otherPlayer').setOrigin(0.5, 0.5).setDisplaySize(53, 40); if (playerInfo.team === 'blue') { otherPlayer.setTint(0x0000ff); } else { otherPlayer.setTint(0xff0000); } otherPlayer.playerId = playerInfo.playerId; self.otherPlayers.add(otherPlayer); }

This code is very similar to the code we added in the addPlayer() function. The main difference is that we added the other player’s game object to our otherPlayers group.

The last thing we need to do before we can test our game logic for adding the other players is, we need to load an asset for these players. You can find that asset here.

This image will need to be placed in the assets folder. Once the image is there, we can load this image into our game. In the preload function, add the following line:

this.load.image('otherPlayer', 'assets/enemyBlack5.png');

Now, if you refresh your game in the browser, you should still see your player’s ship. If you open another tab or browser and navigate to your game, you should see multiple sprites appear in the game window, and if you close one of those games you should see that sprite disappear from the other games.

2018 04 20 2233 - Create A Basic Multiplayer Game In Phaser 3 With Socket.io - Part 2

Handling player input

With the logic for displaying all of the players in the game in place, we will move on to handling player input and allow our player to move. We will handle player input by using Phaser’s built-in keyboard manager. To do this, add the following line at the bottom of the create function in game.js:

this.cursors = this.input.keyboard.createCursorKeys();

This will populate the cursors object with our four main Key objects (up, down, left, and right), which will bind to those arrows on the keyboard. Then, we just need to see if these keys are being held down in the update function.

Add the following code to the update function in game.js:

if (this.ship) { if (this.cursors.left.isDown) { this.ship.setAngularVelocity(-150); } else if (this.cursors.right.isDown) { this.ship.setAngularVelocity(150); } else { this.ship.setAngularVelocity(0); }

if (this.cursors.up.isDown) {
  this.physics.velocityFromRotation(this.ship.rotation + 1.5, 100, this.ship.body.acceleration);
} else {
  this.ship.setAcceleration(0);
}

this.physics.world.wrap(this.ship, 5);

}

In the code above, we did the following:

If you refresh your game, you should now be able to move your ship around the screen.

Apr 20 2018 23 25 04 - Create A Basic Multiplayer Game In Phaser 3 With Socket.io - Part 2

Handling other player movements

Now that our player is able to move, we will move on to handling other player’s movement in our game. In order to track other player movements, and move their sprites in our game, we will need to emit a new event any time a player moves. In the update function of game.js add the following code inside the if statement:

// emit player movement var x = this.ship.x; var y = this.ship.y; var r = this.ship.rotation; if (this.ship.oldPosition && (x !== this.ship.oldPosition.x || y !== this.ship.oldPosition.y || r !== this.ship.oldPosition.rotation)) { this.socket.emit('playerMovement', { x: this.ship.x, y: this.ship.y, rotation: this.ship.rotation }); }

// save old position data this.ship.oldPosition = { x: this.ship.x, y: this.ship.y, rotation: this.ship.rotation };

Let’s review the code we just added:

Next, we will need to update the Socket.io code in server.js to listen for the new playerMovement event. In server.js, add the following code below the socket.io(‘disconnect’) code:

// when a player moves, update the player data socket.on('playerMovement', function (movementData) { players[socket.id].x = movementData.x; players[socket.id].y = movementData.y; players[socket.id].rotation = movementData.rotation; // emit a message to all players about the player that moved socket.broadcast.emit('playerMoved', players[socket.id]); });

When the playerMovement event is received on the server, we update that player’s information that is stored on the server, emit a new event called playerMoved to all other players, and in this event we pass the updated player’s information.

Lastly, we will need to update the client side code to listen for this new event, and when this event is emitted, we will need to update that player’s sprite in the game. In the create function in game.js add the following code:

this.socket.on('playerMoved', function (playerInfo) { self.otherPlayers.getChildren().forEach(function (otherPlayer) { if (playerInfo.playerId === otherPlayer.playerId) { otherPlayer.setRotation(playerInfo.rotation); otherPlayer.setPosition(playerInfo.x, playerInfo.y); } }); });

Now, if you restart your server and open up the game in two different tabs or browsers, and move one of the players, you should see that player move in the other game.

Apr 21 2018 22 47 59 - Create A Basic Multiplayer Game In Phaser 3 With Socket.io - Part 2

Collecting Stars

With the game now handling other player movements, we need to give the players a goal. For this tutorial, we are going to add a star collectible to the game for the players to collect, and when they do, their team will get ten points. To do this, we will need to add a few more Socket.io events to our game, and we will start with the server logic first.

First, we will add two new variables to our server: star and scores. The star variable will be used to store the position of our star collectible, and the scores variable will be used to keep track of both team’s score. In server.js , add the following code beneath the var players = {}; line:

var star = { x: Math.floor(Math.random() * 700) + 50, y: Math.floor(Math.random() * 500) + 50 }; var scores = { blue: 0, red: 0 };

Next, we will emit two new events when a new player connects to the game, starLocation, and scoreUpdate. We will use these two events to send the new player the star collectible’s location, and the current scores of both teams. In server.js, add the following code beneath the socket.emit(‘currentPlayers’, players); line:

// send the star object to the new player socket.emit('starLocation', star); // send the current scores socket.emit('scoreUpdate', scores);

Lastly, we are going to listen for a new event called starCollected, which will be triggered when a player collects the star. When this event is received, we will need to update the correct team’s score, generate a new location for the star, and let each player know about the updated scores and the stars new location.

In server.js, add the following code beneath the socket.on(‘playerMovement’) code:

socket.on('starCollected', function () { if (players[socket.id].team === 'red') { scores.red += 10; } else { scores.blue += 10; } star.x = Math.floor(Math.random() * 700) + 50; star.y = Math.floor(Math.random() * 500) + 50; io.emit('starLocation', star); io.emit('scoreUpdate', scores); });

Now that our server logic has been updated, we will need to update the client side code in game.js. In order to display the two teams scores, we will use Phaser’s Text Game Object. In the create function, add the following code at the bottom of the function:

this.blueScoreText = this.add.text(16, 16, '', { fontSize: '32px', fill: '#0000FF' }); this.redScoreText = this.add.text(584, 16, '', { fontSize: '32px', fill: '#FF0000' });

this.socket.on('scoreUpdate', function (scores) { self.blueScoreText.setText('Blue: ' + scores.blue); self.redScoreText.setText('Red: ' + scores.red); });

Let’s review the code we just added:

If you refresh your game, you should see each teams’ score:

2018 04 22 1703 - Create A Basic Multiplayer Game In Phaser 3 With Socket.io - Part 2
Finally, the last thing we need to add is the logic for the star collectible. First, we will need to load the asset for the star collectible in the preload function. The asset for the game can be downloaded here.

In the preload function, add the following code:

this.load.image('star', 'assets/star_gold.png');

Then, in the create function, add the following code below the score update code we just added:

this.socket.on('starLocation', function (starLocation) { if (self.star) self.star.destroy(); self.star = self.physics.add.image(starLocation.x, starLocation.y, 'star'); self.physics.add.overlap(self.ship, self.star, function () { this.socket.emit('starCollected'); }, null, self); });

In the code above we listened for the starLocation event, and when it is received we do the following:

If you refresh your game, you should now see the star collectible on the screen:

2018 04 22 1728 - Create A Basic Multiplayer Game In Phaser 3 With Socket.io - Part 2 Conclusion

With the star collectible added to the game, that brings this tutorial to a close. In summary, this tutorial showed you how to create a basic multiplayer game in Phaser using Socket.io and Node.js.

I hope you enjoyed this tutorial and found it helpful. If you have any questions, or suggestions on what we should cover next, let us know in the comments below.