Phaser 2.0 Tutorial: Flappy Bird (Part 3)

Intro to Player Movement

In Parts 1 & 2, we talked about how to setup a workflow with generator-phaser-official, sprite creation, tweens, physics, and collisions. In today's installment, we're going to talk about how to actually move the player when appropriate input is detected. It's not going to be a very long tutorial, but it will be important none-the-less.

So, without further ado, let's get our bird flapping.

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

A Note About the Generator

Between writing the previous part and this on, I updated the generator. It now uses Browserify to correctly do dependency injection and inheritance. I also updated the previous tutorials and gists to reflect this. I would suggest doing an update on the generator, re-running it and grabbing the code from the previous gists to get back to a point where you can follow along again.

To update the generator:

$ npm update -g generator-phaser-official

Putting the Flappy into Flappy Bird

Go ahead and open up bird.js and let's create a flap() method under the Bird.prototype.update() method:

Bird.prototype.flap = function() {  
};

This will serve as our base flap() method. Now, let's actually make the bird "flap" by moving upwards.

Bird.prototype.flap = function() {  
    this.body.velocity.y = -400;
};

All we've done here is told our bird's physics body to have a y velocity of -400. The end result will be that our bird will immediately move upward at 400 pixels per second. This might immediately be alarming, as we don't want the bird to just move upward and and never come back down. Luckily, we've already implemented gravity in our game (back in Part 2), so our bird will -immediately- be affected by gravity.

Now, the important part:

Setting up user interactions

We've got a method that will make our bird flap, but how do we know when to call that method?
Simple. We just have to call this.bird.flap() whenever the spacebar is pressed.

There are two ways to do this:
1. We can setup a keypress listener in our play state.
2. We can setup a keypress listener in our bird prefab.

This choice is up to you and is dependent on the type of game you're writing. For this game, we're going to add the listener to the play state.

Pop open play.js and add the following lines to the bottom of the create() method:

create: function() {  
    /* previous setup code */

    // keep the spacebar from propogating up to the browser
    this.game.input.keyboard.addKeyCapture([Phaser.Keyboard.SPACEBAR]);

    // add keyboard controls
    var flapKey = this.input.keyboard.addKey(Phaser.Keyboard.SPACEBAR);
    flapKey.onDown.add(this.bird.flap, this.bird);


    // add mouse/touch controls
    this.input.onDown.add(this.bird.flap, this.bird);


}

We're doing three important things in this code:

this.game.input.keyboard.addKeyCapture([Phaser.Keyboard.SPACEBAR]);  

On a desktop browser, pressing the spacebar scrolls down to the next page. As we don't want this to happen, we tell phaser that the keyboard event that corresponds to the spacebar being pushed should not propogate up to the browser. This is just a convoluted way of saying that the page won't scroll down when the spacebar is hit. This should also be done for arrow keys, tab, and any other key you are going to bind an action to that could adversely affect the browser.

What is Phaser.Keyboard.SPACEBAR?

Phaser.Keyboard.SPACEBAR is, what a classical programming language would call, an enumerable. That means, that SPACEBAR is a property of Phaser.Keyboard that corresponds with a constant value. In this case, Phaser.Keyboard.SPACEBAR is equal to the number 32 which is the event.keyCode for spacebar.

The next lines to look at are the following:

var flapKey = this.input.keyboard.addKey(Phaser.Keyboard.SPACEBAR);  
    flapKey.onDown.add(this.bird.flap, this.bird);

Here, we're telling the input object of our state to link a key on the keyboard to a local variable flapKey. The addKey method creates a Phaser.Key object that has a number of properties and methods and signals, but the one we're concerned with here is the 'onDown' signal. Note: The 'onDown' signal fires once and only once per keypress and does not reset until the key has been released.

What is a Signal?

A Signal (Phaser Documentation: Phaser.Signal) is used for object communication in Phaser. Think of it as a classic Observer or Publish/Subscribe pattern implementation. It works much like a standard event listener, but it uses a custom broadcast channel.

A Closer Look

flapKey.onDown.add(this.bird.flap, this.bird);  

This is the line that really does all of the magic for our keyboard. Here, we are telling the flapKey object that when it is pressed, to call the flap() method on this.bird with a context of this.bird. This is the first time we've set a callback context to something other than this.

Remember our flap() method in the bird prefab?

Bird.prototype.flap = function() {  
  this.body.velocity.y = -400;
};

If passed in a callback context of this to our flapKey.onDown callback handler, the this context in Bird.prototype.flap would be referencing the Play state, instead of this.bird and would try to modify Play.body.velocity.y (which neither exists, nor makes sense).

Touch and Mouse controls

// add mouse/touch controls
this.input.onDown.add(this.bird.flap, this.bird);  

This call is almost identical to our keyboard call above, with one notable change: this.input references either the mouse or touch controls, which ever one is available. If you were to load up the game on your mobile device, tapping the screen would send the onDown signal and call this.bird.flap.

Side Bar: To open your game on another computer or mobile device, make sure you're connected to the same network, open up Gruntfile.js and change the line 38 to read: hostname: '0.0.0.0', kill the grunt process in your terminal with control+c, re-run the grunt task with grunt, and then load up your computer's local address in your mobile browser with a port of 5000. My url looks like: http://192.168.1.77:5000

Checking our Work:

The game in your browser should look like the following:
flapping-1

Close.. but not quite.

Tweaking Physics

Our bird currently flaps a little too far and gravity is a little too weak. We can fix both of these in one step. Gravity in flappy bird is almost realistic (it's been calculated at about 9.7m/s^2, as oppose to real world gravity of 9.86m/s^2). However, I've not done the calculations to figure out exactly what that translates to for our game, so, feel free to play around with the gravity setting in the create() method in play.js. I've found that a gravity setting of about 1200 makes things move correctly for me:

// give our world an initial gravity of 1200
this.game.physics.arcade.gravity.y = 1200;  

flapping-2

Perfect. Or reasonably close to.

Rotating the bird

Great. So now our bird flaps up and down, but that's not exactly right, is it? In the real game, the bird pitches up wards when she flaps, and begins to turn downward as she falls to a maximum of 90 degrees to the ground.

Let's add that behavior.

Open up bird.js, jump down to the flap() method, and drop in this line:

flap: function() {  
    //cause our bird to "jump" upward
    this.body.velocity.y = -400;

    // rotate the bird to -40 degrees
    this.game.add.tween(this).to({angle: -40}, 100).start();
},

This is a simple tween, as we've discussed before, that sets the angle property of our sprite to -40 degrees over the course of 100 milliseconds.

But what about coming back down?

Scroll up to the update() function in our Bird prefab, and add the following lines:

Bird.prototype.update = function() {  
  // check to see if our angle is less than 90
  // if it is rotate the bird towards the ground by 2.5 degrees
  if(this.angle < 90) {
    this.angle += 2.5;
  }
};

This is a fairly simple statement. Everytime we run the update loop, we check to see if our angle is currently less than 90, if it is, increase the angle of the sprite by 2.5 degrees.

The result should look like this:

rotation-1

You might notice that once the bird hits the ground, it doesn't stop rotating until it hits 90 degrees. We'll address this in an upcoming lesson.

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

Source Code:

You can view the source for the files we modified in this lesson here: Phaser 2.0 Tutorial: Flappy Bird (Part 3) gist

Next Time:

In Part 4, we'll take a look at generating obstacles, sprite recycling, and death.

Back Talk

As always, if you've got questions or comments, drop them in the comment section below, hit me up on twitter (@codevinsky), or you can always find me as jdowell in the #phaserio freenode channel