Skip to content

How to create a platform game with Phaser.js

In this tutorial I’ll use Phaser.js to create a platform game.

The player can move around with the left/right arrow keys, and jump with the up arrow key. The goal is to pick up all the stars in the game:

When the player picks up all the stars, we’re going to display “Game over” on top, and there’s nothing else to do.

It’s very very simple, but it’s a start of a game that can be very fun to continue making, and it’s a great way to demonstrate both Phaser and how JavaScript can work as a game creation programming language.

Setup the project

Create an empty folder, and run

npm init -y

to initialize a minimal package.json file.

Then run

npm install phaser

Install Parcel if you haven’t:

npm install -g parcel-bundler

Now create an app.js file, and run

parcel watch app.js

This will tell Parcel to build our game in the dist/app.js file.

Create an index.html file with this content:

<!DOCTYPE html>
<html>
  <head>
    <script src="./dist/app.js"></script>
  </head>
</html>

Install browser-sync to run an HTTP server with the content of this folder:

npm install -g browser-sync

then run

browser-sync start --server --files "."

The above command watches all files in the current folder (and all subfolders) for changes, and launches a web server on port 3000, automatically opening a browser window to connect to the server.

Any time you change a file, the browser will refresh, so we can be more speedy in our development process.

Great! We can start.

Initialize Phaser

Open app.js and write:

import Phaser from 'phaser'

Let’s add a minimal configuration:

const config = {
  width: 800,
  height: 600,
  backgroundColor: 0xffffff,
  scene: {
    preload,
    create,
    update
  },
  physics: {
    default: 'arcade',
    arcade: {
      gravity: { y: 300 },
      debug: false
    }
  }
}

const game = new Phaser.Game(config)

Now create the 3 functions preload(), create() and update():

function preload() {

}

function create() {

}

function update() {

}

Add the artwork

I call it “artwork”, but I made it and it’s the ugliest artwork you’ll ever find.

But it works for our simple game. Download those files and save them as:

ground.png

island.png

player.png

star.png

Create an assets folder, and put them in there.

Create a canvas

Now get back to app.js.

The first thing we’ll do is to load the assets in preload():

function preload() {
  this.load.image('ground', 'assets/ground.png')
  this.load.image('island', 'assets/island.png')
  this.load.image('star', 'assets/star.png')
  this.load.spritesheet('player', 'assets/player.png', {
    frameWidth: 32,
    frameHeight: 48
  })
}

We assign a label to each asset. The first 3 are loaded using this.load.image(), the last one using this.load.spritesheet() because the image contains several little images, that we’ll use to animate the player.

Therefore we must also set the dimensions of each image in the sprite sheet (32x48 pixels).

Create the platforms

Let’s create the platforms now.

We’re going to create a new physics static group to hold them, because they are not moving objects: they are still.

let platforms = this.physics.add.staticGroup()

Then we add the ground:

platforms.create(400, 588, "ground")

And 5 islands:

platforms.create(600, 450, "island")
platforms.create(50, 250, "island")
platforms.create(650, 220, "island")
platforms.create(250, 520, "island")
platforms.create(250, 320, "island")

The numbers here indicate the X and Y coordinates in the 2D space, relative to the top left corner, and refer to the middle of each image.

This is what you should see at this point:

Add the player

Now let’s add the player.

Create a variable on top of the file, outside any function, called player:

let player

Then in create() I’m going to add it near the middle bottom of the screen:

player = this.physics.add.sprite(380, 500, "player")

We set it to have a small bounce:

player.setBounce(0.2)

so it will quickly stand still after being put in the screen, and we set it to not go outside of the screen:

player.setCollideWorldBounds(true)

In this way it will appear and after a couple bounces it will sit at the bottom of the screen:

However, we want the player to stay in top of the ground.

We can do this by adding a collider physics rule between the player and the platforms:

this.physics.add.collider(player, platforms)

In this way, the player will not just stay on top of the ground, but also of the other 5 platforms (islands) we have floating on the screen:

Make the player move

Now let’s make it possible for the player to move.

Let’s add a variable on top of the file, near the player declaration:

let cursors

Then in create() add:

cursors = this.input.keyboard.createCursorKeys()

to allow access to the keyboard events.

Now in the update() function, which is now empty, we’re going to inspect this variable:

function update() {
  if (cursors.left.isDown) {
    player.setVelocityX(-160)
  } else if (cursors.right.isDown) {
    player.setVelocityX(160)
  } else {
    player.setVelocityX(0)
  }
}

update() is continuously called by the game loop, so whenever one key is down we change the player velocity (speed) on the X axis. If no key is pressed, we set it to stand still.

Animate the player

Let’s now make the player image change when it’s moving.

We create 3 animations in create(): one when the player is still, one when it’s moving to the left, one when it’s moving to the right:

this.anims.create({
  key: 'still',
  frames: [{ key: 'player', frame: 4 }],
  frameRate: 20
})

this.anims.create({
  key: 'left',
  frames: this.anims.generateFrameNumbers('player', { start: 0, end: 3 }),
  frameRate: 10,
  repeat: -1
})

this.anims.create({
  key: 'right',
  frames: this.anims.generateFrameNumbers('player', { start: 5, end: 8 }),
  frameRate: 10,
  repeat: -1
})

still is not really an animation, because it only has one frame. But left and right are composed by 4 frames each, and they are infinitely repeated to simulate walking.

Now we need to activate those animations in update():

function update() { 
  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('still')
  }
}

The true parameter for left and right sets the animation to loop.

Jump

To allow the player to jump, add this code into update():

if (cursors.up.isDown && player.body.touching.down) {
  player.setVelocityY(-330)
}

Whenever the player sits on the bottom (player.body.touching.down) and the up arrow is pressed, we push it to the top. Gravity will simulate a jump like the ones we do in the real world.

Add the stars

Let’s now add the stars to the screen.

We add 8 stars by creating a physics group, and we make them collide with the platforms, so they’ll sit on top of them:

let stars = this.physics.add.group()
stars.create(22, 0, "star")
stars.create(122, 0, "star")
stars.create(222, 0, "star")
stars.create(322, 0, "star")
stars.create(422, 0, "star")
stars.create(522, 0, "star")
stars.create(622, 0, "star")
stars.create(722, 0, "star")

this.physics.add.collider(stars, platforms)

Allow the player to pick up the stars

To make the player pick up the stars, we’re going to add an overlap physics effect in create() that is fired when the player meets a star:

this.physics.add.overlap(player, stars, (player, star) => {}, null, this)

When this happens, we hide the star:

this.physics.add.overlap(
  player,
  stars,
  (player, star) => {
    star.disableBody(true, true)
  },
  null,
  this
)

Let’s make a stars counter. When one star is picked up, we increment it:

let score = 0

this.physics.add.overlap(
  player,
  stars,
  (player, star) => {
    star.disableBody(true, true)
    score += 1
  },
  null,
  this
)

and we can display it on top of the screen:

let score = 0
let scoreText = this.add.text(16, 16, "Stars: 0", {
  fontSize: "32px",
  fill: "#000",
})

this.physics.add.overlap(
  player,
  stars,
  (player, star) => {
    star.disableBody(true, true)
    score += 1
    scoreText.setText("Stars: " + score)
  },
  null,
  this
)

That’s it! Our little game works!

Here is how can I help you: