Adding multiplayer to our game (or how I learned to tame the Pikachu)

  • Post author:
  • Post category:Beginner

In this post, we’ll learn how to add a second player to our game. But not just any player…. Pikachu!! We’ll explore how to find Pokemon sprite sheets, customize them to our needs and add them to our game.

You can play a live version of the game (link) or check out the completed code as a Glitch project (link).

Getting started

Previously, we created a basic platformer game in Javascript, from scratch, as the first lesson in our Platformer course.

Today we’ll add multiplayer to our game. This tutorial builds on the first lesson, so be sure to complete the first lesson. For this lesson, we’ll use a clean copy of the platformer from lesson one. Click on the Glitch project (don’t forget to click the “Remix” button). As always, if you ever get stuck or have any questions, feel free to reach out to us!

Finding Pikachu on the Internet

One of the most enjoyable aspects of making our own game is adding whatever creatures we like. If you’re a fan of Pokemon, it turns out there are a ton of sites out there that have Pokemon sprites. For example, if we search for “pikachu sprite sheet” on pikpng.com (link), we can find this sprite sheet:

Look closely at the second row of Pikachu animations: it shows Pikachu walking. In our Glitch project, I’ve already included a cropped version of the animation in the assets folder, so we can just use that in our game:

The Pikachu animation is already included in our Glitch project.

In a later lesson we’ll learn how to use some free tools to crop out animations for our sprites.

Adding Pikachu to our game

Now let’s load the sprite sheet into memory. In the code snippet below, see how I set the frameWidth and frameHeight to be the same size as a single Pikachu image in the sprite sheet (39×38 pixels):

function preload() {
// (I've skipped showing all the code we wrote before)
// Add the code below until the "stop" comment
this.load.spritesheet(
'pikachu',
'https://cdn.glitch.com/adbffbc8-16bf-434f-b487-0def82849e5d%2Fpikachu-spritesheet.png?v=1629776578033',
{ frameWidth: 39, frameHeight: 38 }
);
// stop
}

Be sure to use your own URL, by clicking on the pikachu-spritesheet.png in your assets folder and copying the URL there.

Next, create a player2 variable in the main.js file:

import Phaser from '/phaser.js';
var player;
// Add the code until the "stop" comment
var player2;
// stop

In the create() function we’ll define the player2 variable, similar to the first player:

function create() {
// (I've skipped showing all the code we wrote before)
// Add the following code until the "stop" comment
player2 = this.physics.add.sprite(500, 450, 'pikachu');
player2.setBounce(0.2);
player2.setCollideWorldBounds(true);
player2.isAlive = true;
this.physics.add.collider(player2, platforms);
this.anims.create({
key: 'left2',
frames: this.anims.generateFrameNumbers('pikachu', {
start: 0,
end: 3,
}),
frameRate: 10,
repeat: -1,
});
this.anims.create({
key: 'right2',
frames: this.anims.generateFrameNumbers('pikachu', {
start: 0,
end: 3,
}),
frameRate: 10,
repeat: -1,
});
// "stop"
}

In lines 12–30 we create two new animations, left2 and right2. These animations will animate Pikachu by playing frames 0 through 3.

Now let’s reload our game; it should show Pikachu!

Controlling Pikachu

Now let’s add some keys to control Pikachu. For Player One, we used the arrow (aka “cursor”) keys. For Player Two, let’s use the “WASD” keys. First declare a keys variable near the top of the main.js file:

import Phaser from '/phaser.js';
var player;
var player2;
var cursors;
var stars;
var score = 0;
var scoreText;
var bombs;
// Add the code below
var keys;

Now we’ll define the keys variable in the create() function:

function create() {
// (I've skipped showing all the code we've written before)
// Add this code below
keys = this.input.keyboard.addKeys('W, A, D');
}

In line 5 above, addKeys() creates an object that will respond when we press any of the W, A or D keys. We’ll add code that reacts to those key presses in a moment.

But first, we’re going to refactor our code a bit. As we add more functionality to our game, we’ll want to reorganize it so that it’s clearer and more flexible for future additions; that’s called “refactoring”. In the update() function, we’re going to move all that code into a new function updatePlayerOne(). This way, we can later add a function updatePlayerTwo().

function updatePlayerOne() {
// This is the code you moved from the update() function
if (cursors.left.isDown) {
player.setVelocityX(-160);
player.anims.play('left', true);
} else if (cursors.right.isDown) {
player.setVelocityX(160);
player.anims.play('right', true);
} else {
player.setVelocityX(0);
player.anims.play('turn');
}
if (cursors.up.isDown && player.body.touching.down) {
player.setVelocityY(-310);
}
}
function update() {
updatePlayerOne();
}

Next, let’s create a new function updatePlayerTwo(), and call it in the update()function:

