How to Create a Game with Phaser 3 (original) (raw)

Making amazing cross-platform games is now easier than it’s ever been thanks to Phaser, an Open Source JavaScript game development library developed by Richard Davey and his team at Photonstorm. Games developed with Phaser can be played on any (modern) web browser, and can also be turned into native phone apps by using tools such as Cordova.

Table of contents

Learn by making your first game

The goal of this tutorial is to teach you the basics of this fantastic framework (version 3.x) by developing the “Frogger” type of game you see below:

Dragon crossing game with player moving towards jewels

You can download the game and code here. All the assets included were produced by our team and you can use them in your own creations.

Learning goals

Tutorial requirements

Learn Phaser 3 with our newest Mini-Degree

The Phaser Mini-Degree is now available on Zenva Academy. Learn to code and make impressive games with JavaScript and Phaser 3!

Get Instant Access

Development environment

The minimum development environment you need consists in a code editor, a web browser and a local web server. The first two are trivial, but the latter requires a bit more explanation. Why is it that we need a local web server?

When you load a normal website, it is common that the content of the page is loaded before the images, right? Well, imagine if that happened in a game. It would indeed look terrible if the game loads but the player image is not ready.

Phaser 3 game showing black boxes where unloaded sprites should be

Phaser needs to first preload all the images / assets before the game begins. This means, the game will need to access files after the page has been loaded. This brings us to the need of a web server.

Browsers, by default, don’t let websites just access files from your local drive. If they did, the web would be a very dangerous place! If you double click on the index.html file of a Phaser game, you’ll see that your browser prevents the game from loading the assets.

That’s why we need a web server to server the files. A web server is a program that handles HTTP requests and responses. Luckily for us, there are multiple free and easy to setup local web server alternatives!

Setting up your local web server

The simplest solution I’ve found is a Chrome application named (surprisingly) Web Server for Chrome. Once you install this application, you can launch if from Chrome directly, and load your project folder.

Google Chrome Apps tab with Web Server selected

Web Server for Chrome window with Web Server URL circled

You’ll be able to navigate to this folder by typing the web server URL into your browser.

Hello World Phaser 3

Now that our web server is up and running, lets make sure we’ve got Phaser running on our end. You can find the Phaser library here. There are different manners of obtaining and including Phaser in your projects, but to keep things simple we’ll be using the CDN alternative. I’d recommend you use the non-minified file for development – that will make your life easier when debugging your game.

More advanced developers might want to divert from these instructions and use a more sophisticated development environment setup and workflow. Covering those is outside of the scope of this tutorial, but you can find a great starting point here, which uses Webpack and Babel.

In our project folder, create a index.html file with the following contents:

Learn Game Development at Zenva.com

Now create a folder named js, and inside of it, our game file game.js:

// create a new scene named "Game" let gameScene = new Phaser.Scene('Game');

// our game's configuration let config = { type: Phaser.AUTO, //Phaser will decide how to render our game (WebGL or Canvas) width: 640, // game width height: 360, // game height scene: gameScene // our newly created scene };

// create the game, and pass it the configuration let game = new Phaser.Game(config);

What we are doing here:

If you run this on the browser and open the console you should see a message indicating that Phaser is up and running:

Google Chrome Developer Tools with body tag highlight in Elements

Scene life-cycle

In order for us to add the first images to our game, we’ll need to develop a basic understanding of the Scene life-cycle:

Phaser 3 game lifecycle illustration

There are more methods in the scene life-cycle (render, shutdown, destroy), but we won’t be using them in this tutorial.

Bring in the sprites!

Let’s dive right into it and show our first sprite, the game background, on the screen. The assets for this tutorial can be downloaded here. Place the images in a folder named “assets”. The following code goes after let gameScene = new Phaser.Scene(‘Game’); :

// load asset files for our game gameScene.preload = function() {

// load images this.load.image('background', 'assets/background.png'); };

// executed once, after assets were loaded gameScene.create = function() {

// background this.add.sprite(0, 0, 'background'); }

Let’s see the result:

Phaser 3 game with newly placed background

Not quite what we wanted right? After all, the full background image looks like so:

Phaser 3 game with scale applied for full-screen background

Before solving this issue let’s first go over how coordinates are set in Phaser.

Coordinates

The origin (0,0) in Phaser is the top left corner of the screen. The x axis is positive to the right, and y axis is positive downwards:

