Phaser 2.0 Tutorial: Flappy Bird (Part 1)

My new video tutorial series HTML5 Mobile Game Development with Phaser over at ZenvaAcademy has just gone live.

While reading a blog might be great, watching someone actually create a game in front of you is a lot more engaging and you really get a feel for what goes into making a simple game.

Read the announcement post

While reading a blog might be great, watching someone actually create a game in front of you is a lot more engaging and you really get a feel for what goes into making a simple game.

Over the course of about three hours, we'll build this game together: zenva runner
Phaser 2.0 was released recently and a lot of people are wondering how to use the new API and physics systems. I thought it'd be easy and fun to create a real Flappy Bird clone as a tutorial series on how to use the new release of the library.

Here's the fully playable game we'll be building: Flappy Bird Reborn

In the interest of full disclosure, Part 1 (this part, to be exact) of the
tutorial series will not end with you having a game to play. It will end with you having a (nearly) perfect replica of the Flappy Bird menu screen.
menu-complete If you've never used Phaser before, this tutorial will introduce you to core concepts that need to be understood before setting out on your own.


Why Flappy Bird?

It's been done to hell and back. But, it's also a relatively simple physics based game and it's not difficult to create a perfect clone of it.

Getting Started

First, let's use the generator-phaser-official yeoman generator to create the basic scaffolding for the game.

1: Create a new directory and cd into it
$ mkdir flappy-bird-reborn && cd $_
2: Run the generator
$ yo phaser-official

Answer the prompts with the following values:

You're using the fantastic Phaser generator.  
[?] What is the name of your project? flappy bird reborn
[?] Which Phaser version would you like to use? 2.0.1
[?] Game Display Width: 288
[?] Game Display Height: 505
3: Let the generator run.

Once the generator has run, your directory should now look something like this:

├── Gruntfile.js
├── assets
│   ├── preloader.gif
│   └── yeoman-logo.png
├── bower.json
├── config.json
├── css
│   └── styles.css
├── game
│   ├── main.js
│   └── states
│       ├── boot.js
│       ├── gameover.js
│       ├── menu.js
│       ├── play.js
│       └── preload.js
├── package.json
└── templates
    ├── _index.html.tpl
    └── _main.js.tpl

(I removed the bower_components and node_modules directories from this display for ease of reading)

Go ahead and run the following command in your flappy-bird-reborn directory:

$ grunt

This will compile the assets, run a server, open a browser, and refresh the page whenever you've saved your code.

Assets

I've gone ahead and split our assets up for use in this game. Go ahead and download them here. Extract them to the assets folder and let's move on.

FULL DISCLOSURE: I stole these assets from the Lanica Tutorial on how to create Flappy Bird for Titanium .

The Menu

Every game needs a menu state and ours isn't any different. So, let's go ahead and load the assets that we'll need for the menu. Open up game/states/preload.js. You should see something like this:

  'use strict';
  function Preload() {
    this.asset = null;
    this.ready = false;
  }

  Preload.prototype = {
    preload: function() {
      this.asset = this.add.sprite(this.width/2,this.height/2, 'preloader');
      this.asset.anchor.setTo(0.5, 0.5);

      this.load.onLoadComplete.addOnce(this.onLoadComplete, this);
      this.load.setPreloadSprite(this.asset);
      this.load.image('yeoman', 'assets/yeoman-logo.png');

    },
    create: function() {
      this.asset.cropEnabled = false;
    },
    update: function() {
      if(!!this.ready) {
        this.game.state.start('menu');
      }
    },
    onLoadComplete: function() {
      this.ready = true;
    }
  };

module.exports = Preload  

If you look at the original Flappy Bird, the menu screen consists of the background, the ground, the title, the bird, a start button, and a share button. We're not going to be implementing a share feature, so we don't have to worry about that, but we will be implementing all of the other elements, so we need to load their image assets like so:

preload: function() {  
    this.load.onLoadComplete.addOnce(this.onLoadComplete, this);
    this.asset = this.add.sprite(this.width/2, this.height/2, 'preloader');
    this.asset.anchor.setTo(0.5, 0.5);
    this.load.setPreloadSprite(this.asset);

    this.load.image('background', 'assets/background.png');
    this.load.image('ground', 'assets/ground.png');
    this.load.image('title', 'assets/title.png');
    this.load.image('startButton', 'assets/start-button.png');

    this.load.spritesheet('bird', 'assets/bird.png', 34, 24, 3);
}

The first four lines of this function are boilerplate code. It displays an animated loading image while we load our other assets.

The next four lines load individual images. The syntax for loading an image is very simple:

this.load.image(key,url);  

Basically, this tell's Phaser's cache system to load the image from the given url and store it for easy retrieval with a unique name given as the argument key;

Let's take a quick look at the last line:

This line looks different from the others due to the fact that we are going to load an image for our bird that has animation frames. Here's our bird image:
flappy-bird.png You can see that the image has three frames and you can just take my word for it that each frame is 34 pixels wide and 24 pixels tall.

The syntax for loading a sprite sheet looks like this:

this.load.spritesheet(key, url, frameWidth, frameHeight, numberOfFrames);  

Knowing that our spritesheet has three frames, each 34 pixels wide and 24 pixels tall, we can now easily load the image as a sprite sheet with the following:

this.load.spritesheet('bird', 'assets/bird.png', 34, 24, 3);  

Go ahead and save your code. Your browser should refresh and, things shouldn't look perfect anymore. There should be a small green square on the screen. That's because we've removed the asset that was previously being displayed in that area. But that's ok, because we're about to do something amazing with one line of code.

Coding the Menu:

Pull open game/states/menu.js. You should be looking at something like the following:

  'use strict';
  function Menu() {}

  Menu.prototype = {
    preload: function() {

    },
    create: function() {
      var style = { font: '65px Arial', fill: '#ffffff', align: 'center'};
      this.sprite = this.game.add.sprite(this.game.world.centerX, 138, 'yeoman');
      this.sprite.anchor.setTo(0.5, 0.5);

      this.titleText = this.game.add.text(this.game.world.centerX, 300, '\'Allo, \'Allo!', style);
      this.titleText.anchor.setTo(0.5, 0.5);

      this.instructionsText = game.add.text(this.game.world.centerX, 400, 'Click anywhere to play "Click The Yeoman Logo"', { font: '16px Arial', fill: '#ffffff', align: 'center'});
      this.instructionsText.anchor.setTo(0.5, 0.5);

      this.sprite.angle = -20;
      this.game.add.tween(this.sprite).to({angle: 20}, 1000, Phaser.Easing.Linear.NONE, true, 0, 1000, true);
    },
    update: function() {
      if(this.game.input.activePointer.justPressed()) {
        this.game.state.start('play');
      }
    }
  };

  module.exports = Menu

Go ahead and remove everything inside of the create and update functions so that we can start from scratch:

  'use strict';
  function Menu() {}

  Menu.prototype = {
    preload: function() {
    },
    create: function() {
    },
    update: function() {
    }
  };
  module.exports = Menu;

Adding the Background

The first thing we're going to do is load our background image. We'll use the create method in menu.js for this:

create: function() {  
     // add the background sprite
    this.background = this.game.add.sprite(0, 0, 'background');
},

Go ahead and save your code and check your browser. You should now have the background displaying perfectly on your screen:
background-display

A quick note on the syntax: Loading a sprite and displaying it is ridiculously easy.

var sprite = this.game.add.sprite(x, y, key);  

The x and y arguments should be pretty self explanatory. The key argument is the key that we gave the image in the preloader.

Adding the Ground

In the original Flappy Bird, the ground moves, even on the menu. To do this, we're going to use what's called a TileSprite so that we can cause the ground to automatically scroll without having to instantiate a bunch of sprites with this texture and swap them out when they've left the screen.

Again, in the create method in menu.js, add the following below the line we wrote to add the background:

    // add the ground sprite as a tile
    // and start scrolling in the negative x direction
    this.ground = this.game.add.tileSprite(0, 400, 335, 112, 'ground');
    this.ground.autoScroll(-200, 0);

Save your code and you should see something that looks like this:
ground-sprite

A look at TileSprite syntax:

We have two lines to consider, let's do it one at a time. To add a tileSprite to the game, the following syntax needs to be considered:

var tileSprite = this.game.add.tileSprite(x, y, width, height, key);  

Again, these parameters should be fairly self explanatory with one exception: width and height are the width and height of the image.

The second line is what does the magic:

tileSprite.autoScroll(xSpeed, ySpeed);  

