Getting started with PhaserJS

Building a Pairs game in 120 lines of code by Wayne Ellis (Twitter @codemwnci )

Phaser JS is an HTML5 game framework for building desktop browser, or mobile games using HTML 5 and Javascript.

I have been playing around with Phaser for a little while now. I reached the point where I could make a few games, especially through the few tutorials and examples that are available online, but I wanted to understand it deeper. I have written a book, and many blogs in the past, so I know from experience that one of the truely best ways of gaining deep knowledge, is to step back and try to explain it to someone else. If you can honestly and accurately describe it someone else, so that it makes sense (without having to bluff!), then there is a pretty good chance you understand the material.

This tutorial is my attempt to gain that deeper knowledge by building, experimenting, researching, refactoring and finally documenting a relatively simple game of pairs. If I were to go futher and actually write a book on Phaser, this would be the first or second chapter, so treat this as the beginners entry point into the world of PhaserJS. Phaser is a powerful library and has many capabilities not covered in this simple game, but depending on feedback, I will look to write more tutorials or a full book to cover many more pieces of functionality.

pairs.png

The Aim

By the end of this relatively simple tutorial, we should have created a Pairs game, in just under 120 lines of code. We will have learned how the game loop and core game functions work, how to add sprites and react to events, and how to setup a simple server to test the game locally.

The Setup

To get the game up and running, we will need two main files, and a folder to store all our images. Images, sprites, sounds etc are typically called assets in game development, so create a directory structure that looks like the following:

PairsGame\
+ index.html
+ game.js
+ assets\

We won't put anything in the assets directory for now, we will do that later. Next, we need a way of running our application. We can't simply open the index.html file in a web browser, because browsers typically prevent javascript running from the file system (for security reasons). So, we need to serve this up via a web-server. There are many ways to do this, and if you have experience of Web programming, you may have your own preferred approach, but to keep things simple, I tend to fall back on Python or Node.

If you already have Python installed, then you can simply run

python -m SimpleHTTPServer 9000

or if you prefer Node, then using the Node Package Manager, you can do

npm install -g http-server
http-server -p9000

obviously, for the Node example, you only need to install the http-server package once.

If you don't have either, then as we are learning JavaScript here, I would suggest the Node example, and you can download Node, which includes NPM from nodejs.org.

Both examples run on port 9000, so point your browser at localhost:9000 and you should be ready to go! Okay, you actually see precisely nothing, because we haven't put anything in our index.html, but this is where we get to the fun stuff! Starting the build

We will start with the index.html, as once that is in place, we can leave that alone. Open it up in your favourite text editor, and input the following code.

<!doctype html>
<html>
<head>
  <title>Pairs</title>
</head>
<body>
  <div id="pairs-game" class="game"></div>
  <script src="//cdnjs.cloudflare.com/ajax/libs/phaser/2.4.2/phaser.min.js"></script>
  <script src="game.js"></script>
</body>
</html>

This code simply creates a DIV tag, which is where Phaser will set up the game, then loads PhaserJS, and then finally loads our game file game.js. The rest is pretty much boilerplate HTML code.

Now that we have the HTML in place to load our game, we need to start work on the JavaScript code that will create the game and the event logic. This is where it gets slightly more complicated, so first we should take a look at the heart of Phaser, which is the event loop.

The Event Loop

The event loop is responsible for controlling the flow of the game. Phaser has a myriad different methods that can be implemented for the purpose of managing the game world and its rendering. However, there are a number of core methods that we will focus on in this tutorial. These are

  • preload: As the name suggests, this function preloads game assets prior to the game starting. This is typically were assets are loaded into the game world, ready for creation.
  • create: the create function is used to create the game world, such as the sprites, tilemaps, and sprite groups. The create method is called when the game stage is loaded (and for our game we have only one stage), following the completion of the preload function.
  • update: the update function is called throughout the game at regular intevals. The engine aims to do this at 60 times per second, but there is no guarantees here. This method is often used to capture game events, such as key presses, button clicks, mouse movement etc, and to then update the game state variables as a result of those inputs.
  • render: following the update function, the game engine renders the game world and sprites. Most objects in Phaser render automatically, but the render method is called once these objects have rendered to carry out final post-processing.

The Game Code

We'll begin by creating the skeleton of our game code.

