Making Your First Phaser 3 Game in Modern Javascript - Part 4

Write better code using modern JavaScript for Phaser 3

by on 6 minute read


Games can be one of the most complicated pieces of software to create. And the most fun.

It helps to use modern best practices to avoid nasty bugs or more easily fix ones that come up.

Doing so saves you time and headaches. Most importantly it will help you make a better game.

In this article, we will show you how to create a game in Phaser 3 using modern JavaScript and best practices.

We'll be creating the same game from the official Making your first Phaser 3 game guide. This way you can compare and contrast the differences.

In Part 3 we handled collisions between platforms and the player and added keyboard controls. Go check it out if you haven't already.

Creating Stars

We ended Part 3 with a character that can move and jump on platforms.

Now we need some stars to collect.

 1
 2
 3
 4
 5
 6
 7
 8
 9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
52
53
54
import Phaser from 'phaser'

const GROUND_KEY = 'ground'
const DUDE_KEY = 'dude'
const STAR_KEY = 'star'

export default class GameScene extends Phaser.Scene
{
	// constructor...

	preload()
	{
		this.load.image('sky', 'assets/sky.png')
		this.load.image(GROUND_KEY, 'assets/platform.png')
		this.load.image(STAR_KEY, 'assets/star.png')
		this.load.image('bomb', 'assets/bomb.png')

		this.load.spritesheet(DUDE_KEY, 
			'assets/dude.png',
			{ frameWidth: 32, frameHeight: 48 }
		)
	}

	create()
	{
		this.add.image(400, 300, 'sky')
		
		const platforms = this.createPlatforms()
		this.player = this.createPlayer()
		const stars = this.createStars()

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

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

	// ...

	createStars()
	{
		const stars = this.physics.add.group({
			key: STAR_KEY,
			repeat: 11,
			setXY: { x: 12, y: 0, stepX: 70 }
		})
		
		stars.children.iterate((child) => {
			child.setBounceY(Phaser.Math.FloatBetween(0.4, 0.8))
		})

		return stars
	}
}

As you can tell, we are using the best practices covered in previous parts!

First, the constant STAR_KEY is used to preload the star asset and then to create a group of equally spaced stars in createStars().

Our createStars() method returns the physics group which is used in create() to add a collider for the stars and platforms.

We've kept the logic the same as the official Phaser guide. Check it out for an explanation of how 12 stars are made in the createStars() method.

Your refreshed browser window at http://localhost:8000 should show stars falling from the sky and landing on the ground and platforms. 👍

Collecting Stars

Next, let's handle collecting stars.

 1
 2
 3
 4
 5
 6
 7
 8
 9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
import Phaser from 'phaser'

// ...

export default class GameScene extends Phaser.Scene
{
	// ...

	create()
	{
		this.add.image(400, 300, 'sky')
		
		const platforms = this.createPlatforms()
		this.player = this.createPlayer()
		const stars = this.createStars()

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

		this.physics.add.overlap(this.player, stars, this.collectStar, null, this)

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

	collectStar(player, star)
	{
		star.disableBody(true, true)
	}

	// ...
}

Collecting stars is handled by detecting an overlap between a star and the player. We do this on line 20.

Continuing on line 20 we see this.collectStar as the third parameter. This is the method Phaser will call when an overlap is detected. The last parameter on this line is this. It specifies the scope from which the collectStar() method will be called.

JavaScript Scopes

Let us know in the comments below if you would like a deeper dive into JavaScript scopes. We also recommend doing a search for "javascript scopes".

In the collectStar(player, star) method we simply disable the physics body. The two parameters passed to disableBody() tells Phaser to disable and hide the GameObject.

Back in the browser, you should be able to move the player over stars and have them disappear! 🎉

Create a ScoreLabel

We want to show the player's score as they collect stars so let's create a ScoreLabel class at src/ui/ScoreLabel.js.

 1
 2
 3
 4
 5
 6
 7
 8
 9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
import Phaser from 'phaser'

const formatScore = (score) => `Score: ${score}`

export default class ScoreLabel extends Phaser.GameObjects.Text
{
	constructor(scene, x, y, score, style)
	{
		super(scene, x, y, formatScore(score), style)

		this.score = score
	}