This line tells our tileSprite to automatically scroll at the given speed (in pixels per second). xSpeed is the speed to scroll on the x plane, ySpeed is, you got it, the speed to scroll on the y plane. A negativexSpeedwill scroll to the left, while a positive one will scroll right and a negativeySpeed` will scroll upwards, while a positive one will scroll downwards. Simple.

Adding the title and bird

Ok, this is a big one. Let's talk about what's going to happen first. In the original Flappy Bird game, the title and bird both oscillate slowly up and down while the bird flaps her wings. To mimic this effect we're going to go through the following steps:

  1. Create a group to hold both our title sprite and our bird
  2. Create the title sprite and add it to the title group
  3. Create the bird sprite and add it to the title group
  4. Add an animation to the bird using spritesheet animation and begin playing the animation.
  5. Set the originating location of the group
  6. Add an animation tween to the group that will move everything together in oscillation.
  7. Talk about it.

I'm going to do this in one big code chunk, and then we'll split it up as a discussion.

Underneath the code we added previously, still in the create method of menu.js add this code.

create: function() {  
     // add the background sprite
    this.background = this.game.add.sprite(0, 0, 'background');
},

The Breakdown

Alright, stick with me, we're going to do this one step at a time. I'm going to discuss the code we wrote both pertaining to our application as well as general usage.

Step 1: Create a group to hold both our title sprite and our bird

Our Code:

var sprite = this.game.add.sprite(x, y, key);  
This line of code simply creates a new game group that we can reference later.

What are Groups?

Groups are more generally used to store a bunch of sprites that can be pulled from, like bullets, or enemies. But, groups are powerful. Every sprite inside of a group can be easily iterated through, all have a method called on them at the same time, all of their properties set at the same time, or even, as we're doing, tweened together.

Step 2: Create the title sprite and add it to the title group

x We create the title sprite, using previously discussed syntax, and then on the next line, we add the title to the title group. Pretty easy.

Step 3: Create the bird sprite and add it to the title group

y This is exactly like the previous step, except we're creating the bird sprite.

Step 4: Add an animation to the bird using spritesheet animation and begin playing the animation.

key Alright, cool. Something new.

Simple SpriteSheet Animations

When you have a sprite whose texture asset is a sprite sheet, you can easily add animations to the sprite using the sprite's AnimationManager, which can be accessed with the animations object on the sprite. We're creating a simple animation here. If our spritesheet had more frames and different animations, (say for jumping, running left, running right, idling, or shooting) we could set up a lot of advanced animations, but that's a different tutorial. Our code is very simple, let's break it down generically line by line;
create This tells the sprite's AnimationManager to use all of the frames in the sprite sheet to create an animation named animationKey.

menu.js This tells the sprite's AnimationManager object to begin playing the animation identified by animationKey, at the given frameRate. If loop is true, it will play the animation indefinitely, or until sprite.animations.stop() is called.
So, looking again at our code:

    // add the ground sprite as a tile
    // and start scrolling in the negative x direction
    this.ground = this.game.add.tileSprite(0, 400, 335, 112, 'ground');
    this.ground.autoScroll(-200, 0);
1. We are telling the bird's AnimationManager to use all of the available frames in the sprite's spritesheet to create a new animation called 'flap'.
2. We are telling the sprite's AnimationManager to begin playing the 'flap' animation at 12 frames per second, and to loop it indefinitely.

Step 5: Set the location of the group

var tileSprite = this.game.add.tileSprite(x, y, width, height, key);  
We created our sprites at (0,0) and (200,5), respectively and then added them to the group. If we didn't set the group's location, the screen would end up looking like this:
no-group-location

However, when you change a group's location, it transforms the location of all sprites included in the group by the amount set. So, by changing the titleGroup's location to (30,100), we're effectively moving all of the sprites it contains 30 pixels to the right, and 100 pixels down, which results in the following:
group-location

Like I said, groups are -super- handy. We'll look more at advanced group functions in a later tutorial.

Step 6: Add an animation tween to the group that will move everything together in oscillation.

width This one line does so much, let's look at it a bit more generically so we can tell what exactly is going on.

height * object: The object to tween. This can be a sprite, a group, or any game object. * properties: An object representing which properties to tween. * duration: How long each iteration of the tween should take in milliseconds * ease: The easing function to use (Phaser.Easing Functions Available) * autoStart: Whether or not to automatically start the tween * delay: The amount of time, in milliseconds, to wait before performing the tween * repeat: How many times to repeat the tween * yoyo: Whether or not to run the tween backwards once it completes.

There's a lot of documentation surrounding tweens, and I'd suggest you take a look at it: Phaser.Tween

So, looking back at -our- code:

tileSprite.autoScroll(xSpeed, ySpeed);  
The properties list looks like the following:

  • object: this.titleGroup
  • properties: { y: 115 }
  • duration: 350
  • ease: Phaser.Easing.Linear.NONE
  • autoStart: true
  • delay: 0
  • repeat: 1000
  • yoyo: true

This would read in plain English as the following:

Tween the title group to a "y" position of 115 pixels from the top of the screen over the course of 350 milliseconds without easing. Start the tween as soon as it's defined and without a delay, then repeat it 1000 times and have it run forwards and backwards.

Ok.. Phew... We're done with that. If you were capable of doing all of that without saving and previewing, then I applaud you. You get a gold star.

Either way, save it and check your browser. You should be greated with something like the following:
flappy-bird-intro

The First Interaction: A Start Button

Alright, the menu is looking pretty great, but without a way to start the game, our menu is completely useless.

Add The Button

Once again, at the bottom of the create method in menu.js add the following lines:
tileSprite This code should look pretty similar with the exception of the last two arguments.

xSpeed Just like a call to game.add.sprite, we provide a button with both x & y coordinates, and a key that represents an image we loaded in the preloader. However, we must also specify a callback method to run when the button is interacted with, and a callbackContext that callback will operate inside of.

What are Callbacks and CallbackContexts?

Every function in Phaser that has a callback also has a callback context parameter. If you fail to pass in the context parameter, Phaser will assume a null context. Generally speaking, you will want to make your callback context this, as we want our callbacks to operate inside of a context that we can access all of our game objects from.

You'll also notice, that instead of passing in an actual number for x, we passed in a handly little piece of code that you're gonna become very familiar with:
ySpeed is, you got it, the speed to scroll on the y plane. A negative This will give us the center point on the x and y planes, respectively.

There's also the following:
will scroll to the left, while a positive one will scroll right and a negative Which we use to set the button's anchor position to half of it's width, and half of it's height, or more succinctly, to it's center. Because we put the button's initial position to (game.width/2, 300), the button should appear directly centered on the x plane, and 300 pixels minus half the height of the button on the y plane.

What is an Anchor?

A sprite's anchor is the position inside of the sprite that Phaser uses as it's point of origin. This means that when you position a sprite, the anchor will be moved to that position, and the sprite's texture will be transformed around the anchor. When a sprite is first created, the anchor is set to (0,0), meaning that the sprite will be positioned by it's top-left corner. Setting the anchor to (0.5, 0.5) will mean that the sprite is positioned by the center of it's display area.

Picture this:
The button with the default anchor of (0,0)
default-anchor

The button with the anchor of (0.5,0.5)
default-anchor

The anchor is also the position on the sprite that the sprite will rotate around. That means, with the default anchor, the sprite will rotate around it's top left corner:
rotate-default-anchor

But if we set the anchor to (0.5, 0.5) we'll the much more desirable:
rotate-with-anchor

Below the create method, add a new method caled startClick with the following code:
create This method only does one thing. It calls game.state.start(). This method takes only one parameter: a string that represents the name of the state to start. In this case, we're going to start the 'play' state, which will hold the code for the actual game.

What is a State?

A state is generally a different screen, or display, for a game. In our case, when we're done with our game, we will have 5 states: The Boot State, The Loading Screen State, The Menu Screen State, The Play Screen State, and The Game Over Screen State. Even though we've only talked about the menu screen (and a little bit about the loading screen), we've already got all five states created for us.

How?
The generator did it for us. Because of this, we skipped over creating states, but if you open up main.js, you should see the following:
menu.js The generator automatically created this file for us, and it will create it again each time a new game state is added. The most important thing here is to understand what this code does:
animations Much like the loader, the game's StateManager allows you to add a game state with a key to retrieve it by, and a Phaser.State that represents the state itself. Each state has inherited methods that Phaser automatically recognizes and knows to call at the proper time in the game lifecycle and in fact, you've already seen a few of them (preload,create,update).

At the bottom of menu.js, there's a line of code that reads:
animationKey Because we're using Browserify to define modules, and to require() dependencies, module.exports tells Browserify what should be returned with this file is required. In this case, we're telling our module to export the Menu object that contains all of the code we've written for this state.

Because the generator created these lines in main.js for us:
animationKey the game's StateManager will know to run the code we've written in menu.js when the 'menu' state is played.

Putting it all together

Welp, here we are. At then end of the first part of the journey. If you save your code and check your browser, you should be greeted with this view:
menu-complete Clicking on the start button should take you immediately to the play state, which, right now, should be a bouncing green square. But, we'll change that soon enough...

Your code should now look like the following gist:
Phaser 2.0 Tutorial: Flappy Bird (Part 1) gist

Want More?

My new video tutorial series HTML5 Mobile Game Development with Phaser over at ZenvaAcademy has just gone live.

While reading a blog might be great, watching someone actually create a game in front of you is a lot more engaging and you really get a feel for what goes into making a simple game.

Read the announcement post


Next Time:

We'll take a look at physics and prefabs: Phaser 2.0 Tutorial: Flappy Bird (Part 2)


Questions, comments or complaints? You can hit me up on twitter: @codevinsky, I'm always in the #phaser.io chat, or simply leave a comment below.