Building A HTML5 Game With Phaser

Phaser is a new TypeScript/JavaScript HTML5 game framework created by Richard Davey. Richard is at the forefront of HTML5 game development and Phaser has captured my attention since he first mentioned working on it a few months back. Not only does it take advantage of TypeScript, which I already really enjoy, but has been designed to be incredibly easy to pick up and quickly prototype games with. It’s actually heavily based on Flixel, a favorite game framework among Flash game devs, so with Impact’s typing and Phaser’s Flixel based API it should be very easy for Flash devs to pick it up. Let’s take a look at a simple demo I wrote that will cover the basics of using Phaser.

Before getting started, here is a copy of the full demo source code which includes all of the assets you will need to run it. I recently wrote a post covering my Phaser workflow with Visual Studio, NodeJS and Grunt which you can read about here and I plan on coming back to this post and cleaning it up more as Phaser matures and I figure out the best workflow for Phaser.

To get the full effect of Phaser you are going to want to take advantage of TypeScript, although it’s not critical since you can just write your games in straight JavaScript. For this post I am going to focus on TypeScript. If you are on a PC you can simply install the TypeScript plugin into a copy of Visual Studio Express. If you don’t want to use Visual Studio or are on a Mac you can use Node to compile for you. I recently wrote an article on how I use Node for my TypeScript development which you can read about here and I plan on writing a more comprehensive post on setting up Phaser shortly. For now I just want to walk you through the basics of how the framework works. For the time being, I’m just going to assume you are using Visual Studio. Here is the sample project I created:

While there are many ways to make a game with Phaser, the following post will just walk you through the most straight forward way. We will not be extending any classes and will just focus on using Phaser’s core classes to make a player, bullets and some zombies. To start we are going to want create a new game instance. As you can see I am simply wrapping this in an anonymous function just like I would a normal JS file:

/// <reference path="../phaser/Phaser/Game.ts"></reference>
(function () {
    // Create game instance and connect init, create and update methods
    var myGame = new Phaser.Game(this, 'game', 800, 480, init, create, update);
})();

At the top you will see I have a reference for the Phaser Game.ts file which will allow us to create game instance and get compiler checking with TypeScript. Think of this as an import statement in something like AS3 or JavaScript. Let’s take a quick step back to discuss my project’s setup. I have a development folder on SkyDrive called TypeScript. In it I simply checked out Phaser and create a new VS project for my game so I just need to tell the compiler to look up two directories from the current file and go into the checked out phaser directory. The path may be different depending on how you setup your own project.

After that I can create a new Phaser Game instance. Phaser classes are inside of a Phaser module so you will need to preface any class with that. The Game constructor requires a few things such as the local scope, the width and height of the game, and some callbacks which we can use to prototype up a simple game. These callbacks are init, create, update and render (which I am not using here). Let’s add them to our project file right below where we define our game:

function init(){
}
function create(){
}
function update(){
}

To get our game started we will need to preload some assets. Phaser comes with its own basic pre-loader. Let’s add the following to our init function:

myGame.loader.addTextureAtlas('entities', 'assets/textures/entities.png', 'assets/textures/entities.txt');
myGame.loader.load();

You can preload a wide range of file types. Here we are going to be loading up a texture atlas. This is a packaged up set of sprites along with an atlas file that has coordinates for each of our sprites so the engine knows how to cut them out. You can learn more about using Texture Atlases here.

Once you initiate load, Phaser will start pre-loading everything and when the loading is done it will call create on the main game instance. Let’s add a new variable to store our player:

var player: Phaser.Sprite;

Now we can setup our player in the create method we setup before. Add the following:

// create player and configure
player = myGame.createSprite(myGame.stage.width * .5 - 50, 200, "entities");
player.drag.x = 900;
player.maxVelocity.x = 250;
player.animations.add('idle', ['player-idle-1.png'], 10, false, false);
player.animations.add('fire', ['player-fire-1-00.png', 'player-fire-1-01.png', 'player-fire-1-02.png'], 10, true, false);
player.animations.add('walk', ['player-walk-1-00.png', 'player-walk-1-01.png', 'player-walk-1-02.png', 'player-walk-1-03.png', 'player-walk-1-04.png', 'player-walk-1-05.png', 'player-walk-1-06.png', 'player-walk-1-07.png'], 10, true, false);
player.animations.play('idle');