function update() {
updatePlayerOne();
// Add the following line of code
updatePlayerTwo();
}
function updatePlayerTwo() {
if (keys.W.isDown) {
if (player2.body.touching.down) {
player2.setVelocityY(-310);
}
} else if (keys.A.isDown) {
player2.flipX = true;
player2.setVelocityX(-160);
player2.anims.play('left2', true);
} else if (keys.D.isDown) {
player2.flipX = false;
player2.setVelocityX(160);
player2.anims.play('right2', true);
} else {
player2.setVelocityX(0);
player2.anims.stop();
}
}

Similar to the controls for Player One, when we press the W key, then set Player Two’s velocity upward (-310). One interesting bit of code is line 7 and line 11, which flips our sprite. We have to flip the sprite because our Pikachu sprite sheet is only facing to the right, so, when moving left, we flip the image.

Let’s try out our new controls!

Making Pikachu collect stars and freeze to bombs

Now let’s enable Pikachu to collect stars and get frozen by bombs. This is similar logic to Player One. Take a moment to think about what game logic enabled this, then see if you got it right, below!

function create() {
// (I've skipped showing all the code we wrote before)
// Add the following code until the "stop" comment
this.physics.add.overlap(player2, stars, collectStar);
this.physics.add.collider(player2, bombs, hitBomb, null, this);
// stop
}

In lines 5 and 6, whenever player2 (our Pikachu) collides with stars, then call the same collectStar() function. Similarly, if player2 collides with bombs, then call the same hitBomb function.

Now our Pikachu can collect stars and be hit by the bomb:

Pikachu and collect stars and be hit by bombs

Bonus: teamwork!

Notice if either Pikachu or Player One touches a bomb, then it’s game over. Wouldn’t it be awesome if the other player could “unfreeze” their teammate? Kinda like freeze tag. Let’s add this logic and then wrap up our post on multiplayer.

First, we’ll need to track if our players are alive, by adding the following code at the bottom of our create() function:

function create() {
// (I've skipped showing all the code we wrote before)
// Add the following code until the "stop" comment
player.isAlive = true;
player2.isAlive = true;
// stop
}

Lines 5–6 define an isAlive variable, which will track if, well, our players are alive. We’ll set them to true at the start of our game.

Next, we’ll change our update() function to check if our players are alive, before updating them:

function update() {
if (player.isAlive) {
updatePlayerOne();
}
if (player2.isAlive) {
updatePlayerTwo();
}
}

Finally, we’ll replace the hitBomb() function with the following code:

function hitBomb(pl, bomb) {
pl.isAlive = false;
pl.setTint(0xff0000);
pl.anims.pause();
pl.setVelocityX(0);
if (!player.isAlive && !player2.isAlive) {
this.physics.pause();
this.add.text(200, 100, 'game over!', {
fontSize: '64px',
fill: '#000',
});
}
}

This code is worth an explanation. First, notice we changed the first variable name to pl. This is to prevent a “name collision” with the global variable player. Later, we’ll talk about how to avoid creating these global variables for cleaner, more maintainable code.

Back to the code: whenever player or player2 collides with a bomb, the hitBomb() function is called, and the pl variable is assigned to either player or player2. Lines 2–5 will “freeze” that player and “kill” the player ( pl.isAlive = false ).

Line 7 checks if both players are dead, if so, then pause the game and show the Game Over text.

Now if we run our game, if a bomb kills either player, the other player can continue to collect stars. Let’s add the final touch: if a live player touches a dead one, they’ll come back life. This is the part in freeze tag where you touch a frozen friend to “unfreeze” them.

At the bottom of the create() function, add the following code:

function create() {
// (I've skipped showing all the code we wrote before)
// Add the following code until the "stop" comment
this.physics.add.collider(player, player2, playerCollide, null, this);
// stop
}

Next, we’ll define the playerCollide() function, which gets called whenever both players collide:

function playerCollide(pl1, pl2) {
var deadPlayer = pl1.isAlive ? pl2 : pl1;
deadPlayer.isAlive = true;
deadPlayer.setTint(0xffffff);
}

Line 2 uses that “ternary” operator again (remember from our platformer game tutorial?). deadPlayer will be either player or player2, depending on who is alive. Lines 4–5 will make our deadPlayer alive again, and remove the red tint.

Now if we run our game, after a player is frozen, the other player and unfreeze them by touching them. Awesome teamwork!

Conclusion

Congratulations on adding multiplayer to our game! You are well on your way to learning Javascript and writing fun games too! If you want to review the intro tutorial, check out the first post on writing your first platformer game in Javascript (link) as part of our Platformer course (link).

If you got stuck or have any questions, feel free to contact us. Happy to help debug together.

What’s next? Try adding other characters, with different animation speeds or sizes. Try adding more animations from the sprite sheet. Try adding a big boss to the game. Your imagination is the limit!!!!