//Lingfan Jiang/ Kai Zhang
//Section B
//lingfanj@andrew.cmu.edu / kaiz1@andrew.cmu.edu
//final project
//bird variables
var g = 0.5;
var v = 0;
//upforce
var Fup = 20;
var birdx = 120;
var birdy = 240;
//pipe
var pipes = [];
var currentPipeY;
var gap = 170;
//counter
var successC = 0;
//lean degrees
var gameOn = true;
//terrain
var terrainDetail = 0.002;
var terrainSpeed = -0.001;
//clouds
var clouds = [];
var cloudsx = [];
var cloudsy = [];
var cloudsSpeed = -0.5;
var cloudsSize = [];
var gameOn = true;
var totalTime;
//board content
var level = ["Bronze", "Silver", "Gold", "Platinum", "Diamond", "Master", "王者!"]
var newcolor = ["green", "pink", "gold", "orchid", "tan", "grey", "white"]
var numbercount;
function setup() {
createCanvas(640,480);
frameRate(80);
pipes.push(makePipes());
}
function draw() {
background(170, 237, 239);
//create cloud objects
updateAndDisplayclouds();
terrain();
updateAndDisplayPipes();
drawBird(birdx, birdy);
controlBird();
//create pipe each 80 frames
if (frameCount % 90 == 0 & gameOn) {
pipes.push(makePipes());
}
if (pipes.length != 0) {
//remove the already gone pipes
if (pipes[0].x < -50) {
pipes.shift();
}
//hit test
if (pipes[0].x < birdx + 18 & pipes[0].x + 30 > birdx - 18) {
if (birdy - 15 < pipes[0].gapY || birdy + 15 > pipes[0].gapY + gap) {
//hit indicator
fill("red");
ellipse(birdx + 5, birdy, 30, 30);
//make the pipes stop
for (var i = 0; i < pipes.length; i++) {
pipes[i].speed = 0;
}
gameOn = false;
//game over content
textAlign(CENTER);
textSize(50);
fill("orange");
text("GAME OVER", width / 2, height / 2);
fill("orange");
push();
rectMode(CENTER);
textSize(18);
rect(320, 300, 100, 50);
fill(255);
noStroke();
text("RESTART", 321, 307);
pop();
//add score
} else if (pipes[0].x == birdx - 2) {
successC ++;
}
}
}
drawBoard();
}
////////////////////////////////
function controlBird(){
v += g;
birdy += v;
//make the bird stop when it hits the bottom or the top
if (birdy > height & gameOn) {
birdy = height;
v = 0;
}
else{
birdy = birdy;
}
if (birdy < -30) {
birdy = -30;
v = 0;
}
}
function updateAndDisplayclouds(){
for (var n = 0; n < 8; n ++) {
cloudsx.push(-150, random(width) + 150);
cloudsy.push(random(40, 100));
cloudsSize.push(random(80, 140));
clouds[n] = makeClouds(cloudsx[n], cloudsy[n], cloudsSize[n]);
clouds[n].cdraw();
cloudsx[n] += cloudsSpeed;
if (cloudsx[n] < -100) {
cloudsx[n] = random (width + 100, width + 150);
}
}
}
function drawBird(x, y) {
push();
translate(x, y);
angleMode(DEGREES);
rotate(1.5 * v);
fill("yellow");
ellipse(0, 0, 36, 30);
fill("white");
ellipse(0 + 9, 0 - 5, 15, 15);
ellipse(0 - 15, 0, 17, 8);
fill("black");
ellipse(0 + 13, 0 - 6, 5, 5);
fill("orange");
ellipse(0 + 10, 0 + 8, 20, 8);
ellipse(0 + 10, 0 + 2, 20, 8);
pop();
}
function drawBoard(){
noFill();
strokeWeight(2);
stroke(0);
rect(10, 10, 140, 46);
noStroke();
fill(0);
textSize(18);
textAlign(CENTER);
text("Score: " + successC, 80, 30);
text("Level: " + level[numbercount], 80, 48);
strokeWeight(1);
stroke(0);
}
function keyPressed() {
if (key === " " & gameOn) {
v -= Fup;
//set up a maximum speed for the bird
if (v < -10) {
v = -10;
}
}
}
function mouseClicked() {
//reset the game when the game is over
if (mouseX > 270 & mouseX < 370 && mouseY > 275 && mouseY < 325 && gameOn == false) {
pipes = [];
gameOn = true;
successC = 0;
}
}
function makePipes() {
var pipe = {
x: 640,
gapY: random(50, height - gap - 40),
pwidth: 50,
speed: 2,
move: pipeMove,
draw: pipeDraw,
col: "green"
}
return pipe;
}
function pipeDraw() {
//change the color and level when the score gets higher
numbercount = floor(successC / 5);
if (numbercount > 6) {
numbercount = 6;
}
fill(newcolor[numbercount]);
push();
translate(this.x, 0);
rect(0, -10, this.pwidth, this.gapY);
rect(-3, this.gapY - 30, this.pwidth + 6, 30);
rect(0, this.gapY + gap, this.pwidth, height - this.gapY - gap + 10);
rect(-3, this.gapY + gap, this.pwidth + 6, 30);
strokeWeight(5);
stroke(255);
line(10, 10, 10, this.gapY - 40);
line(10, 470, 10, this.gapY + gap + 40);
line(7, this.gapY - 10, 7, this.gapY - 20);
line(7, gap + this.gapY + 10, 7, gap + this.gapY + 20);
pop();
}
function pipeMove(){
this.x -= this.speed;
}
function updateAndDisplayPipes() {
for (var i = 0; i < pipes.length; i++) {
pipes[i].move();
pipes[i].draw();
}
}
function terrain() {
push();
stroke(135, 210, 167);
for (var q = 0; q < width; q++) {
var t = (q * terrainDetail) - (millis() * terrainSpeed / 10);
var y = map(noise(t), 0, 1 , height / 2 - 40, height - 50);
line(q, y, q, height);
}
pop();
}
function makeClouds(x, y, size) {
var cloud = {"cx": x, "cy": y, "csize": size, "cdraw": cloudDraw};
return cloud;
}
function cloudDraw() {
push();
noStroke();
fill(255);
ellipse(this.cx, this.cy, this.csize, this.csize * 0.7);
pop();
}
Introduction:
For our group final project, we decided to recreate the once viral game on mobile platforms – Flappy Birds, back in 2013. I(Kai) still recall how I would spend hours playing the game and tried to master it. Unfortunately, the game was removed from App Store because of it being “too addictive”. Up till now, I still have the game on my phone, despite it was too old for my current operating system. In order to experience a very unique game again, we tried to take a shot to develop the game.
Development Process:
First of all, Lingfan started to build the basic framework of the game, by creating an object “the bird” that will rise as we press the space button using basic physics, along with an array of tubes as objects that show up as time lapsed from right end of the canvas. Then I’ve taken over the file. I first used primitive geometries to create the tubes and the bird so they resemble the original look of the game. Also I’ve created the background and clouds that moved along with the camera panned, of course, of a much slower speed. As a next step, I started working on the “collision” moments when bird hit the tube. The logic is when the x position of tube passes through the location of the bird, the program determines if the bird is in between the gap of the tubes. If not, the tube turned grey, and an indicator would show up and informed the player the bird has hit the tube. The game was pretty much playable at this point, but we still need a reward/failure system, otherwise the game would run forever, even if the bird hits the tube. Lingfan then took over the file again and created it. He set a Boolean that determines if the game is over or not, which changes its value from true to false as it hits the tube. And then a button was created to restart the game. Every time the bird hits the tube, it would fall and we’ll see the “GAME OVER” appearing at the center of the screen. After the restart button is clicked, the code wipes the existing tubes array and brings back the bird so we can replay the game. As a final step, we worked together to clean up the code and further polish the game, including adding the score counter, a game rank indicator that tells you how good you are playing this game, the change of tube colors as you promote to the next level, the 3-dimensional look of the tube, etc. As this point, the game was pretty much finished.
Reflections:
One of the surprises about the game we’ve created is how it felt so close to the original game as we are playing. And during the development of the game, we’ve taken advantage of most skills that we acquired during the semester, so we got to practice pretty much everything again. Please go ahead and give it a try, and we hope you will enjoy the game as much as we do.
Game instruction:
Simply tap the space key so the bird rises to avoid the tube and earn 1 point each time the bird flies past a gap. You will get to the next level of the rank every time you successfully passed 5 tubes. (We tried to make it a little easier and more rewarding for you, as the original game was pretty hard to beat.)