Flappy - MissionBit/fall-2014-intro-mission-tt GitHub Wiki
Setup
Start a new project in your GitHub folder using the Phaser template from https://github.com/missionbit/phaser-template (click "Download ZIP").
Download the images we'll be using from here: http://bit.ly/mb-flappy-sprites (click "Download Gist"). Rename the "gist*" folder to "images" and put it in your new project's folder.
Everything from this point on we'll be doing in the game.js
file.
To make sure all of the files are in the right place, change the preload
method to use 'images/bird_1.png'
instead of logo.png
. The line of code should look like this:
game.load.image('logo', 'images/bird_1.png');
Instead of the Mission Bit logo, you should now see a bird rotating around in the center of the screen.
Let's clean that up a bit and change everything that says 'logo'
to 'bird'
. You should see it twice, once in preload
and once in create
.
Animation
A spritesheet is just a big image file where you have each frame of animation in a grid.
We can make the bird flap by using a spritesheet instead of a single image. Change your game.load.image('bird', 'images/bird_1.png')
code to use the spritesheet like this:
game.load.spritesheet('bird', 'images/bird_sheet.png', 68, 48);
We haven't told it to do any animation yet, so it should look the same at this point. To add the animation, we just define an animation called 'flap'
to tell it which frames to play, at what speed, and in what order and then tell it to play the 'flap'
animation (this code should go after this.sprite.anchor.setTo(0.5, 0.5)
):
this.sprite.animations.add('flap', [0,1,2,1], 10, true);
this.sprite.animations.play('flap');
Physics
Next we want to add physics to our sprite and add gravity in our create
method after we've set up the sprite:
game.physics.enable(this.sprite);
game.physics.arcade.gravity.y = 100;
To prevent it from falling off the screen for now, you can add this code:
// Stop the bird from falling off the screen, for now
this.sprite.body.collideWorldBounds = true;
Controls
Games aren't very fun if they're not interactive, so let's add keyboard controls. We need to tell it to capture the spacebar key, and then we want to change the bird's velocity when it's pressed.
We set up the capture of the spacebar in the create
method like this:
// keep space from scrolling the page
game.input.keyboard.addKeyCapture([Phaser.Keyboard.SPACEBAR]);
Then, in the update
method, we check for the key press and change the bird's velocity:
if (game.input.keyboard.justPressed(Phaser.Keyboard.SPACEBAR)) {
this.sprite.body.velocity.y = -100;
}
You might want to go ahead and remove the this.sprite.angle += 1;
code, we don't need the bird to spin automatically!
Now if you click on the page to give it focus and hit the spacebar, the bird should fly up a bit. You might notice that it doesn't feel like Flappy Bird just yet. You'll have to tweak the numbers to get it to feel right. Try changing the gravity and that velocity until you get something that feels about right.
Floor
To add a floor, we'll use something called a TileSprite. A TileSprite takes an image and repeats it over and over, and we can pan and zoom around too.
First we need to load the floor image in our preload
method:
game.load.image('floor', 'images/floor.png');
Next we need to create the floor in our create
method. We'll zoom out to 50% so that it looks to be about the right size for our game:
this.floor = game.add.tileSprite(0, game.world.height - 40, game.world.width, game.world.height, 'floor');
this.floor.tileScale.set(0.5);
To give the illusion of movement we can pan the floor in our update
method.
this.floor.tilePosition.x -= 200;
Maybe that's a bit too fast! Try coming up with a speed that makes more sense :)
Obstacles
You may have noticed that the bird falls right through the floor, that's not good. What's up with that? Well, we need to add physics to the floor, and ask Phaser to check for collisions between the bird and other objects. We know that we're going to want to add pipes later, so add a group for obstacles rather than checking for collision directly with the floor.
We'll need to set it up in the create
method:
this.obstacles = game.add.group();
this.obstacles.add(this.floor);
game.physics.enable(this.floor);
this.floor.body.immovable = true;
this.floor.body.allowGravity = false;
Then, in the update
method, we need to check for collisions
if (game.physics.arcade.collide(this.sprite, this.obstacles)) {
console.log('Game over man, game over!');
game.paused = true;
}
Pipes
First we need to load the images for the pipes in our preload
method. The images are named images/pipe_top.png
and images/pipe_bottom.png
. A good name for the assets would be pipe_top
and pipe_bottom
respectively.
We'll start with just one pair of pipes and work up from there. Because we'll be making more than one set of pipes we want to make a function for creating pipes so we don't have to repeat ourselves so much. You can put this function after the var game;
line.
function makePipePair(group, offsetX) {
var top = group.create(0, 0, 'pipe_top');
var bottom = group.create(0, 0, 'pipe_bottom');
top.anchor.set(0, 1);
var spacing = 100;
function addPhysics(pipe) {
game.physics.enable(pipe);
pipe.body.immovable = true;
pipe.body.allowGravity = false;
pipe.body.velocity.x = -200;
}
function positionPipes(top, bottom) {
var center = 250;
var left = game.world.width;
top.x = left;
bottom.x = left;
top.y = center - spacing;
bottom.y = center + spacing;
}
addPhysics(top);
addPhysics(bottom);
positionPipes(top, bottom);
top.x += offsetX;
bottom.x += offsetX;
}
Then, at the end of the create
method you'll need to call makePipePair
to create the first set of pipes:
makePipePair(this.obstacles, 0);
More Pipes
We can add a lot of pipes by calling that function more than once. Try adding more lines of makePipePair
with a larger number each time.
makePipePair(this.obstacles, 0);
makePipePair(this.obstacles, 400);
makePipePair(this.obstacles, 800);
You can make that even shorter by using a for
loop. It will look like this:
var pipeSpacing = 400;
var numPipes = 10;
for (var i = 0; i < numPipes; i += 1) {
makePipePair(this.obstacles, i * pipeSpacing);
}
To make it more interesting, we can change the center of each pipe to be a random number. Change the var center = 250
line to something like this (you may want to play with those numbers):
var center = game.rnd.integerInRange(50, game.world.height - 50);
Infinite Pipes
To make sure our game doesn't end, we need to make the pipes keep coming. The easiest way to do that is just to move them when they move off the left of the screen.
First we need to change the makePipePair
function to take a third argument, we'll call that newOffsetX
because that's how much we'll move it from the right side of the screen. The first line of that function should now look like this:
function makePipePair(group, offsetX, newOffsetX) {
We also need to change how we call it from the create
method. It will look something like this:
makePipePair(this.obstacles, i * pipeSpacing, pipeSpacing * numPipes - game.world.width);
Finally we need to update the position when it moves off the screen, this should go at the end of the makePipePair
function.
top.checkWorldBounds = true;
top.events.onOutOfBounds.add(function () {
if (top.x < 0) {
positionPipes(top, bottom);
top.x += newOffsetX;
bottom.x += newOffsetX;
}
});
Polish
We have a game that works and might even be fun, but there's plenty more we can do to make it better.
Consider making the game your own:
- Use your own graphics and/or animations (PIXLR is a good free drawing tool)
- Add a start screen (this would be an additional state in the game)
- Restart the game rather than pausing it on death
- Keep score
- Add music and/or sound effects
- Rotate the bird based on its velocity
- Add clouds in the background that scroll like the ground
- Move the pipes behind the background (or, move the ground in front of the pipes)
Some places to look for ideas on how to implement this:
Try searching for an answer first, and if you're having a hard time then ask a classmate or one of us. You should get used to collaborating because the next thing we do will be a group project!