X-Y Coordinate system with 0,0 and 10,6 as points

Sprites by default have their origin point in the center, box on x and y. This is an important difference with Phaser 2, where sprites had what was called an anchor point on the top-left corner.

This means, when we positioned our background on (0,0), we actually told Phaser: place the center of the sprite at (0,0). Hence, the result we obtained.

To place the top-left corner of our sprite on the top-left corner of the screen we can change the origin of the sprite, to be it’s top-left corner:

// executed once, after assets were loaded gameScene.create = function() {

// background let bg = this.add.sprite(0, 0, 'background');

// change origin to the top-left of the sprite bg.setOrigin(0,0); };

The background will now render in the position we want it to be:

Background rendered based on X-Y coordinates

The Player

Time to create a simple player we can control by either clicking or touching on the game. Since we’ll be adding more sprites, let’s add these to preload so we don’t have to modify it again later:

// load asset files for our game gameScene.preload = function() {

// load images this.load.image('background', 'assets/background.png'); this.load.image('player', 'assets/player.png'); this.load.image('dragon', 'assets/dragon.png'); this.load.image('treasure', 'assets/treasure.png'); };

We’ll then add the player sprite and reduce it’s size by 50%, inside of create:

// player this.player = this.add.sprite(40, this.sys.game.config.height / 2, 'player');

// scale down this.player.setScale(0.5);

Our Valkyrie is ready for some action! We need to develop next the ability for us to move her with the mouse or touchscreen.

Phaser 3 game scene with Player sprite added

Detecting input

Phaser 3 provides many ways to work with user input and events. In this particular game we won’t be using events but will just check that the “active input” (be default, the mouse left button or the touch) is on.

If the player is pressing/touching anywhere on the game, our Valkyrie will walk forward.

To check for input in this manner we’ll need to add an update method to our scene object, which will normally be called 60 times per second (it is based on the requestAnimationFrame method, in less performing devices it will be called less often so don’t assume 60 in your game logic):

// executed on every frame (60 times per second) gameScene.update = function() {

// check for active input if (this.input.activePointer.isDown) {

// player walks

} };

You can verify that this works by placing a console.log entry in there.

Moving the player

When the input is active we’ll increase the X position of the player:

// check for active input if (this.input.activePointer.isDown) {

// player walks
this.player.x += this.playerSpeed;

}

this.playerSpeed is a parameter we haven’t declared yet. The place to do it will be the init method, which is called before the preload method. Add the following before the preload definition (the actual declaration order doesn’t matter, but it will make our code more clear). We are adding other parameters as well which we’ll use later:

// some parameters for our scene (our own customer variables - these are NOT part of the Phaser API) gameScene.init = function() { this.playerSpeed = 1.5; this.enemyMaxY = 280; this.enemyMinY = 80; }

Now we can control our player and move it all the way to the end of the visible area!

Treasure hunt

What good is a game without a clear goal (take that Minecraft!). Let’s add a treasure chest at the end of the level. When the player position overlaps with that of the treasure, we’ll restart the scene.

Since we already preloaded all assets, jump straight to the sprite creation part. Notice how we position the chest in X: 80 pixels to the left of the edge of the screen:

// goal this.treasure = this.add.sprite(this.sys.game.config.width - 80, this.sys.game.config.height / 2, 'treasure'); this.treasure.setScale(0.6);

In this tutorial we are not using a physics system such as Arcade (which comes with Phaser). Instead, we are checking collision by using a utility method that comes in Phaser, which allows us to determine whether two rectangles are overlapping.

We’ll place this check in update, as it’s something we want to be testing for at all times:

// treasure collision if (Phaser.Geom.Intersects.RectangleToRectangle(this.player.getBounds(), this.treasure.getBounds())) { this.gameOver(); }

Let’s declare our gameOver method (this is our own method, you can call it however you want – it’s not part of the API!). What we do in this method is restart the scene, so you can play again:

// end the game gameScene.gameOver = function() {

// restart the scene
this.scene.restart();

}

A group of dragons

Life is not easy and if our Valkyrie wants her gold, she’ll have to fight for it. What better enemies than evil yellow dragons!

What we’ll do next is create a group of moving dragons. Our enemies will have a back and forth movement – the sort of thing you’d expect to see on a Frogger clone :)