(function() {
  'use strict';
  function Game() {}
  // variables will go here later
  Game.prototype = {
    preload: function() {
    },
    create: function () {
    },
    update: function () {
    },
    render: function() {

    }
  };
  var game = new Phaser.Game(800, 520, Phaser.AUTO, 'pairs-game');
  game.state.add('game', Game);
  game.state.start('game');
}());

This skeleton is doing a few things for us. Firstly we invoke strict mode for best practice JavaScript usage. There is plenty of documentation on the web of why we should use strict mode, so I will not repeat it here. The next few lines creates a Game object, and adds the 4 functions described in the game loop to the object. This effectively creates a game State object.

The final few lines of code we should inspect a little deeper.

var game = new Phaser.Game(800, 520, Phaser.AUTO, 'pairs-game');

This line creates a phaser game object, which in turn sets up the framework and game world engine. The first parameter (800) specifies the width of the game canvas, the second parameter (520) specifies the height of the game canvas, the third parameter specifies the rendering mode (CANVAS, or WEBGL), which we have set to AUTO to allow Phaser to choose the best method for us. The final parameter specifies the id of the DIV tag we created in the HTML file right at the start. This can be left blank, and Phaser would simply append the game element onto the end of the BODY tag. We, however, did create a DIV tag, which gives us greater control of the placement of the game in the HTML page, therefore we need to specify this value.

game.state.add('game', Game);
game.state.start('game');

The first line of the final pieces of code adds the State object that we created to the list of states available in the game world. It is out of scope of this tutorial for creating more than one state, but a game can have many states (think Menu, GameLoop, GameOver, etc). More states are added in this way and allows for neat separation of game logic.

The second line of code tells the game engine to start the game state. As we only have one, we simply tell it to start the 'game' state, which is the very same one we have just added to the game world. This will immediately start the game loop, calling preload create update render on the Game object that we have created. Right now though, we should simply see a black screen 800 x 520 pixels. So next step is to start to build the game logic.

Preload

As already mentioned, the preload adds the assets for the game into the game engine ready for use. Our game is going to contain 20 'cards', so we need 11 images (the unflipped card, plus 10 pairs). You can create your own images, or download the images from the GitHub page. Save them to the assets directory as back.png, 0.png, 1.png….etc. Next we need to load these assets. Update the preload function, so that it looks like the following:

preload: function() {
  this.load.image('back', 'assets/back.png');
  this.load.image('0', 'assets/0.png');
  this.load.image('1', 'assets/1.png');
  this.load.image('2', 'assets/2.png');
  this.load.image('3', 'assets/3.png');
  this.load.image('4', 'assets/4.png');
  this.load.image('5', 'assets/5.png');
  this.load.image('6', 'assets/6.png');
  this.load.image('7', 'assets/7.png');
  this.load.image('8', 'assets/8.png');
  this.load.image('9', 'assets/9.png');
},

As we have created a State object, this give us access to all the State object functions and properties. The load property is the Loader object, which in turn gives us access to the image function that loads an image into the game world, accessible from the unique name provided.

This code could be shortened by a few lines by using a for-loop, but for the purpose of clarity and simplicity in this tutorial I have kept it in its more verbose form. Feel free to use a for-loop if you prefer.

Create

Before we can see some real evidence our code has worked, we need to create the intial world state. To do this, we need to a number of things:

  1. We need to create 20 sprites, making up the 10 pairs
  2. We need to create 20 more sprites showing the unflipped cards
  3. We need to shuffle the pack of cards to randomise the placement of the pairs
  4. We need to hide the 10 pairs and only show the unflipped cards

These 4 steps are achieved by replacing the create function with the following code:

create: function () {
  for (var i = 0; i < 10; i++) {   
    images.push(this.game.add.sprite(0,0,''+i));
    images.push(this.game.add.sprite(0,0,''+i));
  }

  this.shuffle(images);
  for (var i = 0; i < 4; i++) {
    for (var j = 0; j < 5; j++) {
      var idx = i*5+j;
      cards[idx] = this.game.add.sprite(j*TILE_SIZE,i*TILE_SIZE,'back');
      cards[idx].index = idx;
      images[idx].x = j*TILE_SIZE;
      images[idx].y = i*TILE_SIZE;
      images[idx].visible = false;
      cards[idx].inputEnabled = true;
      cards[idx].events.onInputDown.add(this.doClick);
      cards[idx].events.onInputOver.add(function(sprite) { sprite.alpha = 0.5; });
      cards[idx].events.onInputOut.add(function(sprite) { sprite.alpha = 1.0; });
    }
  }
},
shuffle: function(o) {
  for(var j, x, i = o.length; i; j = Math.floor(Math.random() * i), x = o[--i], o[i] = o[j], o[j] = x);
  return o;
},

