Our project is inspired by the game Flappy Bird. In our rendition, 2020 events are the obstacles on the pipes and the goal is to “survive” 2020. The character moves up when you hit the letter ‘m’, the game ends if you hit one of the pipes, hit the ground or if you win by passing 12 pairs of pipes. If you lose by hitting a pipe or win you can play the game again, but if you hit the ground you lose and cannot restart the game. The two sounds you hear are the bounce when you move the character and the clash when the character hits one of the pipes. Based on which pipe you hit, you see a different “game over” message. We chose a darker color scheme and used a fire in the background to match the mood of 2020. If we had more time with this project, we would add a webcam function so that the character would be the user’s face instead of just a smiley face. Overall, this project was definitely challenging but also really fun to create.
/*
* Final Project
* Rishi Karthikeyan and Amy Lee
* rkarthik and amyl2
* Section B
*/
// Run a local server to hear sounds used
// Press the letter m to control character
// The game ends if the character hits the ground or the pipes
// If you lose by hitting a pipe or win you can play the game again
// but if you hit the ground you lose and cannot restart the game
var score = 0;
var count = 0;
var faceX = 50;
var faceY = 200;
var hitPipe = false;
var endGame = false;
var gameOn = true;
var gameStart = true;
var characterOn = true;
var pipes = [];
var fire = [];
var pipeImages = []; // an array to store the images
var factImages = []; // an array to store the dates between the pipes
var gameOverImages = []; // an array to store the customized game over images
var timeline1 = ["1/1/20", "1/16/20", "1/19/20", "2/2/20", "3/11/20",
"3/19/20", "3/20/20", "4/1/20", "5/1/20", "5/26/20", "11/6/20", "11/7/20"]
var timeline2=["Australian Bush Fire","Trump Impeached","COVID-19","Superbowl",
"Parasite Wins Oscars","Zoom University","Tiger King","Royal Family Split",
"Murder Hornets", "BLM Movement", "Election", "First Female VP Elect"]
function preload() {
// Circular images that go in the pipes
var filenames = [];
filenames[0] = "https://i.imgur.com/tP1n00I.png"; // Fire
filenames[1] = "https://i.imgur.com/sY9uWvm.png"; // Trump
filenames[2] = "https://i.imgur.com/lCBeEEt.png"; // Covid
filenames[3] = "https://i.imgur.com/3Rckn2m.png"; // Superbowl
filenames[4] = "https://i.imgur.com/ul9jOdN.png"; // Oscar
filenames[5] = "https://i.imgur.com/iWRAmux.png"; // Zoom
filenames[6] = "https://i.imgur.com/dbYhauj.png"; // Tiger King
filenames[7] = "https://i.imgur.com/lVbGhGa.png"; // Meg and Harry
filenames[8] = "https://i.imgur.com/U1PoDE5.png"; // Hornets
filenames[9] = "https://i.imgur.com/nqwizdc.png"; // BLM
filenames[10] = "https://i.imgur.com/xtEZvMR.png"; // Election
filenames[11] = "https://i.imgur.com/1lZjhEQ.png"; // Kamala
for (var i = 0; i < filenames.length; i ++) {
pipeImages.push(loadImage(filenames[i]));
}
// Displays game over message based on which pipe the character dies on
var filenames2 = [];
filenames2[0] = "https://i.imgur.com/ibfDcOO.png"; // Fire
filenames2[1] = "https://i.imgur.com/vFpcofS.png"; // Trump
filenames2[2] = "https://i.imgur.com/NMyD9W2.png"; // Covid
filenames2[3] = "https://i.imgur.com/aU3iUST.png"; // Superbowl
filenames2[4] = "https://i.imgur.com/hqLtxr4.png"; // Oscar
filenames2[5] = "https://i.imgur.com/59MSAMG.png"; // Zoom
filenames2[6] = "https://i.imgur.com/ArzVHYU.png"; // Tiger King
filenames2[7] = "https://i.imgur.com/up0miQL.png"; // Meg and Harry
filenames2[8] = "https://i.imgur.com/pSRx5bC.png"; // Hornets
filenames2[9] = "https://i.imgur.com/EzfHYCr.png"; // BLM
filenames2[10] = "https://i.imgur.com/wJvHY17.png"; // Election
filenames2[11] = "https://i.imgur.com/929AM73.png"; // Kamala
for (var i = 0; i < filenames2.length; i ++) {
gameOverImages.push(loadImage(filenames2[i]));
}
// Load sounds for when the character moves or hits the pipes
characterSound = loadSound("https://courses.ideate.cmu.edu/15-104/f2020/wp-content/uploads/2020/12/bounce.wav");
clashSound = loadSound("https://courses.ideate.cmu.edu/15-104/f2020/wp-content/uploads/2020/12/clash.wav");
}
function setup() {
createCanvas(450, 400);
background(12, 22, 24);
imageMode(CENTER);
useSound();
if (gameOn == true) {
fire.push(makeFire());
pipes.push(makePipes());
}
}
function soundSetup() {
// Set the volume for the sound effects
characterSound.setVolume(0.01);
clashSound.setVolume(0.05);
}
function draw() {
background(12, 22, 24);
gradientSky();
if (gameOn == true) {
// Start the count once the game is on
count++
// Display & add fire
updateAndDisplayFire();
if (count % 50 == 10){
fire.push(makeFire());
}
updateAndDisplayPipes();
// Add new pipes
if (count % 120 == 0){
if (gameOn == true) {
}
if (pipes.length < 12) {
pipes.push(makePipes());
}
if (count > 300) {
score += 150;
}
}
}
// Score tracker
textSize(15);
fill(72,160,141);
noStroke();
text('SCORE ' + score, 50, 23);
drawFace();
if (count >= 1690){
gameOn = false;
}
if (gameOn == false) {
gameWon();
}
if (hitPipe == true){
gameOver(round((count/120) - ((120*3)/120)));
}
}
function gradientSky() {
// Background color gradient
var darkCol = color(0,12,15);
var lightCol = color(14,38,39);
for (var y = 0; y < height; y ++){
var backG = map(y, 0, height, 0, 1);
var backGColor = lerpColor(darkCol, lightCol, backG);
stroke(backGColor);
line(0, y, width, y);
}
// Fire Embers
for (var i = 0; i < 35; i++) {
strokeWeight(4);
if (round(random(1)) == 0 ) {
stroke(148, 67, 43);
} else {
stroke(81, 33, 22);
}
point(random(0,width), random(-0, height));
}
}
// *************** FIRES *************** //
function updateAndDisplayFire(){
// Update the fire's positions, and display them.
for (var i = 0; i < fire.length; i++){
fire[i].move();
fire[i].draw();
}
}
// Method to update position of fire every frame
function fireMove() {
this.x += this.speed;
}
// Draw the fire
function fireDraw() {
push();
translate(0, height - 220);
scale(1.2, 1.75);
noStroke();
fill(81, 33, 22); //red fire
beginShape();
vertex(this.x, this.y + 125);
vertex(this.x, this.y + 115);
vertex(this.x + 5, this.y + 115);
curveVertex(this.x + 4, this.y + 113);
curveVertex(this.x + 3, this.y + 112);
curveVertex(this.x + 3, this.y + 108);
curveVertex(this.x + 5, this.y + 102);
curveVertex(this.x + 9, this.y + 98);
vertex(this.x + 18 + random(-1, 1), this.y + 93+ random(-1, 1));
curveVertex(this.x + 17, this.y + 110);
curveVertex(this.x + 21, this.y + 112);
curveVertex(this.x + 31, this.y + 109);
curveVertex(this.x + 31, this.y + 102);
curveVertex(this.x + 25, this.y + 95);
curveVertex(this.x + 24, this.y + 89);
curveVertex(this.x + 27, this.y + 83);
curveVertex(this.x + 34, this.y + 77);
curveVertex(this.x + 40, this.y + 70);
curveVertex(this.x + 34, this.y + 56);
vertex(this.x + 30 + random(-1, 1), this.y + 53 + random(-1, 1));
curveVertex(this.x + 40, this.y + 56);
curveVertex(this.x + 50, this.y + 64);
curveVertex(this.x + 48, this.y + 75);
curveVertex(this.x + 41, this.y + 83);
curveVertex(this.x + 38, this.y + 90);
curveVertex(this.x + 40, this.y + 98);
curveVertex(this.x + 49, this.y + 99);
curveVertex(this.x + 52, this.y + 95);
curveVertex(this.x + 48, this.y + 90);
curveVertex(this.x + 48, this.y + 84);
vertex(this.x + 55 + random(-1, 1), this.y + 75 + random(-1, 1));
curveVertex(this.x + 54, this.y + 81);
curveVertex(this.x + 56, this.y + 89);
curveVertex(this.x + 60, this.y + 97);
curveVertex(this.x + 56, this.y + 104);
curveVertex(this.x + 62, this.y + 117);
curveVertex(this.x + 72, this.y + 118);
curveVertex(this.x + 80, this.y + 112);
curveVertex(this.x + 75, this.y + 99);
curveVertex(this.x + 68, this.y + 92);
curveVertex(this.x + 63, this.y + 83);
curveVertex(this.x + 70, this.y + 63);
curveVertex(this.x + 84, this.y + 54);
vertex(this.x + 94 + random(-1, 1), this.y + 50 + random(-1, 1));
curveVertex(this.x + 85, this.y + 56);
curveVertex(this.x + 77, this.y + 65);
curveVertex(this.x + 73, this.y + 73);
curveVertex(this.x + 77, this.y + 77);
curveVertex(this.x + 87, this.y + 75);
vertex(this.x + 89 + random(-1, 1), this.y + 63 + random(-1, 1));
curveVertex(this.x + 93, this.y + 68);
curveVertex(this.x + 93, this.y + 78);
curveVertex(this.x + 87, this.y + 86);
curveVertex(this.x + 81, this.y + 93);
curveVertex(this.x + 87, this.y + 99);
curveVertex(this.x + 92, this.y + 99);
curveVertex(this.x + 97, this.y + 92);
curveVertex(this.x + 94, this.y + 87);
curveVertex(this.x + 94, this.y + 84);
vertex(this.x + 98 + random(-1, 1), this.y + 77 + random(-1, 1));
curveVertex(this.x + 101, this.y + 85);
curveVertex(this.x + 104, this.y + 89);
curveVertex(this.x + 107, this.y + 97);
curveVertex(this.x + 104, this.y + 104);
curveVertex(this.x + 101, this.y + 110);
curveVertex(this.x + 109, this.y + 115);
vertex(this.x + 113, this.y + 115);
vertex(this.x + 113, this.y + 128);
endShape(CLOSE);
pop();
push();
translate(0, height - 125);
scale(1, 1);
noStroke();
fill(148, 67, 43); //orange fire
beginShape();
vertex(this.x, this.y + 125);
vertex(this.x, this.y + 115);
vertex(this.x + 5, this.y + 115);
curveVertex(this.x + 4, this.y + 113);
curveVertex(this.x + 3, this.y + 112);
curveVertex(this.x + 3, this.y + 108);
curveVertex(this.x + 5, this.y + 102);
curveVertex(this.x + 9, this.y + 98);
vertex(this.x + 18 + random(-1, 1), this.y + 93+ random(-1, 1));
curveVertex(this.x + 17, this.y + 110);
curveVertex(this.x + 21, this.y + 112);
curveVertex(this.x + 31, this.y + 109);
curveVertex(this.x + 31, this.y + 102);
curveVertex(this.x + 25, this.y + 95);
curveVertex(this.x + 24, this.y + 89);
curveVertex(this.x + 27, this.y + 83);
curveVertex(this.x + 34, this.y + 77);
curveVertex(this.x + 40, this.y + 70);
curveVertex(this.x + 34, this.y + 56);
vertex(this.x + 30 + random(-1, 1), this.y + 53 + random(-1, 1));
curveVertex(this.x + 40, this.y + 56);
curveVertex(this.x + 50, this.y + 64);
curveVertex(this.x + 48, this.y + 75);
curveVertex(this.x + 41, this.y + 83);
curveVertex(this.x + 38, this.y + 90);
curveVertex(this.x + 40, this.y + 98);
curveVertex(this.x + 49, this.y + 99);
curveVertex(this.x + 52, this.y + 95);
curveVertex(this.x + 48, this.y + 90);
curveVertex(this.x + 48, this.y + 84);
vertex(this.x + 55 + random(-1, 1), this.y + 75 + random(-1, 1));
curveVertex(this.x + 54, this.y + 81);
curveVertex(this.x + 56, this.y + 89);
curveVertex(this.x + 60, this.y + 97);
curveVertex(this.x + 56, this.y + 104);
curveVertex(this.x + 62, this.y + 117);
curveVertex(this.x + 72, this.y + 118);
curveVertex(this.x + 80, this.y + 112);
curveVertex(this.x + 75, this.y + 99);
curveVertex(this.x + 68, this.y + 92);
curveVertex(this.x + 63, this.y + 83);
curveVertex(this.x + 70, this.y + 63);
curveVertex(this.x + 84, this.y + 54);
vertex(this.x + 94 + random(-1, 1), this.y + 50 + random(-1, 1));
curveVertex(this.x + 85, this.y + 56);
curveVertex(this.x + 77, this.y + 65);
curveVertex(this.x + 73, this.y + 73);
curveVertex(this.x + 77, this.y + 77);
curveVertex(this.x + 87, this.y + 75);
vertex(this.x + 89 + random(-1, 1), this.y + 63 + random(-1, 1));
curveVertex(this.x + 93, this.y + 68);
curveVertex(this.x + 93, this.y + 78);
curveVertex(this.x + 87, this.y + 86);
curveVertex(this.x + 81, this.y + 93);
curveVertex(this.x + 87, this.y + 99);
curveVertex(this.x + 92, this.y + 99);
curveVertex(this.x + 97, this.y + 92);
curveVertex(this.x + 94, this.y + 87);
curveVertex(this.x + 94, this.y + 84);
vertex(this.x + 98 + random(-1, 1), this.y + 77 + random(-1, 1));
curveVertex(this.x + 101, this.y + 85);
curveVertex(this.x + 104, this.y + 89);
curveVertex(this.x + 107, this.y + 97);
curveVertex(this.x + 104, this.y + 104);
curveVertex(this.x + 101, this.y + 110);
curveVertex(this.x + 109, this.y + 115);
vertex(this.x + 113, this.y + 115);
vertex(this.x + 113, this.y + 128);
endShape(CLOSE);
pop();
}
function makeFire() {
var fire = {x: width,
y: 0,
speed: -2,
move: fireMove,
draw: fireDraw}
return fire;
}
// *************** CHARACTER *************** //
function drawFace() {
if (characterOn == true) {
push();
// Head
noStroke();
fill(72,160,141);
ellipse(faceX, faceY, 30, 30);
// Eyes
fill(12, 22, 24);
ellipse(faceX - 9, faceY - 1, 3.5, 3.5);
ellipse(faceX + 9, faceY - 1, 3.5, 3.5);
// Mouth
noFill();
stroke(12, 22, 24);
strokeWeight(1);
arc(faceX, faceY, 5, 5, 0, PI, OPEN);
pop();
faceY += 1;
// Keep face from going off of the canvas
if (faceY < 15){
faceY += 5;
}
// Game over if you hit the bottom of the canvas
if (faceY > height - 15){
hitBottomGameOver();
}
}
}
// *************** PIPES *************** //
function updateAndDisplayPipes(){
// Update the pipe's positions, and display them
for (var i = 0; i < pipes.length; i++) {
pipes[i].move();
// Game over if face hits the pipes
if (((faceY < pipes[i].pipeHeight + 30) & (faceX > pipes[i].x &&
faceX < pipes[i].x + pipes[i].pipeWidth)) || ((faceY > 400 -
((400 - (pipes[i].pipeHeight + (200 - pipes[i].pipeHeight) +
(122 - (200 - pipes[i].pipeHeight)))) + 55)) &&
(faceX > pipes[i].x && faceX < pipes[i].x + pipes[i].pipeWidth))){
hitPipe = true;
clashSound.play();
}
if (gameStart == true) {
pipes[i].draw();
}
// Display image on pipe
pipes[i].imageNumber = i;
if ( pipes[i].imageNumber > 11) {
pipes[i].imageNumber = 0;
}
// Display timeline date in between pipes
pipes[i].timelineNumber = i;
if ( pipes[i].timelineNumber > 11) {
pipes[i].timelineNumber = 0;
}
}
}
// Method to update position of pipe every frame
function pipesMove() {
this.x += this.speed;
}
// Draw the pipe
function pipesDraw() {
var pipe1Height = this.pipeHeight - 30;
var pipe2Height = 400 - (this.pipeHeight + (200 - this.pipeHeight) +
(126 - (200 - this.pipeHeight)) );
fill(34,79,82);
noStroke();
// Top Pipe
rect(this.x, -30, this.pipeWidth, this.pipeHeight);
ellipse(this.x + 40, pipe1Height, this.pipeWidth, this.pipeWidth);
image(pipeImages[this.imageNumber], this.x + 40, pipe1Height, 60, 60);
// Fact in middle of pipe
textSize(15);
textAlign(CENTER);
fill(148, 67, 43);
text(timeline1[this.timelineNumber], this.x + 40, (pipe1Height +
((400 - ((pipe1Height) + (pipe2Height)))/2)) - 2);
text(timeline2[this.timelineNumber], this.x + 40, (pipe1Height +
((400 - ((pipe1Height) + (pipe2Height)))/2)) + 15);
// Bottom Pipe
push();
fill(34,79,82);
translate(0,400);
rect(this.x, -pipe2Height, this.pipeWidth, pipe2Height);
ellipse(this.x + 40, -pipe2Height, this.pipeWidth, this.pipeWidth);
image(pipeImages[this.imageNumber], this.x + 40, -pipe2Height, 60, 60);
pop();
}
function makePipes() {
var pipe = {x: 650,
pipeWidth: 80,
pipeHeight: random(130, 200),
speed: -2,
move: pipesMove,
draw: pipesDraw,
imageNumber: 0,
timelineNumber: 0
}
return pipe;
}
function keyPressed() {
if (key == 'm'){
faceY -= 20;
if (characterOn == true) {
characterSound.play();
}
}
}
// Function that is run when character hits the bottom of the canvas
function hitBottomGameOver() {
push();
gradientSky();
noStroke();
fill(72,160,141);
rectMode(CENTER);
rect(width/2, height/2, 227, 55);
textAlign(CENTER);
textSize(20);
fill(148, 67, 43);
text(' Y O U L O S T 2 0 2 0 ! ', width/2, height/2);
characterOn = false;
noLoop();
pop();
}
// Function that is run when the character hits the pipe
function gameOver(pipeNumber){
// Display the gradient sky
gradientSky();
// Display game over image depending on which pipe was hit
image(gameOverImages[pipeNumber], width/2, height/2, 250, 250);
// Display play again button
fill(148, 67, 43);
stroke(180, 67, 43);
strokeWeight(2);
push();
rectMode(CENTER);
rect(width/2, height/2 + 160, 110, 35);
pop();
textSize(15);
noStroke();
fill(255);
text('PLAY AGAIN', width/2, height/2 + 165);
gameStart = false;
characterOn = false;
hitPipe = true;
gameOn = false;
}
// Function that is run when the character wins the entire game
function gameWon() {
push();
// "YOU WON 2020" sign in center of screen
noStroke();
fill(72,160,141);
rectMode(CENTER);
rect(width/2, height/2, 225, 55);
textAlign(CENTER);
textSize(20);
fill(148, 67, 43);
text(' Y O U W O N 2 0 2 0 ! ', width/2, height/2);
// Play again button
fill(148, 67, 43);
stroke(180, 67, 43);
strokeWeight(2);
rect(width/2, height/2 + 55, 110, 35);
textSize(15);
noStroke();
fill(255);
text('PLAY AGAIN', width/2, height/2 + 55);
characterOn = false;
pop();
}
function mousePressed() {
// Press to play again after a user wins the game
if (count >= 1689 & gameOn == false) {
if (mouseX > width/2 - 55 && mouseX < width/2 + 55 &&
mouseY < height/2 + 72.5 && mouseY > height/2 + 37.5) {
gameOn = true;
gameStart = true;
characterOn = true;
endGame = false;
count = -50;
score = 0;
pipes = [];
fire = [];
}
}
// Press to play again when user lost the game by hitting a pipe
if (hitPipe == true) {
if (mouseX > width/2 - 55 & mouseX < width/2 + 55 &&
mouseY < height/2 + 177.5 && mouseY > height/2 + 142.5) {
hitPipe = false;
endGame = false;
gameStart = true;
gameOn = true;
characterOn = true;
count = -50;
score = 0;
pipes = [];
fire = [];
}
}
}
***on our Autolab submission we use the up arrow to control the character but here we used the letter m because with the up arrow it would move the whole page up and not just the canvas***