Final Project – Survive 2020 Game

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
/* 
 * 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 = [];
        }
    }
}





original proposal mockup

***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***

Leave a Reply