Making a Web-based Flappy Bird Clone with p5.js | Part 3

Jimmy Lam
JavaScript in Plain English
5 min readJun 28, 2021

--

Recap

This is part 3 of the flappy bird clone series. You can find part 1 here and part 2 here. In the previous article, I went over how to add the ground and pipes object to the screen and make them move like in Flappy Bird.

The bird object

Now, we will be working on the famous flapping bird itself. Again, the bird in this game stays static in the horizontal direction throughout the gameplay and only moves up and down the screen. Therefore, the positional updates for the bird will be only in the up and down direction. The bird in the game is also flapping its wings and therefore we need to find a mechanism to show the different images of the bird to make it look like the bird is flapping its wings.

First is the importing of the bird images. In the provided starter code, I have included 3 bird images where the only difference between them is the wing position. To use these in our code, we will preload the images into a single array.

let BIRD_IMG;function preload() {
BIRD_IMG = [
loadImage(“assets/bird1.png”),
loadImage(“assets/bird2.png”),
loadImage(“assets/bird3.png”),
];
}

Before we dive into the main code, let’s collect our thoughts on what we need to do with the bird. First thing is to have the bird be able to jump. Then, whenever the bird jumps, it rotates itself upwards. And when it is rotated upwards, it flaps its wings. When the bird is not flying, it loses altitude and points downwards to the ground.

The what comes up must come down

function Bird() {
this.SCALE = 1.75;
this.y = WIN_HEIGHT / 2;
this.x = WIN_WIDTH / 3;
this.tilt = 0;
//Physics variables
this.tick_count = 0;
this.gravity = 0.2;
this.velocity = 0; //Jump velocity — NOT CONSTANT VELOCITY
this.jump_height = this.y + 20;
this.show = (i) => {
imageMode(CENTER);
// NOTE: we change the image mode here to make sure the bird
// rotates around it's center.
image(
BIRD_IMG[i],
0,
0,
this.SCALE * BIRD_IMG[i].width,
this.SCALE * BIRD_IMG[i].height
);
};
this.update = () => {
let displacement = this.getDisplacement();
this.y -= displacement;
this.updateTiltBasedOnDisplacement(displacement);
//Limiting y position of the bird
if (this.y > height — GROUND_HEIGHT) {

this.y = height — GROUND_HEIGHT;
displacement = 0;
}
if (this.y < -30) {
this.y = -30;
displacement = 0;
}
translate(this.x, this.y);
rotate(this.tilt);
}
this.getDisplacement = () => {
// Displacement means the amount of y position change every update
this.tick_count += 1;
//kinematics equation — initial velocity is 0 in most cases except
//for jump event
let displacement = this.velocity — 0.5 * this.gravity * this.tick_count ** 2;
this.velocity -= this.gravity * this.tick_count;
//Limiting velocity and displacement so fall rate doesn’t get
//too high
if (this.velocity < 0) {
this.velocity = 0;
}
if (displacement >= 5) {
displacement = 5;
}
//Positive displacement means we are jumping
if (displacement > 0) {
displacement += 10;
}
return displacement;
};
this.updateTiltBasedOnDisplacement = (displacement) => {
if (displacement > 0 || this.y < this.jump_height) {
// Negative means tilt up because counter-clockwise
this.tilt = -25;
} else {
if (this.tilt < 76) {
this.tilt += 20;
}
if (this.tilt > 90) {
this.tilt = 90;
}
}
};
this.hitGround = () => {
return this.y > height — GROUND_HEIGHT — BIRD_IMG[0].width;
};
}

This is a lot of code and it may look a little complicated but let’s just take it bit by bit. First, let’s look at the getDisplacement method. This is method is used to get the vertical distance at each interval the bird will experience. Within the bird object itself, we keep an internal clock which we call tick_count — this is used as the time variable in our kinematic equation which you can see with the displacement calculation.

In the update method, we get the displacement in which we add to the y-position of the bird. Here, we are not showing the bird image at the x- and y-positions like we did with the ground and the pipes. Instead, we use the translate function provided by p5 to move the bird — to be honest, I don’t really remember why I made the decision to write the code this way but it should be interesting to know a different way of doing things. The updateTiltBasedOnDisplacement is also pretty self-explanatory but if you have any questions about it, don’t hesitate to ask in the comment section.

What comes down… can sometimes come up?

function Bird() {
this.FLAP_INTERVAL = 2;
...
this.update = () => {
...
this.updateImage()
};
this.updateImage = () => {
if (this.tilt > 80) {
//If bird is pointing down, don’t animate flapping
this.show(1);
} else {
if (this.img_count <= this.FLAP_INTERVAL) {
this.show(0);
} else if (this.img_count <= this.FLAP_INTERVAL * 2) {
this.show(1);
} else if (this.img_count <= this.FLAP_INTERVAL * 3) {
this.show(2);
} else if (this.img_count <= this.FLAP_INTERVAL * 4) {
this.show(1);
}
}
};
this.updateImgCount = () => {
//Updates to keep track of flapping mechanism
this.img_count += 1;
if (this.img_count > this.FLAP_INTERVAL * 4) {
this.img_count = 0;
}
};
this.jump = () => {
this.velocity = 10; //add positive velocity to simulate a jump
this.tick_count = 0; //tick count reset
this.jump_height = this.y;
};
this.getDisplacement = () => {
...
};
this.updateTiltBasedOnDisplacement = (displacement) => {
...
};
this.hitGround = () => {
...
};
}

The updateImage method contains the “animation” logic in which the image shown is dependent on the image_count of the bird. The FLAP_INTERVAL is an adjustment factor to how many frames the image stays at a particular position. A FLAP_INTERVAL of 2 means each image is shown for 2 frames. The jumping here means simply to add a little bit of positive velocity to the bird and the getDisplacement method will handle the positional update.

// index.js...
let bird;
function setup(){
...
bird = new Bird()
}
function draw() {
...
//don't need to show bird because show is called inside update
bird.update();
}
function keyPressed() {
if (key === " ") {
bird.jump();
}
}
function mouseClicked() {
bird.jump()
}

Finally, keyPressed and mouseClicked are p5 functions that listen to user events. Here, I want to make the bird jump when I hit the space bar or when I click on the mouse. As you may have guessed, keyPressed and mouseClicked are p5 functions to detect user events.

That’s it for now

Well, that was a dense one but we are in the home stretch now. If there are any questions, please do let me know in the comments. Next time, we will be adding a simple collision detection mechanism and add the score tracking. Here is the link to part 4.

More content at plainenglish.io

--

--

Hi! I am a self-taught developer who love to play around with different web technologies