In Phaser, a group is an object that allows you to create and work with multiple sprites at the same time. Let’s start by creating our enemies in, yes, create:

// group of enemies this.enemies = this.add.group({ key: 'dragon', repeat: 5, setXY: { x: 110, y: 100, stepX: 80, stepY: 20 } });

Phaser 3 game with six, giant dragon sprites on screen

The dragons are too big. Let’s scale them down:

// scale enemies Phaser.Actions.ScaleXY(this.enemies.getChildren(), -0.5, -0.5);

This is looking better:

Phaser 3 game with dragon sprites shrunk to fit

Bouncing enemies

The up and down movement of the dragons will follow the logic described below. When making games and implementing mechanics, it is in my opinion always good to outline them and understand them well before attempting implementation:

Since we have basically an array of enemies, we’ll iterate through this array, in update, and apply this movement logic to each enemy (note: speed hasn’t been declared yet, so assume each enemy has a value setup for this property):

// enemy movement let enemies = this.enemies.getChildren(); let numEnemies = enemies.length;

for (let i = 0; i < numEnemies; i++) {

// move enemies
enemies[i].y += enemies[i].speed;

// reverse movement if reached the edges
if (enemies[i].y >= this.enemyMaxY && enemies[i].speed > 0) {
  enemies[i].speed *= -1;
} else if (enemies[i].y <= this.enemyMinY && enemies[i].speed < 0) {
  enemies[i].speed *= -1;
}

}

This code will make the dragons move up and down, provided speed was set. Let’s take care of that now. In create, after scaling our dragons, let’s give each a random velocity between 1 and 2:

// set speeds Phaser.Actions.Call(this.enemies.getChildren(), function(enemy) { enemy.speed = Math.random() * 2 + 1; }, this);

Now our up and down movement is complete!

Colliding with enemies

We’ll implement this using the same approach we took for the treasure chest. The collision check will be performed for each enemy. It makes sense to utilize the same for loop we’ve already created:

// enemy movement and collision let enemies = this.enemies.getChildren(); let numEnemies = enemies.length;

for (let i = 0; i < numEnemies; i++) {

// move enemies
enemies[i].y += enemies[i].speed;

// reverse movement if reached the edges
if (enemies[i].y >= this.enemyMaxY && enemies[i].speed > 0) {
  enemies[i].speed *= -1;
} else if (enemies[i].y <= this.enemyMinY && enemies[i].speed < 0) {
  enemies[i].speed *= -1;
}

// enemy collision
if (Phaser.Geom.Intersects.RectangleToRectangle(this.player.getBounds(), enemies[i].getBounds())) {
  this.gameOver();
  break;
}

}

Camera shake effect

A really cool feature of Phaser 3 is that of camera effects. Our game is playable but it will be nicer if we can add some sort of camera shake effect. Let’s replace gameOver by:

gameScene.gameOver = function() {

// shake the camera this.cameras.main.shake(500);

// restart game this.time.delayedCall(500, function() { this.scene.restart(); }, [], this); }

There is a problem with this implementation, can you guess what it i?

After colliding with an enemy, the gameOver method will be called many times during the 500 ms. We need some sort of switch so that when you run into a dragon, the gameplay freezes.

Add the following at the end of create:

// player is alive this.isPlayerAlive = true;

The code below goes at the very start of update, so that we only process it if the player is alive:

// only if the player is alive if (!this.isPlayerAlive) { return; }

Our gameOver method:

gameScene.gameOver = function() {

// flag to set player is dead this.isPlayerAlive = false;

// shake the camera this.cameras.main.shake(500);

// restart game this.time.delayedCall(500, function() { this.scene.restart(); }, [], this); };

Now the method won’t be activated many times in a row.

Fading out

Before saying goodbye we’ll add a fadeout effect, which will commence half-way through the camera shakeup:

gameScene.gameOver = function() {

// flag to set player is dead this.isPlayerAlive = false;

// shake the camera this.cameras.main.shake(500);

// fade camera this.time.delayedCall(250, function() { this.cameras.main.fade(250); }, [], this);

// restart game this.time.delayedCall(500, function() { this.scene.restart(); }, [], this);

};

// reset camera effects this.cameras.main.resetFX();

That’s all for this tutorial! Hope you’ve found this resource helpful.

Full dragon crossing game with player and enemy movement