Flappy Bird series deserves to be updated to celebrate the incredible story behind the original game so here I am with the version running with Phaser 3.16.2 but most of all I wanted to update the game because next week I’ll add an evil twist to the game.
Well, about the game, no need to explain how it works:
Tap to flap.
I didn’t just update the code, I changed it a bit to match the same concepts used in the making of the Radical HTML5 prototype.
The reason is simple: both games are mainly the same, just using a different scrolling and a different way to move the player.
Have a look at the source code:
var game; var gameOptions = { // bird gravity, will make bird fall if you dont 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() { let gameConfig = { type: Phaser.AUTO, backgroundColor:0x87ceeb, scale: { mode: Phaser.Scale.FIT, autoCenter: Phaser.Scale.CENTER_BOTH, parent: 'thegame', width: 320, height: 480 }, pixelArt: true, physics: { default: 'arcade', arcade: { gravity: { y: 0 } } }, scene: playGame } game = new Phaser.Game(gameConfig); window.focus(); } class playGame extends Phaser.Scene{ constructor(){ super('PlayGame'); } preload(){ this.load.image('bird', 'bird.png'); this.load.image('pipe', 'pipe.png'); } create(){ this.pipeGroup = this.physics.add.group(); this.pipePool = []; for(let i = 0; i < 4; i++){ this.pipePool.push(this.pipeGroup.create(0, 0, 'pipe')); this.pipePool.push(this.pipeGroup.create(0, 0, 'pipe')); this.placePipes(false); } this.pipeGroup.setVelocityX(-gameOptions.birdSpeed); this.bird = this.physics.add.sprite(80, game.config.height / 2, 'bird'); this.bird.body.gravity.y = gameOptions.birdGravity; this.input.on('pointerdown', this.flap, this); this.score = 0; this.topScore = localStorage.getItem(gameOptions.localStorageName) == null ? 0 : localStorage.getItem(gameOptions.localStorageName); this.scoreText = this.add.text(10, 10, ''); this.updateScore(this.score); } updateScore(inc){ this.score += inc; this.scoreText.text = 'Score: ' + this.score + '\nBest: ' + this.topScore; } placePipes(addScore){ let rightmost = this.getRightmostPipe(); let pipeHoleHeight = Phaser.Math.Between(gameOptions.pipeHole[0], gameOptions.pipeHole[1]); let pipeHolePosition = Phaser.Math.Between(gameOptions.minPipeHeight + pipeHoleHeight / 2, game.config.height - gameOptions.minPipeHeight - pipeHoleHeight / 2); this.pipePool[0].x = rightmost + this.pipePool[0].getBounds().width + Phaser.Math.Between(gameOptions.pipeDistance[0], gameOptions.pipeDistance[1]); this.pipePool[0].y = pipeHolePosition - pipeHoleHeight / 2; this.pipePool[0].setOrigin(0, 1); this.pipePool[1].x = this.pipePool[0].x; this.pipePool[1].y = pipeHolePosition + pipeHoleHeight / 2; this.pipePool[1].setOrigin(0, 0); this.pipePool = []; if(addScore){ this.updateScore(1); } } flap(){ this.bird.body.velocity.y = -gameOptions.birdFlapPower; } getRightmostPipe(){ let rightmostPipe = 0; this.pipeGroup.getChildren().forEach(function(pipe){ rightmostPipe = Math.max(rightmostPipe, pipe.x); }); return rightmostPipe; } update(){ this.physics.world.collide(this.bird, this.pipeGroup, function(){ this.die(); }, null, this); if(this.bird.y > game.config.height || this.bird.y < 0){ this.die(); } this.pipeGroup.getChildren().forEach(function(pipe){ if(pipe.getBounds().right < 0){ this.pipePool.push(pipe); if(this.pipePool.length == 2){ this.placePipes(true); } } }, this) } die(){ localStorage.setItem(gameOptions.localStorageName, Math.max(this.score, this.topScore)); this.scene.start('PlayGame'); } }
Now, the twist I talked about at the beginning of the post, the one I want to add next week: the bird is controlled by AI and you move the pipes.
Want to know more? Stay tuned, and meanwhile download the source code.