We will ignore the shuffle function, it is outside the scope of this tutorial on how this works, but suffice to say that Google + StackOverflow answered the query of how to shuffle an array in JavaScript and this complex but neat little function does the job. Normally I would have used a library such as underscore.js, but I did not want to add in additional dependencies for this tutorial.

Stepping through the code the first for loop of the create function fulfils step 1 of our requirements, by creating 10 pairs of images. These are all defaulted to the top-left corner of the game area for now, and the name of the sprite loaded is a number 0 to 10 (which links to one of the images 0 to 10 we loaded in the preload function). The add sprite code has 3 parameters (xCo-ordinates, yCo-ordinates, imageKey). The x and y co-ordinates start 0,0 at the top left corner of the game area, going to 800, 520 (as this is the size we configured) at the bottom-right of the game area.

The next statement shuffles the images in the array, so that the pairs are not all next to each other. This fulfils step 3 of our requirements.

The next for loop is a double loop. The outer loop i counts 0 → 3, representing each row, and the inner loop j counts 0 → 4 representing each item in the row. The code inside therefore gets executed 20 times (5 items each in 4 rows). The first line calculates the index which translates a 2 dimensional list into a 1 dimensional array. idx = i*5+j, will result in a set of indexes that look like the following

[ 0][ 1][ 2][ 3][ 4]
[ 5][ 6][ 7][ 8][ 9]
[10][11][12][13][14]
[15][16][17][18][19]

Once we have the index, we create a new sprite that will display the unflipped card. We do this in a similar way to the first sprite creation, except this time, rather than placing a (0,0) co-0rdinates, we will place it according to the position it needs to be on the screen. As the for loop variable i represents rows, we simply multiply the tile size by this row index (first row is 0, second row is 130, etc) and set this to the yCoordinates. We do the same for the xCoordinates but using the j variable. We also create a property on the sprite called index, and save the current index value. This will be used later when we a flipping between the flipped and unflipped cards.

Next we update the x and y coordinates of the now shuffled image tiles so that they are in the correct (not 0,0) location.

At this point, we now have two tiles in each of the 20 locations on the game area; the image tile and the unflipped tile. Finally, we need to hide the image tiles by setting the visible property to false. We will flip-flop between the unflipped tile and the image tile being visible as a result of mouse clicks.

Finally in the double for-loop we add the mouse events. The first line inputEnabled tells Phaser that we want to track mouse events. The onInputDown function tracks mouse clicks, and calls the doClick function. The onInputOver function tracks the mouse hovering over the sprite. It executes the inline function, which changes the transparency (alpha) to 50% to show which tile is currently in focus. The onInputOut function is called when the mouse moves away from a sprite. In our code is executes the inline function to set the transparency back to nil, i.e. alpha 100%.

If we try to run this code, it will fail because we have not created some of the variables that we are working with, such as images, cards, TILE_SIZE etc. S0, before we run our code, we need to set up those variables. To do this, find the 'variables will go here later' comment, and add in to following variable initialisations.

var TILE_SIZE = 130;
var cards = [];
var images = [];

We also need to create the doClick function, so add a placeholder before the update function to match the following

doClick: function (sprite) {
},

At this point, we should see a screen of unflipped cards in a 5 x 4 configuration, and a little space on the side to show the current score. This all looks very nice, but doesn't do anything fun apart from the mouse-over effect! So, next up we need to start to add in the game logic.

doClick

The doClick function will be called when the player clicks on an unflipped tile (as we only set the onInputDown event on the card sprites, and not the image sprites).