I’ll walk you through the process. First we tell our reference of the game, myGame, to create a new Sprite. This takes an x and y position along with a reference to a graphic. If you remember when we set up our loader we gave our texture the” id ‘entities’. When we create our sprite, we simply pass it a reference to that asset’s” id and it will automatically look up the image data from the loader for us. Once we have the sprite we need to configure a few things on it such as drag (the deceleration value), maxVelocity (how fast the sprite can move) and our animations.

Animations are handled by the AnimationManager which is accessible in all sprites as a property called animations. From here you can set up different types of animations and label them for easy reference later when you are switching between them. The animation.add method accepts a name, the frames for the animation (this could be numbers or names if using a Texture Atlas), a time (based on framerate), whether it loops and if it uses numerical indexes. As you can see I set up 3 animations,” idle, fire and walk then tell the sprite to play its” idle frame. At this point if you compile you should see your player standing in the middle of the screen

Now let’s look at how we can move the player around the screen. You can access keyboard controls via Input manager. Since it can be a lot to type out each time you want to check a key I set it to a variable for easy reference. Under our player variable, add the following:

var keyboard: Phaser.Keyboard;

Since we are going to directly type it to the Phaser.Keyboard class we will need to reference its TypeScript file in our project. Add this to the top of your file:

/// <reference path="../../phaser/Phaser/system/input/Keyboard.ts"></reference>

Now we can set this up in our create method below our player
setup:

// save reference to keyboard
keyboard = myGame.input.keyboard;

In our update function we can now reference the keyboard directly to start moving the player. Add the following:

// Player controls
if (keyboard.isDown(Phaser.Keyboard.RIGHT)) {
    player.acceleration.x += 100;
    player.flipped = false;
}else if (keyboard.justReleased(Phaser.Keyboard.RIGHT)) {
    player.acceleration.x = 0;
}
if (keyboard.isDown(Phaser.Keyboard.LEFT)) {
    player.acceleration.x -= 30;
    player.flipped = true;
}else if (keyboard.justReleased(Phaser.Keyboard.LEFT)) {
    player.acceleration.x = 0;
}

Now if you run your project you should be able to move your player left and right. Let’s review what is going on here. I am making use of two methods on the keyboard manager called isDown and justReleased. These allow me to query what is going on with the keyboard. When a key is down I begin to add acceleration to the player over each frame. This will insure that the player speeds up until his max velocity is reached. I also make sure to flip the player’s sprite graphic so he appears to be facing the correct direction. Finally if the key is released I simply reset the acceleration back to 0. This will allow the player’s drag to take over and slowdown the velocity. Playing with the max velocity and drag is what gives the player the fluid movement effect you see in most platformers. You can always make the player come to a full stop without sliding by simply setting the velocity and acceleration to 0 when you release a key. In this example I just threw a few values in,” ideally I would tighten this up so it doesn’t feel so “loose” when moving around.

Now we have a player that is able to run around the screen left and right but you may have noticed he is not animating. Let’s add the following below our keyboard controls:

// Player animations
if (Math.abs(player.velocity.x) > 10) {
    player.animations.play('walk');
}else{
    player.animations.play('idle');
}

Here we can simply switch between the walk and” idle animations by testing the player sprite’s velocity. If it’s not 0 than we know he is moving so we should show the walk animation.

While it is fun to move the player around the screen you may want him to do something more like shoot. Let’s set up a few variables to track this:

//weapons
var weaponId: number = 1;
var fire: bool = false;
var bulletGroup: Phaser.Group;
var shotDelayTime = 0;
var shotDelay = 200;

Now in our create function we are going to need to set up the group our bullets will belong to:

bulletGroup = myGame.createGroup(10);

Groups are a great way to organize sprites and eventually we can use these groups to test for collisions. Another thing that groups help us with is Object Pooling. As you can see, I am setting up the group to 10. This means that we will only have at max, 10 bullets on the screen. As you will see when we create the bullet sprites the Group will manage recycling instances for us. Let’s take a look at the createBullet function you will need you add just below your create function:

function creatBullet() {
    // Get new instance from the bullet group via recycle
    var bullet: Phaser.Sprite = <phaser .Sprite>bulletGroup.recycle(Phaser.Sprite);
    // reset exists flag if it went off stage of collided with a zombie
    bullet.exists = true;
    // offset so it looks like the bullet comes out of the gun and isn't spawned inside of the player
    bullet.x = player.x + (player.flipped ? 0 : player.width); 
    bullet.y = player.y + 25;
    bullet.flipped = player.flipped;
    bullet.velocity.x = bullet.flipped ? -600 : 600;
    bullet.loadGraphic("entities");
    bullet.animations.frameName = "bullet-gun.png";
}
</phaser>

I have documented this a bit but wanted to go over the basics. First off we are going to create a new reference to a Sprite we will call bullet so I am asking the bulletGroup to recycle a Sprite. Internally the group will see if it has one created, if not it will simply make a new one. If there are more sprites than its limit, which is 10, it will return the first item in the pool. This means if you set the number to something like 3 or very low and kept firing you bullet sprite wouldn’t make it to the end of the screen since it will most likely be recycled since the pool is so small.

Next I am going to set the bullet’s exists property to true. This is a great technique for managing instances that are no longer needed to be updated or rendered but you want to keep around for pooling. You’ll see how we set this to false later on when test if bullets fly off screen. The rest of the code here simply manages setting up the x,y position, whether the bullet is flipped, what direction to move in, manually setting up a graphic and assigning a single animation frame graphic.

Now we need a way to shoot our bullet. Let’s add a new keyboard control to our game right under our other player controls in the update function:

if (keyboard.isDown(Phaser.Keyboard.SPACEBAR)) {
    fire = true; 
}else if (keyboard.justReleased(Phaser.Keyboard.SPACEBAR)) {
    fire = false;
}

Here you see we are setting a flag if the fire key is down based on the state of the spacebar. Below this we can put in our fire logic:

// Check to see if the player should be firing
if (fire) {
    // increase the shotDelayTime based on the game's time delta
    shotDelayTime += myGame.time.delta;
    // If the delay is greater than 200 create a bullet
    if (shotDelayTime > shotDelay) {
        creatBullet();
        // reset the shotDelayTime
        shotDelayTime = 0;
    }
    // stop the player so they can shoot
    player.velocity.x = player.acceleration.x = 0;
}

This may look complicated but it’s really straight forward. We keep track of a delay between shots. This way if you didn’t delay calls to createBullet it would spawn too many and they would all “bleed” together visually. Instead we setup a short delay between shots to space them out better. Here you will see I keep track of the shotDelayTime by adding the game’s time delta over each update call. The delta represents how much time has passed since the last frame was rendered. This is usually returned in milliseconds. From here we can see if the shotDelayTime is greater than the shotDelay and create a new bullet. Once the createBullet function is called we need to make sure we reset the shotDelayTime. Finally we make sure the player is standing still when shooting by setting his velocity and acceleration to 0.

In order to see the player animate when shooting we will need to modify our player animation code in the update function to look like this:

// Player animations
if (Math.abs(player.velocity.x) != 0) {
    player.animations.play('walk');
}
else if(fire){
    player.animations.play("fire");
}else{
    player.animations.play('idle');
}

At this point you should be able to walk around and shoot.

The final thing we want to add is a way to check if our bullets have gone off screen. We can take advantage of a helper method on the group called forEach to assign it a callback to run each time our game is updated. Put this at the end of our game’s update function.

// bullet logic
 bulletGroup.forEach(updateBullets);
In addition we will need the function for this to call. Make sure you add the following function:
function updateBullets(target: Phaser.Sprite) {
    if (target.x > myGame.stage.width) {
        target.exists = false;
    }
}

This will be called on each bullet in the group and we can see if the bullet has moved off screen. If it does, we simply set its exists property to false. If you test your game you should be able to run back and forth and shoot. Now we just need to add something to shoot at. We’ll need to setup a few more variables to track the zombie logic for our game.