	setScore(score)
	{
		this.score  = score
		this.updateScoreText()
	}

	add(points)
	{
		this.setScore(this.score + points)
	}

	updateScoreText()
	{
		this.setText(formatScore(this.score))
	}
}

It is not strictly necessary to create a separate class for our score label. The official Phaser guide simply adds a Text object to the Scene.

We've chosen to do so to keep all the score updating logic in one place. This will keep our GameScene class smaller and easier to read.

Encapsulating common logic for reusability is a best practice. Because we made a ScoreLabel class we can use it again in a different Scene without duplicating code–staying DRY!

Keeping Score

With our ScoreLabel in hand, we can add it to our GameScene.

 1
 2
 3
 4
 5
 6
 7
 8
 9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
52
53
54
import Phaser from 'phaser'

import ScoreLabel from '../ui/ScoreLabel'

// constants ...

export default class GameScene extends Phaser.Scene
{
	constructor()
	{
		super('game-scene')

		this.player = undefined
		this.cursors = undefined
		this.scoreLabel = undefined
	}

	create()
	{
		this.add.image(400, 300, 'sky')
		
		const platforms = this.createPlatforms()
		this.player = this.createPlayer()
		const stars = this.createStars()

		this.scoreLabel = this.createScoreLabel(16, 16, 0)

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

		this.physics.add.overlap(this.player, stars, this.collectStar, null, this)

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

	collectStar(player, star)
	{
		star.disableBody(true, true)

		this.scoreLabel.add(10)
	}

	// ...

	createScoreLabel(x, y, score)
	{
		const style = { fontSize: '32px', fill: '#000' }
		const label = new ScoreLabel(this, x, y, score, style)

		this.add.existing(label)

		return label
	}
}

Our score logic differs from the official Phaser guide.

Because we have a separate ScoreLabel class we need to create a new instance of it on line 48 in the createScoreLabel(x, y, score) method and then use this.add.existing() to add it to the Scene.

The official Phaser guide uses the this.add.text() factory instead. In both cases, a Text object is created.

One notable difference is that we pass a number to ScoreLabel instead of a formatted string. This is another application of DRY in that we don't need to do the same score text formatting in more than one place. It only happens in the updateScoreText() method of ScoreLabel.

We can see it in action on line 40 where we add 10 points to the score each time a star is collected.

Try it out at http://localhost:8000 and you should be able to get 120 points by collecting all the stars. 🤩

Coming Up

We've now transformed parts 8 and 9 from the official Making your first Phaser 3 game guide into modern JavaScript.

In the next and final part, we will add bombs to make things a little more interesting!

Let us know in the comments below if anything doesn’t work or is unclear. We’ll be happy to fix or clarify!

Be sure to sign up for our newsletter so you don't miss any future Phaser 3 game development tips and techniques!

Drop your email into the box below.

Learn to make an Infinite Jumper in Phaser 3 with modern JavaScript!

Drop your email into the box below to get this free 60+ page book and join our newsletter.

Learn more about the book here.

Phaser 3 JavaScript HTML5 Guide Tutorial

Want tips and techniques more suited for you?


You may also like...


Video Guides


Beginner Guides


Articles Recommended For You

Fix Stretched Image Distortions in Phaser 3 with 9-Slice Scaling

by on

Are you having image distortion problems when scaling to make a button or panel graphic bigger? Multiple versions of the …

5 minute read

Command Pattern to Undo Player Actions

by on

Are you looking for a clean and reusable way to implement undo for player actions? Perhaps you are making a turn-based …

15 minute read

Advanced Logging with the Strategy Pattern

by on

Have you ever tried debugging a problem with your game that only seems to happen in production? The Developer Tools …

7 minute read

State Pattern for Character Movement in Phaser 3

by on

Writing clean and well-organized code is something all game developers aspire to. We want code to be reusable and easy …

7 minute read

Didn't find what you were looking for?


comments powered by Disqus