The steps we take will be

  1. If it is the first of the two tiles we try to pair, then save the index of the clicked tile in a variable 'firstClick'
  2. If it is the second of the two tiles, then save the index of the clicked tile in a variable 'secondClick'
  3. Regardless of first or second click, then store the time we clicked (this will be used in the Update function to wait for 500 milliseconds before unflipping failed matches), hide the unflipped card and show the flipped card (using the visible property of the card and image sprites).
  4. Also if second click, check the key of the image sprite (numbered 0 → 10) for both clicked indexes to see if we have a match.
  5. If we do have a match, then add some points to the score, and clear the first/secondClick variables, so that we can start clicking more tiles.
  6. If we do not have a match, then remove some points from the score, and set the noMatch boolean variable to true (which will be checked by the Update function.

The following code achieves the 6 steps. This is done largely in the order of the 6 steps, with exception of step 3, but this is described in the comments below.

doClick: function (sprite) {  
  if (firstClick == null) {
    firstClick = sprite.index;  // step 1
  }
  else if (secondClick == null) {
    secondClick = sprite.index; // step 2
    // step 4
    if (images[firstClick].key === images[secondClick].key) {      
      // step 5
      score += 50;
      firstClick = null; secondClick = null;
    }
    else {
      // step 6
      score -= 5;
      noMatch = true;
    }
  }
  else {
    return; // don't allow a third click, instead wait for the update loop to flip back after 0.5 seconds
  }
  // step 3 --we only get here on first or second click due to
  // the else statement returning 
  clickTime = sprite.game.time.totalElapsedSeconds();
  sprite.visible = false;
  images[sprite.index].visible = true;
},

Before we can run the game, we need to add a few more variables that we have used in the doClick function. Underneath where we created the images/cards array variables, we need to add the following variable initialisations.

var firstClick, secondClick;
var noMatch, clickTime;
var score = 100;

If we now run the game, we should be able to click a few cards, as well as the hover effect we already had working. However, if we get a noMatch, this stops us from being able to click any more! We could have allowed the player to start clicking as soon as we found a noMatch, but we should give a little bit of time to see the failed match, before the cards unflip, and the game continues. To manage the time delay, we use the Update function, which we will go straight onto next.

Update

As we have already mentioned, the update method is called upto 60 times per second to run the game loop. This is an ideal place for checking any time-based activities in your game logic. In our case, we need to do a couple of things:

  1. We need to check that there has been a noMatch found
  2. We need to check the time between the noMatch and the current time is sufficient that the player can register the failed match images.
  3. Once the time delay is up, we hide the image cards, show the unflipped cards, and clear out the first/secondClick variables, so that the doClick function can start accepting mouse clicks again. We also reset the noMatch variable, so that the update function is not processing unneceesarily.

This should all be pretty simple to achieve by replacing the update function with the following code:

update: function () {
  if (noMatch) {
    if (this.game.time.totalElapsedSeconds() - clickTime > 0.5) {       
       cards[firstClick].visible = true;
       cards[secondClick].visible = true;
       images[firstClick].visible = false;
       images[secondClick].visible = false;
       firstClick = null; secondClick = null;
       noMatch = false;
    }
  }
},

And we are now almost done. If we play the game now, we can see the unmatched cards flip back, and we can continue searching for the pairs until we have found all 10. The only thing that is missing is to show the current score.

There are lots of ways to achieve displaying text on the game canvas, but we will make use of the render function.

Render

Going back to the game loop mechanics. The render function is called after the Update function, and after all the sprites have auto-rendered by the game enging. We are already keeping track of the score by adding to our initial 50 points for every match, and removing 5 points for every failed match, so all we need to do is display it in the space we have left on the right hand side.

We need to replace the render function that is currently blank, with the following lines of code:

render: function() {
  this.game.debug.text('Score: ' + score, 660, 20);
},

This simply writes the text Score: 100, or whatever the current score is, at the co-ordinates x:660, y:20. As the render function is called upto 60 times per second as well, this will ensure that the score is always up to date.

Conclusion

Hopefully this demonstrates how easy it is to get up and running with the Phaser framework. This tutorial only scratches the surface of what Phaser is capable of doing. The purpose of this tutorial was to show

  1. Game States
  2. Game Loop
  3. Sprite Rendering
  4. Mouse Events

Phaser has so much more, including animations, tweenings, tilemaps, generating game maps from JSON/CSV, physics, sounds, keyboard/gamepad integrations, multiple game states, scaling for different sized devices, etc. I hope this tutorial has sparked your interest. Feel free to leave comments/feedback, and it may help motivate me to write more tutorials or even a book on this cool framework.

The source code, and the images for the game can all be found on GitHub .