//zombies
var zombieGroup: Phaser.Group;
var zombieSpawnDelay: number = 1000; // milliseconds before next zombie is spawned
var zombieSpawnDelayTime: number = 0;

Here you can see we are going to need a new group for our zombies and a set of variables to track the time between spawning each one. Let’s setup the new group right below where we create our bullet group in the init function:

zombieGroup = myGame.createGroup(10);

Now we can add in our createZombie function:

zombieGroup = myGame.createGroup(10);
Now we can add in our createZombie function:
function createZombie() {
    var zombie: Phaser.Sprite = <phaser .Sprite>zombieGroup.recycle(Phaser.Sprite);
    zombie.exists = true;
    zombie.x = Math.random() < 0.5 ? myGame.stage.width + 30 : - 30;
    zombie.y = player.y;
    zombie.flipped = zombie.x > 0 ? true : false;
    zombie.loadGraphic("entities");
    zombie.velocity.x = zombie.flipped ? -100 : 100;
    var style = Math.random() < 0.5 ? 0 : 1;
    zombie.animations.frameName = "zombie-a-" + style + "-00.png";
    zombie.animations.add('walk', ['zombie-a-' + style + '-00.png', 'zombie-a-' + style + '-01.png', 'zombie-a-' + style + '-02.png', 'zombie-a-' + style + '-03.png', 'zombie-a-' + style + '-04.png', 'zombie-a-' + style + '-05.png'], 10, true, false);
    zombie.animations.play("walk");
}

This should look very similar to our create bullet function. In this one however we are going to set the spawn x position to just off screen. You can always get the width and height of your game via the stage property in the main game instance, in our case that would be myGame.stage. We are using a random number to figure out if it should be on the left or right side of the screen. Next we test what side of the screen the zombie is on and set its flipped value accordingly. After that we set a random value again to 0 or 1 in the style variable. This is because the zombie sprite has two color version so we can have a little bit of randomization with each zombie created. Finally we setup animation just like before and set the zombie to walk.

Now we want to start spawning these zombies by adding the following just below where we are managing creating bullets in the update function:

// Spawn zombies
zombieSpawnDelayTime += myGame.time.delta;
if (zombieSpawnDelayTime > zombieSpawnDelay) {
    createZombie();
    zombieSpawnDelayTime = 0;
}

So just like our bullet spawner, we are simply going to keep track of the time between each spawn delay. Once the delay time is greater than the spawn delay we create a new zombie and reset the delay time. If you run this, you should see zombies running around on the screen and you can move your player and shoot but everything is passing through each other.

Let’s take a look at some basic collision detection.

We’ll need to add the following to the end of our update function:

// test collision of bullets and zombies
myGame.collide(bulletGroup, zombieGroup, bulletCollides);

You can easily test for collisions between two groups by using the collide method from our game instance. Here you’ll see that we pass in both groups and a reference to a callback. Let’s add the following function to our game now to handle the callback:

function bulletCollides(targetA: Phaser.Sprite, targetB: Phaser.Sprite) {
        targetA.exists = false;
        targetB.exists = false;
}

This function will accept targetA which are our bullets and targetB which are our zombies. All I am doing here is setting their exits property to false if they collide but you could do more like increase a score for each zombie killed or try to apply damage to each zombie sprite so it takes more than a single shot to kill them.

At this point you will have a fully running demo showing over a simple introduction to making games with Phaser. While this isn’t a full game it should give you a basic” idea of how the core mechanics of the engine works. In your own games you may want to take advantage of TypeScript’s ability to create classes and extend those to build more complex code structures which is something I’ll cover in future posts. One of the best advantages of using Phaser however that is you could just as easily make games without doing any classes or inheritance as you can see from the above example. Stay tuned for future posts where I’ll cover setting up Phaser with node and grunt as well as more advanced techniques for making games including how to publish them to the Windows Store.

Subscribe To My Mailing List

Want to learn how to make a game? Not sure where to start? Even if you are a seasoned game maker there is still a lot you can learn from my free 15 page guide on how to build, release and market a game.

Simply sign up for my mailing list and also get access to great tips and advice on how to make better games. I promise to not spam your inbox!

Join Now

More in Game Dev (23 of 41 articles)
super-jetroid-starter-kit-feature