Talking about Flappy Bird game, Game development, HTML5, Javascript and Phaser.
While Phaser 3 is becoming more and more stable, there’s still a lot to do and learn with Phaser 2, especially if you have games developed with Phaser 2 and want to keep them up to date or port to Phaser 3.
I made a Flappy Bird prototype about three years ago and it’s time to update it to the latest Phaser 2 version before showing you how to port to Phaser 3 line by line.
Also, I added some more room for customization and I not destroy any sprite during the game, I only recycle them saving memory and improving performance.
Have a look at the prototype:
Use the mouse to flap.
Now, let me show you the source code, still uncommented but rather easy to read, with a lot of customizable options:
var game;
var gameOptions = {
// bird gravity, will make bird fall if you don't flap
birdGravity: 800,
// horizontal bird speed
birdSpeed: 125,
// flap thrust
birdFlapPower: 300,
// minimum pipe height, in pixels. Affects hole position
minPipeHeight: 50,
// distance range from next pipe, in pixels
pipeDistance: [220, 280],
// hole range between pipes, in pixels
pipeHole: [100, 130],
// local storage object name
localStorageName: "bestFlappyScore"
}
window.onload = function() {
game = new Phaser.Game(320, 480, Phaser.CANVAS);
game.state.add("Play", play, true);
}
var play = function(){}
play.prototype = {
preload:function(){
game.load.image("bird", "bird.png");
game.load.image("pipe", "pipe.png");
},
create:function(){
game.stage.backgroundColor = "#87CEEB";
game.scale.scaleMode = Phaser.ScaleManager.SHOW_ALL;
game.scale.pageAlignHorizontally = true;
game.scale.pageAlignVertically = true;
game.stage.disableVisibilityChange = true;
game.physics.startSystem(Phaser.Physics.ARCADE);
this.pipeGroup = game.add.group();
this.score = 0;
this.topScore = localStorage.getItem(gameOptions.localStorageName) == null ? 0 : localStorage.getItem(gameOptions.localStorageName);
this.scoreText = game.add.text(10, 10, "-", {
font:"bold 16px Arial"
});
this.updateScore(0);
this.bird = game.add.sprite(80, 240, "bird");
this.bird.anchor.set(0.5);
game.physics.arcade.enable(this.bird);
this.bird.body.gravity.y = gameOptions.birdGravity;
game.input.onDown.add(this.flap, this);
var pipePosition = game.width
do{
this.addPipe(pipePosition);
pipePosition += game.rnd.between(gameOptions.pipeDistance[0], gameOptions.pipeDistance[1]);
} while(pipePosition < game.width * 4);
},
update:function(){
game.physics.arcade.collide(this.bird, this.pipeGroup, this.die, null, this);
if(this.bird.y > game.height || this.bird.y < 0){
this.die();
}
},
updateScore: function(inc){
this.score += inc;
this.scoreText.text = "Score: " + this.score + "\nBest: " + this.topScore;
},
flap: function(){
this.bird.body.velocity.y = -gameOptions.birdFlapPower;
},
die: function(){
localStorage.setItem(gameOptions.localStorageName, Math.max(this.score, this.topScore));
game.state.start("Play");
},
addPipe: function(posX){
var pipeHoleHeight = game.rnd.between(gameOptions.pipeHole[0], gameOptions.pipeHole[1]);
var pipeHolePosition = game.rnd.between(gameOptions.minPipeHeight + pipeHoleHeight / 2, game.height - gameOptions.minPipeHeight - pipeHoleHeight / 2);
var upperPipe = new Pipe(game, posX, pipeHolePosition - pipeHoleHeight / 2, -gameOptions.birdSpeed);
game.add.existing(upperPipe);
upperPipe.anchor.set(0.5, 1);
this.pipeGroup.add(upperPipe);
var lowerPipe = new Pipe(game, posX, pipeHolePosition + pipeHoleHeight / 2, -gameOptions.birdSpeed);
game.add.existing(lowerPipe);
lowerPipe.anchor.set(0.5, 0);
this.pipeGroup.add(lowerPipe);
}
}
Pipe = function (game, x, y, speed) {
Phaser.Sprite.call(this, game, x, y, "pipe");
game.physics.enable(this, Phaser.Physics.ARCADE);
this.body.velocity.x = speed;
this.giveScore = true;
};
Pipe.prototype = Object.create(Phaser.Sprite.prototype);
Pipe.prototype.constructor = Pipe;
Pipe.prototype.update = function() {
if(this.x + this.width < game.state.states[game.state.current].bird.x && this.giveScore){
game.state.states[game.state.current].updateScore(0.5);
this.giveScore = false;
}
if(this.x < -this.width){
this.giveScore = true;
game.state.states[game.state.current].pipeGroup.sort("x", Phaser.Group.SORT_DESCENDING);
if(game.state.states[game.state.current].pipeGroup.getChildAt(0).x == game.state.states[game.state.current].pipeGroup.getChildAt(1).x){
this.x = game.state.states[game.state.current].pipeGroup.getChildAt(0).x + game.rnd.between(gameOptions.pipeDistance[0], gameOptions.pipeDistance[1]);
}
else{
this.x = game.state.states[game.state.current].pipeGroup.getChildAt(0).x;
}
}
};
The game is managed by ARCADE physics, and during next days you will see it improved and ported to Phaser3, meanwhile download the source code.
Never miss an update! Subscribe, and I will bother you by email only when a new game or full source code comes out.