Final Project – Taisei Manheim, Carly Sacco, Jai Sawkar

For our project, we decided to create an iPhone and allow for interactions with some of the apps typical users like to spend time on. Carly created the Snapchat app and added in a few filters that can be changed by hovering over the different circles.Tai recreated a music app where the user can choose different songs to listen to. Jai created the Instagram and Clock apps and also some of the initial images for the home screen. For instagram, the page mimics a person’s profile picture (his own), and for the clock app, each of the locations in different time zones represent their current live time. Putting together the code was difficult, but Carly went over the commenting and formatting, while Tai helped with debugging and combining all of the code.

Although this project was very fun to complete, we had a lot of glitches we weren’t expecting. Having multiple apps that all ran but did not interfere with each other was a bit of a struggle. Coding between three people made the code lengthy and since we had separately used the same functions, when we consolidated our codes, the functions were overriding each other. Also, for the music app, we originally had an entire album to play from, but by the end of the project our code was so long and took a while to load, that we had to cut out a lot of songs and shorten the length of the songs, so there are only 2 short sound clips that play if you click the “1” or “2” key.

sketch

// Jai Sawkar, Tai Manheim, Carly Sacco
// jsawkar@andrew.cmu.edu, tmanheim@andrew.cmu.edu, csacco@andrew.cmu.edu
// Section C
// Final Project

//states that will determine is each app is running
//home screen starts on
menuOn = true;
//all other apps are off at the beginning of the code
musicOn = false;
snapchatOn = false;
instagramOn = false;
clockOn = false;

//var playerIMG;
var song1;
var song2;

function preload() {
    
    //images for the app icons on home screenh
    var snapURL = "https://i.imgur.com/UfJfDg1.png?"
    snapPic = loadImage(snapURL); 
    var musicURL = "https://i.imgur.com/m6NxUGy.png?"
    musicPic = loadImage(musicURL);
    var instaURL = "https://i.imgur.com/qTYtnyQ.png?"
    instaPic = loadImage(instaURL);
    var clockURL = "https://i.imgur.com/eX2G9P3.png?"
    clockBG = loadImage(clockURL);
    
    //Instagram Pictures
    var instaPic1URL = "https://i.imgur.com/FVXjmZU.jpg?2"
        instaPic1 = loadImage(instaPic1URL);
    var instaPic2URL = "https://i.imgur.com/RIkwcRD.jpg?2"
        instaPic2 = loadImage(instaPic2URL);
    var instaPic3URL = "https://i.imgur.com/eBPLHVa.jpg?2"
        instaPic3 = loadImage(instaPic3URL);
    var instaPic4URL = "https://i.imgur.com/aKhmsST.jpg?2"
        instaPic4 = loadImage(instaPic4URL);
        
    var instaPic5URL = "https://i.imgur.com/Pf6NRRh.png"
        instaPic5 = loadImage(instaPic5URL); 
        

    //music cover album photo
    var albumImage = "https://i.imgur.com/ajcDQxJ.jpg"
    albumCover = loadImage(albumImage);

    //load tracks
    song1 = loadSound("https://courses.ideate.cmu.edu/15-104/f2019/wp-content/uploads/2019/12/382698__iwawiwi__small-fragment-of-one-man-rumba-music-recording-from-wikimedia-common.wav");
    song2 = loadSound("https://courses.ideate.cmu.edu/15-104/f2019/wp-content/uploads/2019/12/213096__soundsexciting__gong-with-music.wav"); 
}


function soundSetup(){
    //volume for tracks
    song1.setVolume(2);
    song2.setVolume(2);
}


function setup() {
    createCanvas(640, 400);

    //background of phone
    push();
    rectMode(CENTER);
    fill(100)
    rect(width/2, height/2, 159, 323, 15);
    pop();

    //loads in the cameras for snapchat
    //starting camera: no filter
    normalCamera = createCapture(VIDEO);
    normalCamera.size(158, 318); //camera size to fit in phone
    normalCamera.hide();

    //filter1: threshold
    filter1 = createCapture(VIDEO);
    filter1.size(158, 318); //camera size to fit in phone
    filter1.hide();

    //filter2: invert
    filter2 = createCapture(VIDEO);
    filter2.size(158, 318); //camera size to fit in phone
    filter2.hide();

    //filter3: posterize
    filter3 = createCapture(VIDEO);
    filter3.size(158, 318); //camera size to fit in phone
    filter3.hide();

    //filter4: grayscale
    filter4 = createCapture(VIDEO);
    filter4.size(158, 318); //camera size to fit in phone
    filter4.hide();
}

function draw() {
    
    //when the m key is pressed it sets the music screen to true
    if (keyIsDown(77)) {
        musicOn = true;
        menuOn = false;
    }
    //when the s key is pressed it sets the snapchat screen to true
    if (keyIsDown(83)) {
        snapchatOn = true;
        menuOn = false;
    }
    //when the i key is pressed it sets the instagram screen to true
    if (keyIsDown(73)) {
        instagramOn = true;
        menuOn = false;
    }
    //when the c key is pressed it sets the clock screen to true
    if (keyIsDown(67)) {
        clockOn = true;
        menuOn = false;
    }
    //when the h key is pressed it sets the menu to true
    if (keyIsDown(72)) {
        menuOn = true;
        snapchatOn = false;
        musicOn = false;
        instagramOn = false;
        clockOn = false;
    }
    //when menuOn is true it calls the function to make the phone
    if (menuOn == true) {
        makePhone();
    }
    //when musicOn is true it calls the function for the music app
    if (musicOn == true){
       makeMusicApp();
    }
    //when snapchatOn is true it calls the function for the snapchat app
    if (snapchatOn == true){
       makeSnapchatApp();
    }
    //when instagramOn is true, it calls the funtion for the instagram app
    if (instagramOn == true){
       makeInstagramApp();
    }
    //when clockOn is true it calls the function for the clock app
    if (clockOn == true){
       makeClockApp();
    }

    //a function for the the outline of the phone 
    //so that it stays consistent through the apps
    makeBorder();

    //directions on the canvas
    push();
    noStroke();
    textFont('Helvetica');
    fill(189, 160, 0)
    text("type s for snapchat", 20, height / 2 - 40);
    text("hover over buttons to change filter", 30, height / 2 - 25);
    fill(169, 68, 194)
    text("type m for music", 20, height / 2 - 5);
    text("press the song number you want", 30, height / 2 + 10 ) 
    text("press the space bar to play & pause", 30, height  / 2 + 20);
    fill(219, 37, 113);
    text("type i for instagram", 20, height / 2 + 40)
    fill(103, 168, 156);
    text("type c for clock", 20, height / 2 + 60);

    fill(89, 92, 91);
    textSize(20)
    text("type h to return home", 20, height / 2 - 70);
    pop(); 
} 

function makeMusicApp() {
    //phone screen
    push()
    rectMode(CENTER);
    fill(50);
    rect(width / 2, height / 2, 148, 308, 15); 

    //album cover image
    image(albumCover, width / 2 - 30, height / 2 - 140);

    //pause button
    fill(50, 235, 50);
    noStroke();
    rect(width / 2, height / 2 - 42.5, 50, 20, 10);

    //lines seperating songs
    push();
    strokeWeight(2);
    stroke(255);
    line(width / 2 - 74, height / 2 - 25, width / 2 + 74, height / 2 - 25);
    line(width / 2 - 74, height / 2 - 5, width / 2 + 74, height / 2 - 5);
    line(width / 2 - 74, height / 2 + 15, width / 2 + 74, height / 2 + 15);
    line(width / 2 - 74, height / 2 + 35, width / 2 + 74, height / 2 + 35);
    line(width / 2 - 74, height / 2 + 55, width / 2 + 74, height / 2 + 55);
    line(width / 2 - 74, height / 2 + 75, width / 2 + 74, height / 2 + 75);
    line(width / 2 - 74, height / 2 + 95, width / 2 + 74, height / 2 + 95);
    line(width / 2 - 74, height / 2 + 115, width / 2 + 74, height / 2 + 115);
    line(width / 2 - 74, height / 2 + 135, width / 2 + 74, height / 2 + 135);
    pop();

    //album title
    noStroke();
    fill(255);
    textAlign(CENTER);
    textSize(12);
    text("Nostalgia Ultra", width / 2, height / 2 - 67.5);

    //pause button writing
    textSize(10);
    text("Pause", width / 2, height / 2 - 40);

    //song titles
    text("1. strawberry swing", width / 2, height / 2 - 12.5);
    text("2. novacane", width / 2, height / 2 + 7.5);
    text("3. we all try", width / 2, height / 2 + 27.5);
    text("4. songs for women", width / 2, height / 2 + 47.5);
    text("5. there will be tears", width / 2, height / 2 + 67.5);
    text("6. swim good", width / 2, height / 2 + 87.5);
    text("7. dust", width / 2, height / 2 + 107.5);
    text("8. american wedding", width / 2, height / 2 + 127.5);
    text("9. nature feels", width / 2, height / 2 + 147.5);
    pop(); 
}

function keyPressed() {
    //corresponds the number key pressed to the song
    //key 1 plays song 1
    if (keyCode === 49) {
        //play song 1
        song1.play();
        //pause others
        song2.pause();
    }
    
    //key 2 plays song 2
    if (keyCode === 50) {
        //play song 2
        song2.play();
        //pause others
        song1.pause();
    }

    //spacebar pauses any song
    if (keyCode === 32) {
        //pause all songs
        song1.pause();
        song2.pause();
    }
} 

function normalCamera() {
   push();
   //loads the pixels from the camera
   normalCamera.loadPixels(); 
   //displays the camera
   image(normalCamera, 242, 41);  
   pop();
}

function filterThreshold() {
    push();
    //loads the pixels from the camera
    filter1.loadPixels(); 
    //displays the camera
    image(filter1, 242, 41);  
    //applies the filter
    filter(THRESHOLD);
    pop();
    //buttons options on the screen
    buttons();

}

function filterInvert() {
    push();
     //loads the pixels from the camera
    filter2.loadPixels(); 
    //displays the camera
    image(filter2, 242, 41);  
    //applies the filter
    filter(INVERT);
    pop();
    //buttons options on the screen
    buttons();

}

function filterPosterize() {
    push();
    //loads the pixels from the camera
    filter3.loadPixels(); 
    //displays the camera
    image(filter3, 242, 41); 
    //applies the camera 
    filter(POSTERIZE, 3);
    pop();
    //buttons options on the screen
    buttons();

}

function filterGray() {
    push();
        //loads the pixels from the camera 
    filter4.loadPixels();
    //displays the camera
    image(filter4, 242, 41);  
    //applies the filter
    filter(GRAY);
    pop();
    //buttons options on the screen
    buttons();

}

function buttons() {
    //function that creates the buttons that appear in the snapchat app
    //biggest normal middle circle
    noFill();
    stroke(255);
    strokeWeight(2);
    ellipse(width/2, height / 2 + 135, 25, 25);

    //first button: threshold
    noFill();
    stroke(255);
    strokeWeight(2);
    ellipse(width/2 - 60, height / 2 + 135, 20, 20);

    //second button: invert
    noFill();
    stroke(255);
    strokeWeight(2);
    ellipse(width/2 - 30, height / 2 + 135, 20, 20);

    //third button: posterize
    noFill();
    stroke(255);
    strokeWeight(2);
    ellipse(width/2 + 30, height / 2 + 135, 20, 20);

    //fourth button: blur
    noFill();
    stroke(255);
    strokeWeight(2);
    ellipse(width/2 + 60, height / 2 + 135, 20, 20);

}

function makeSnapchatApp(){
   //begins with the normal camera &
   //uses the normal camera as default when no 
   //filter is running
   push();
   normalCamera.loadPixels(); 
   image(normalCamera, 242, 41);
   pop();

   buttons();

   //if the mouse is hovering over a button, then the associated 
   //filter will appear

   //threshold filter
    if  (mouseX > 250 & mouseX < 275  && mouseY > 320) {
        filterThreshold();
    }
    //invert filter
    if  (mouseX > 270 & mouseX < 295  && mouseY > 320) {
        filterInvert();
    }
    //posterize filter
    if  (mouseX > 335 & mouseX < 360  && mouseY > 320) {
        filterPosterize();
    }
    //gray filter
    if  (mouseX > 365 & mouseX < 400  && mouseY > 320) {
        filterGray();
    }

}

function makeInstagramApp(){
        //Instagram Background
            push();
            rectMode(CENTER);
            fill(245);
            noStroke();
            rect(width/2, height/2, 159, 323, 15);

        //Instagram Heading
            textSize(32);
            fill(42, 57, 107);
            textAlign(CENTER);
            textFont('Satisfy');
            text('Instagram', width/2 + 1, 80);

        //Line Below Instagram
            stroke(42, 57, 107); 
            strokeWeight(.5);
            line(width/2 - 73.5, 95, width/2 + 73.5, 95);

        //Profile Pic
            fill(240);
            strokeWeight(1.5);
            push();
            scale(1.2)
            image(instaPic5, width/2 - 113.5, 84);
            pop();
            noFill();

        //Profile Info
            noStroke();
            fill(42, 57, 107);
            textAlign(LEFT);
            textFont('Helvetica');
            textSize(10);
            text("Jai Sawkar", width/2 - 68, 165);
            fill(60)
            textSize(9);
            text("@jsawkar", width/2 - 68, 175);

        //Followers & Following
            rectMode(CORNER)
            fill(60, 60, 60, 20)
            rect(width/2 - 7, 102.5, 75, 30);
            stroke(60);
            strokeWeight(.2);
            line((width/2-7 + width/2 + 68)/2, 102.5, (width/2-7 + width/2 + 68)/2, 132.5)
         
            fill(60)
            textSize(7);
            noStroke();
            text("Followers", width/2 - 3, 110);
            text("Following", width/2 + 35, 110);

            textSize(8);
            fill(0);
            textAlign(LEFT);
            text("1,027", width/2 + 2, 122)
            text("971", width/2 + 43, 122);

        //Profile Bio
            textSize(9);
            textAlign(RIGHT);
            text("sammamish, wa", width/2 + 65, 165);
            text("carnegie mellon '22", width/2 + 65, 175);
        
        //Line Below Instagram Bio
            stroke(42, 57, 107); 
            strokeWeight(.5);
            line(width/2 - 73.5, 190, width/2 + 73.5, 190);
            pop();

        //Loading Instagram Pictures
            push();
            scale(0.6)
            image(instaPic1, width/2 + 105, 330); 
            image(instaPic2, width/2 + 220, 330);
            image(instaPic3, width/2 + 105, 445);
            image(instaPic4, width/2 + 220, 445);
            pop();
}

function makeClockApp(){
    push();
    //Black Background
        rectMode(CENTER);
        fill(20);
        noStroke();
        rect(width/2, height/2, 159, 323, 15);

    //CLOCK Heading
     
        textSize(32);
        fill('WHITE');
        textFont('Helvetica');
        text('Clock', width/2 - 70, 80);


    //Line below Clock
        stroke(255); 
        strokeWeight(.5);
        line(width/2 - 73.5, 90, width/2 + 73.5, 90);

        var h = hour();
        var m = minute();
        var s = second();

        var hPos = width/2 + 10; //variable for hour time position
        var mPos = width/2 + 40; //variable for minute time position

        var singleHPos = width/2 + 20; //variable for hour time position if it is a single digit
        var singleMPos = width/2 + 51;//variable for minute time position if it is a single digit

        //Pittsburgh Variables
            var pittsburghH = h; //Current hour
            var pittsburghM = m; //Current minute

            var pittsburghHPos = hPos; //Hour position for Pittsburgh
            var pittsburghMPos = mPos; //Minute position for Pittsburgh

            //If Hour is a Single Digit, Move Hour Time To Be Closer to ":"
                if (pittsburghH < 10){ 
                    pittsburghHPos = singleHPos;
                }

            //If Minute is a Single Digit, Move Minute Time To Be Closer to ":" & add a "0"
                if (pittsburghM < 10){
                    pittsburghMPos = singleMPos;
                    textSize(20);
                    fill(255);
                    text("0", mPos, 115);
                }

        //Cupertino Variables
        var cupertinoH = h - 3; //Current local hour
        var cupertinoM = m; //Current local minute

        var cupertinoHPos = hPos; //Hour position for Cupertino
        var cupertinoMPos = mPos; //Minute position for Cupertino

        //If Hour is a Single Digit, Move Hour Time To Be Closer to ":"
                if (cupertinoH < 10){ 
                    cupertinoHPos = singleHPos;
                }
        //If Minute is a Single Digit, Move Minute Time To Be Closer to ":" & add a "0"
                if (cupertinoM < 10){
                    cupertinoMPos = singleMPos;
                    textSize(20);
                    fill(255);
                    text("0", mPos, 147);
                }

        //Beijing Variables
        var beijingH = (h + 13) % 12; //Current local hour
        var beijingM = m; //Current local minute

        var beijingHPos = hPos; //Hour position for Beijing
        var beijingMPos = mPos; //Minute position for Beijing

        //If Hour is a Single Digit, Move Hour Time To Be Closer to ":"
                if (beijingH < 10){ 
                    beijingHPos = singleHPos;
                }
        //If Minute is a Single Digit, Move Minute Time To Be Closer to ":" & add a "0"
                if (beijingM < 10){
                    beijingMPos = singleMPos;
                    textSize(20);
                    fill(255);
                    text("0", mPos, 181);
                }

        //Chennai Variables
        var chennaiH = (h + 10) % 12; //Current local hour
        var chennaiM = (m + 30) % 60; //Current local minute

        var chennaiHPos = hPos; //Hour position for Chennai
        var chennaiMPos = mPos; //Minute position for Chennai

        //If Hour is a Single Digit, Move Hour Time To Be Closer to ":"
                if (chennaiH < 10){ 
                    chennaiHPos = singleHPos;
                }
        //If Minute is a Single Digit, Move Minute Time To Be Closer to ":" & add a "0"
                if (chennaiM < 10){
                    chennaiMPos = singleMPos;
                    textSize(20);
                    fill(255);
                    text("0", mPos, 215);
                }

        //Chicago Variables
        var chicagoH = h-1; //Current local hour
        var chicagoM = (m) % 60; //Current local minute

        var chicagoHPos = hPos; //Hour position for Chicago
        var chicagoMPos = mPos; //Minute position for Chicago

        //If Hour is a Single Digit, Move Hour Time To Be Closer to ":"
                if (chicagoH < 10){ 
                    chicagoHPos = singleHPos;
                }
        //If Minute is a Single Digit, Move Minute Time To Be Closer to ":" & add a "0"
                if (chicagoM < 10){
                    chicagoMPos = singleMPos;
                    textSize(20);
                    fill(255);
                    text("0", mPos, 249);
                }

        //Abu Dhabi Variables
        var adH = (h + 9) % 12; //Current local hour
        var adM = (m) % 60; //Current local minute

        var adHPos = hPos; //Hour position for Abu Dhabi
        var adMPos = mPos; //Minute position for Abu Dhabi

        //If Hour is a Single Digit, Move Hour Time To Be Closer to ":"
                if (adH < 10){ 
                    adHPos = singleHPos;
                }
        //If Minute is a Single Digit, Move Minute Time To Be Closer to ":" & add a "0"
                if (adM < 10){
                    adMPos = singleMPos;
                    textSize(20);
                    fill(255);
                    text("0", mPos, 283);
                }

        //Adelaide Variables
        var adelaideH = (h + 6) % 12; //Current local hour
        var adelaideM = (m + 30) % 60; //Current local minute

        var adelaideHPos = hPos; //Hour position for Abu Dhabi
        var adelaideMPos = mPos; //Minute position for Abu Dhabi

        //If Hour is a Single Digit, Move Hour Time To Be Closer to ":"
                if (adelaideH < 10){ 
                    adelaideHPos = singleHPos;
                }
        //If Minute is a Single Digit, Move Minute Time To Be Closer to ":" & add a "0"
                if (adelaideM < 10){
                    adelaideMPos = singleMPos;
                    textSize(20);
                    fill(255);
                    text("0", mPos, 318);
                }


        //Displaying Pittsburgh Time
            //Local Time Label
                textSize(8);
                fill(150);
                noStroke();
                text('LOCAL TIME', width/2 - 70, 100);
            //Pittsburgh Label
                textSize(15);
                fill(255);
                text('Pittsburgh', width/2 - 70, 115);
            //Pittsburgh Time
                textSize(20);
                text(pittsburghH, pittsburghHPos, 115);
                text(pittsburghM, pittsburghMPos, 115);
                text(":", width/2 + 32, 114);
            //Line below Pittsburgh
                stroke(255); 
                strokeWeight(.5);
                line(width/2 - 74, 122, width / 2 + 73, 122);

        //Displaying Cupertino Time
            //- 3 Hrs Time Label
                textSize(8);
                fill(150);
                noStroke();
                text('-3HRS', width / 2 - 70, 132);
            //Cupertino Label
                textSize(15);
                fill(255);
                text('Cupertino', width / 2 - 70, 147);
            //Cupertino Time
                textSize(20);
                text(cupertinoH, cupertinoHPos, 147);
                text(cupertinoM, cupertinoMPos, 147);
                text(":", width/2 + 32, 146);
            //Line below Cupertino
                stroke(255); 
                strokeWeight(.5)
                line(width/2 - 74, 156, width / 2 + 73, 156);

        //Displaying Beijing Time
            // + 13 Hrs Time Label
                textSize(8);
                fill(150);
                noStroke();
                text('+13HRS', width / 2 - 70, 166);
            //Beijing Label
                textSize(15);
                fill(255);
                text('Beijing', width / 2 - 70, 181);
            //Beijing Time
                textSize(20);
                text(beijingH, beijingHPos, 181);
                text(beijingM, beijingMPos, 181);
                text(":", width / 2 + 32, 180);
            //Line below Beijing
                stroke(255); 
                strokeWeight(.5);
                line(width / 2 - 74, 190, width / 2 + 73, 190);

        //Displaying Chennai Time
            // + 10:30 Hrs Time Label
                textSize(8);
                fill(150);
                noStroke();
                text('+10:30HRS', width/2 - 70, 200);
            //Chennai Label
                textSize(15);
                fill(255);
                text('Chennai', width/2 - 70, 215);
            //Chennai Time
                textSize(20);
                text(chennaiH, chennaiHPos, 215);
                text(chennaiM, chennaiMPos, 215);
                text(":", width / 2 + 32, 214);
            //Line below Chennai
                stroke(255); 
                strokeWeight(.5);
                line(width / 2 - 74, 224, width/2 + 73, 224);


        //Displaying Chicago Time
            // - 1Hrs Time Label
                textSize(8);
                fill(150);
                noStroke();
                text('-1HRS', width/2 - 70, 234);
            //Chennai Label
                textSize(15);
                fill(255);
                text('Chicago', width/2 - 70, 249);
            //Chennai Time
                textSize(20);
                text(chicagoH, chicagoHPos, 249);
                text(chicagoM, chicagoMPos, 249);
                text(":", width / 2 + 32, 248);
            //Line below Chicago
                stroke(255); 
                strokeWeight(.5);
                line(width / 2 - 74, 258, width/2 + 73, 258);

        //Displaying Abu Dhabi Time
            // +9Hrs Time Label
                textSize(8);
                fill(150);
                noStroke();
                text('+9Hrs', width / 2 - 70, 268);
            //Abu Dhabi Label
                textSize(15);
                fill(255);
                text('Abu Dhabi', width / 2 - 70, 283);
            //Abu Dhabi Time
                textSize(20);
                text(adH, adHPos, 283);
                text(adM, adMPos, 283);
                text(":", width / 2 + 32, 282);
            //Line below Abu Dhabi
                stroke(255); 
                strokeWeight(.5);
                line(width / 2 - 74, 293, width / 2 + 73, 293);

        //Displaying Adelaide Time
            // +9Hrs Time Label
                textSize(8);
                fill(150);
                noStroke();
                text('+15:30Hrs', width / 2 - 70, 303);
            //Abu Dhabi Label
                textSize(15);
                fill(255);
                text('Adelaide', width / 2 - 70, 318);
            //Abu Dhabi Time
                textSize(20);
                text(adelaideH, adelaideHPos, 318);
                text(adelaideM, adelaideMPos, 318);
                text(":", width / 2 + 32, 317);
                pop();
}


function makeBorder(){
    //phone outline
    noFill();
    stroke(60, 59, 59);
    strokeWeight(6);
    rect(width/2, height/2, 159, 323, 15)

    //notch
    fill(60, 59, 59);
    rect(width/2, 48, 81, 11, 10);

    //fill(61, 60, 60);
    //ringer
    rect(239.5, 94, 1.6, 11.9);
    //Volume Up
    rect(239.5, 123, 1.6, 23.75);
    //Volume Down
    rect(239.5, 149, 1.6, 23.75);

    //Lock
    rect(400.5, 137, 1.6, 38)
   
    //Notch Hardware (Left to Right)
    fill(88, 89, 91);
    ellipseMode(CENTER);
    ellipse(289, 48, 5);
    ellipse(301, 48, 5);
    ellipse(340, 48, 5);
}

function makePhone(){
    //phone background
    push();
    rectMode(CENTER);
    fill(100)
    rect(width/2, height/2, 159, 323, 15);
    pop();
    //calls the app images on the home screen
    push()
    rectMode(CENTER);
    makeSnapchat();
    makeMusic();
    makeInsta();
    makeClock();
    pop();
}


function makeSnapchat(){
    //loads the snapchat app for the homescreen
    push();
    translate(width/2 - 60, 85)
    scale(0.25);
    image(snapPic, 0, 0)
    pop();
}

function makeMusic(){
    //loads the music app for the homescreen
    push();
    
    translate(width/2 + 6, 85)
    scale(0.25);
    image(musicPic, 0, 0)

    pop();
}

function makeInsta(){
    //loads the insta app for the homescreen
    push();
    
    translate(width/2 - 60, 160)
    scale(0.25);
    image(instaPic, 0, 0)

    pop();
}

function makeClock(){
    //loads the clock app for the homescreen
    push();
    
    translate(width/2 + 6, 160)
    scale(0.25);
    image(clockBG, 0, 0)
    pop();

    push()
    translate(width/2 + 1.5, 155);
    scale(0.15);
    //function that makes the clock work
    clockMove();
}


function clockMove(){
    //controls the clock on the homescreen to work
    angleMode(DEGREES);

    var h = hour();
    var m = minute();
    var s = second();

    push()
    noFill();

    translate(200, 200);
    rotate(-90);

    strokeWeight(5);
    var hStart = map(h % 12, 0, 12, -90, 360);
    arc(0, 0, 220, 220, 0, hStart)

    strokeWeight(4);
    var mStart = map(m, 0, 60, 0, 360);
    arc(0, 0, 240, 240, 0, mStart)

    stroke(0);
    strokeWeight(2);
    var sStart = map(s, 0, 60, 0, 360);
    arc(0, 0, 260, 260, 0, sStart)
    pop()
}

Kimberlyn Cho and Sarah Kang – Final Project

finalproj

/*Kimberlyn Cho
ycho2@andrew.cmu.edu
Sarah Kang
sarahk1@andrew.cmu.edu
Section C
Final Project
*/

/*INSTRUCTIONS:
(SARAH KANG) OPENING PAGE

(KIMBERLYN CHO) SCENE 1: 
    -use MOUSE to choose ingredient and click in bowl to place ingredient
    -press ENTER to mix ingredients
    -use MOUSE to add salt and pepper as desired and ENTER to mix again
(KIMBERLYN CHO) SCENE 2:
    -use LEFT and RIGHT arrow keys to move frying pan
    -dumpling is fully fried once "FINISH" button pops up
(SARAH KANG) SCENE 3: 
    -use DOWN arrow key to plate/start
    -use keys 'y, u, m' to finish and eat
*/


//KIMBERLYN CHO GLOBAL VARIABLES\\
//ingredient image links
var ingredients = [
    "https://i.imgur.com/kbiRJNO.png?3",
    "https://i.imgur.com/BqKFhGa.png?3",
    "https://i.imgur.com/X37RSp0.png?2",
    "https://i.imgur.com/1hN8NJV.png?5",
    "https://i.imgur.com/zlwmpov.png?3"];
//current ingredient selection
var currIngredient;
//content of bowl
var texts = [];
//shifting frying pan positions
var xpos = 200;
var ypos = 150;
//dumpling location on frying pan
var dxpos = 200;
//measure frying time (number of frying pan shifts)
var frytime = 1;
//toggle to control when scene3 is drawn
var updatedraw = true;
//toggles to control scenes
var toggle = true;
var toggle2 = true;
//drag effect of dumpling on frying pan
var shift = false;
var startTime = -1000;
var startXPos;
var transitionLength = 1.0;
var direction = 200;

function preload() {
    //KIMBERLYN CHO IMAGES
    //loading images into variables
    mushroom = loadImage(ingredients[0]);
    greenonion = loadImage(ingredients[1]);
    steak = loadImage(ingredients[2]);
    salt = loadImage(ingredients[3]);
    pepper = loadImage(ingredients[4]);

    //SARAH KANG IMAGES
    var plate = "https://i.imgur.com/kvmDFlr.jpg";
    var chopsticks = "https://i.imgur.com/cWrGYFq.jpg";
    var dinnertime = "https://i.imgur.com/QCuDyQk.jpg";
    var dumps = "https://i.imgur.com/wIrlEFb.jpg";
    var hand1 = "https://i.imgur.com/MmOZHtp.jpg"
    var hand2 = "https://i.imgur.com/FBDI4yN.jpg"
    var hand3 = "https://i.imgur.com/bYtuDzt.jpg"
    var kid = "https://i.imgur.com/BiavReL.jpg"
    var finaldump = "https://i.imgur.com/0ZQYp9Y.jpg"
        Img = loadImage(plate);
        Img2 = loadImage(chopsticks);
        Img3 = loadImage(dinnertime);
        Img4 = loadImage(dumps);
        Img5 = loadImage(hand1);
        Img6 = loadImage(hand2);
        Img7 = loadImage(hand3);
        Img8 = loadImage(kid);
        Img9 = loadImage(finaldump);
}

function setup() {
    createCanvas(400, 300);
    background(0);
    scene0setup();
}

function draw() {
    scene1();
    //scene3
    if (updatedraw == false) {
        background(0);
        scene2();
        toggle2 == false;
    };
}

//START PAGE (SARAH KANG)
function scene0setup() {
    //plate
    image(Img, 0, 50);
    //title
    textSize(60);
    textFont('Arial');
    textAlign(CENTER);
    stroke(255);
    strokeWeight(3);
    fill(0);
    text("\"DUMPLING\"", 200, 150);
    //chopsticks
    image(Img2, 200, 0);
    //start button
    noFill();
    rect(320, 260, 60, 24);
    textSize(10);
    strokeWeight(0.4);
    text("START", 350, 276);
}

//SCENE 1: ADDING AND MIXING FILLING (KIMBERLYN CHO)

//SCENE 1 BACKGROUND
function scene1setup() {
    //bowl
    background(0);
    noStroke();
    fill(240);
    ellipse(width / 2, height / 2, 200);
    fill(255);
    ellipse(width / 2, height / 2, 100);
    stroke(0);
    noFill();
    ellipse(width / 2, height / 2, 180);
    stroke(255);
    //next button
    rect(320, 260, 60, 24);
    textSize(10);
    strokeWeight(0.4);
    textAlign(CENTER);
    text("NEXT", 350, 276);
    //ingredient selection
    image(mushroom, 10, 10);
    image(greenonion, 10, 60);
    image(steak, 12, 130);
    image(salt, 340, 20);
    image(pepper, 337, 90);

    fill(255);
    textAlign(LEFT);
    textSize(10);
    text("1. click ingredient of choice, then place it in bowl with another click", 20, 280);
    text("2. to mix, press the ENTER key", 20, 290);
}   

//SCENE 1 INTERACTION
function scene1() {
    //updating ingredient selection based on where user clicks
    if (mouseIsPressed) {
        fill(0);
        if (mouseX > 10 & mouseX < 60 &&
            mouseY > 15 && mouseY < 55) {
            currIngredient = "\"mushroom\""
        };
        if (mouseX > 10 & mouseX < 60 &&
            mouseY > 65 && mouseY < 120) {
            currIngredient = "\"green onion\""
        };
        if (mouseX > 10 & mouseX < 60 &&
            mouseY > 130 && mouseY < 160) {
            currIngredient = "\"beef\""        
        };
        if (mouseX > 340 & mouseX < 370 &&
            mouseY > 20 && mouseY < 70) {
            currIngredient = "\"salt\""
        };
        if (mouseX > 340 & mouseX < 360 &&
            mouseY > 95 && mouseY < 140) {
            currIngredient = "\"pepper\""
        };
    };
}
//*see mousepressed fuction for code that updates and draws content of bowl
//*see keypressed function for code that mixes content of bowl

//SCENE 2: FRYING DUMPLING

//SCENE 2 BACKGROUND
function scene2() {
    //frying pan
    noStroke();
    fill(240);
    ellipse(xpos, ypos, 200);
    rectMode(CENTER);
    rect(xpos, 250, 25, 100);
    fill(255);
    ellipse(xpos, ypos, 150);
    fill(0);
    text("\"DUMPLING\"", dxpos, ypos);
    fill(255);
    textAlign(LEFT);
    textSize(12);
    text("press left and right arrow keys to fry until ready to plate!", 20, 20);

    //indication of when dumpling is done frying
    if  (frytime > 5) {
        stroke(255);
        noFill();
        strokeWeight(1);
        rectMode(CORNER);
        rect(320, 260, 60, 24);
        textSize(10);
        strokeWeight(0.4);
        textAlign(CENTER);
        text("PLATE", 350, 276);
        noLoop();
    } else {
        stroke(255);
        noFill();
        strokeWeight(1);
        rectMode(CORNER);
        rect(320, 260, 60, 24);
        textSize(10);
        strokeWeight(0.4);
        textAlign(CENTER);
        text("NEXT", 350, 276);
    };
    //updating position of dumpling on frying pan with a drag effect
    if (shift & startTime > 0) {
        var currentTime = millis()/1000 - startTime;
        var slide = map(currentTime, 0, transitionLength, 0, 1);
        dxpos = lerp(dxpos, direction, slide);
        if (currentTime > transitionLength) {
            shift = false;
        };
    };
}

//SCENE 2 INTERACTION
//**see keypressed function for code that shifts frying pan

function scene3setup() {
    background(0);
    //plate with silverware
    image(Img3, 0, 10);
    //instruction
    fill(255);
    textAlign(LEFT);
    textSize(12);
    text("press down arrow to plate, then type the letter that pops up", 15, 285);
}

function mousePressed() {
//INTERACTION PART OF SCENE 1 CONT.
    //constraining ingredients to bowl
    if (mouseX > 150 & mouseX < 250 &&
        mouseY > 100 && mouseY < 200) {
        //updating array to store content of bowl even if ingredients are added after mix
        texts.push(currIngredient);
        //adding selected ingredient into bowl
        text(currIngredient, mouseX, mouseY);
    };

//SCENE TRANSITIONS
    //start to scene 1
    if ((mouseX > 320 & mouseX < 380 && mouseY > 260 && mouseY < 280) && (toggle == true)) {
        scene1setup();
    };
    //scene 1 to scene 2
    if ((mouseX > 320 & mouseX < 380 && mouseY > 260 && mouseY < 280) && (toggle == false)) {
        updatedraw = false;
    };

    //scene 2 to scene 3
    if ((mouseX > 320 & mouseX < 380 && mouseY > 260 && mouseY < 280) && (frytime > 5)) {
        updatedraw = true;
        scene3setup()
    };

}

function keyPressed() {
//INTERACTION PART OF SCENE 1 CONT.
    //mixing content of bowl
    if (keyCode === ENTER) {
        //clearing bowl
        scene1setup();
        //redrawing contents of bowl with randomization to mimic mixing
        for (var i = 0; i < texts.length; i++) {
            fill(0);
            text(texts[i], floor(random(150, 250)), floor(random(100, 200)));
        };
        toggle = false;
    };

//INTERACTION PART OF SCENE 2
    //shifting pan and dumpling position
    if (keyCode == LEFT_ARROW) {
        //updating frying pan location
        xpos = 150;
        //initiating drag effect on dumpling
        shift = true;
        direction = 150;
        startTime = millis()/1000;
        startXPos = xpos;
        frytime +=1;
        toggle2 = false;
    };
    if (keyCode == RIGHT_ARROW) {
        //updating frying pan location
        xpos = 250;
        //initiating drag effect on dumpling
        direction = 250;
        shift = true;
        startTime = millis()/1000;
        startXPos = xpos;
    };

//INTERACTION PART OF SCENE 3
    //keypress to start
    if (keyCode == DOWN_ARROW) {
            image(Img4, 145, 115); //dumpling
            stroke(255);
            textSize(20);
            text("\"Y\"", 300, 40);
    }
    //sequence of typing
    if (keyCode == 'y') {
    }
    if (keyCode == 'u') {
    }
    if (keyCode == 'm') {        
    }
}
function keyTyped() {
    if (key === 'y') {
        stroke(255);
        textSize(20);
        text("\"U\"", 300, 250);
    }
    if (key === 'u') {
        stroke(255);
        textSize(20);
        text("\"M\"", 75, 250);
    }    
    if (key === 'm') {
        //kid thumbs up
        image(Img8, 10, 10);
        //plate of dumplings
        image(Img9, 125, 100);
        noStroke();
        fill(0);
        rect(0, 270, 400, 30);
        fill(255, 194, 194);
        textAlign(CENTER);
        textSize(20);
        text("time to eat!", 200, 285);
    }  
}

For our final project, we were inspired by the classic Nintendo game, Cooking Mama and wanted to recreate an unconventional, more conceptual version of the cooking experience. We were also inspired by the abstract approach in representing visuals often seen in high fashion, such as Off-White; the dumpling being cooked is represented by the literal word “dumpling”. We struggled a lot with putting our scenes together and working out the transitions, but after some compromise and editing, we were able to put together a fun game.

Timothy Liu — Final Project


tcliu-FINALPROJECT

Welcome to Crossy Chicken, my final project for 15-104! A tribute to the original Frogger, Crossy Chicken involves a chicken trying to reach her beloved egg. But to get there, the chicken must first cross two busy streets as well as a perilous bridge; can you get her back to her egg?

To play, use the WASD keys (W = Up, S = Down, A = Left, D = Right) to direct the chicken and follow the instructions on screen! Make sure you dodge all the cars and don’t let the chicken’s feet touch the water!

I had a fantastic time coding this project, and I’m proud of how I distributed the work. I broke my project down into phases, and I worked through them in the following order:

  1. Character movement
  2. Introducing cars
  3. Adding roads
  4. Creating the river and bridge
  5. Adding the next 3 rows of cars
  6. Losing screen
  7. Winning screen
  8. Reset button (ENTER)
  9. Start button (SPACE)
  10. Adding sounds

In the end, I wound up with what I felt was a very polished and complete game. I’m really happy that I was able to build a visually pleasing game with fun and retro graphics while using a multitude of elements from p5js. From objects, to loops, to sound, my game really has a bit of everything in it. The soundtrack (which is actually the original Frogger music!) adds an element of excitement and nostalgia, and the losing and winning sound effects complete the experience for the gamer. I hope you have a great time playing!

(*Note 2: as is typically the case with p5js, the sound is sometimes a bit glitchy on Chrome and certain other browsers. However, the implementation of sound is all correct, as can be seen in my code.)

// Timothy Liu
// 15-104, Section C
// tcliu@andrew.cmu.edu
// FINAL PROJECT: Crossy Chicken, the game!

var showStart = true; // start off the game showing the start screen

var myChar; // the player (the chicken)
var charSpeed = 2; // speed player moves at

var borderW = 40; // border used in all popup screens

var firstCars = []; // arrays of cars (from bottom of screen to top)
var secondCars = [];
var thirdCars = [];
var fourthCars = [];

// road characteristics
var roadWidth = 100;
var roadLine = 2;
var carSpacing;

// river and bridge characteristics
var riverStart = 243; // identifies the top of the river
var riverWidth = 100; // identifies the width of the river
var riverEnd = riverStart + riverWidth; // identifies the bottom of the river
var bridgeWidth = 30; // width of the bridge
var bridgeMiddle = riverStart + riverWidth / 2; // the top part of the middle bridge segment
var randomBridgeStart; // where the first segment of the bridge starts 
var randomBridgeLength; // how long the middle segment of the bridge is

// variables that help draw/locate the egg and nest
var eggLocY = 50;
var eggW = 12;
var eggH = 16;
var nestLocY = 60;
var nestShadowY = 57;
var nestW = 30;
var nestH = 18;

// sound
var soundtrack;
var winSound;
var loseSound;

// preload all sounds
function preload() {

    // background track
    soundtrack = loadSound("https://courses.ideate.cmu.edu/15-104/f2019/wp-content/uploads/2019/12/froggertrack.wav");
    soundtrack.setVolume(0.5);

    // losing jingle
    loseSound = loadSound("https://courses.ideate.cmu.edu/15-104/f2019/wp-content/uploads/2019/12/lose.wav");
    loseSound.setVolume(0.6);

    // winning jingle
    winSound = loadSound("https://courses.ideate.cmu.edu/15-104/f2019/wp-content/uploads/2019/12/win.wav");
    winSound.setVolume(0.9);

}

function setup() {

    createCanvas(600, 600); // create the canvas

    soundtrack.play(); // play the frogger soundtrack that's been preloaded

    myChar = makeCharacter(); // makes the character

    carSpacing = random(30, 45); // determines spacing between cars; ensures they don't hit each other (but varies the spacing randomly)
    
    randomBridgeStart = random(50, 500); // determines the random location of the bridge
    randomBridgeLength = random(60, 250); // determines the random length of the bridge

    firstCarSetup(); // defines properties for all the different rows of cars
    secondCarSetup(); // second row of cars (2nd from bottom)
    thirdCarSetup(); // third row of cars (3rd from bottom)
    fourthCarSetup(); // top row of cars

}

function draw() {

    background(87, 168, 84); // grass floor

    noStroke();

    drawFirstRoad(); // draw in the bottom road
    drawRiver(); // draw in the river
    drawSecondRoad(); // draw in the top road

    drawNest(); // draw in the nest

    // draw in and move the character!
    myChar.draw();
    myChar.move();

    // draws in the cars on the road using drawCars()
    showFirstCars();
    showSecondCars();
    showThirdCars();
    showFourthCars();

    characterWins(); // function that runs when the character reaches the egg and wins
    characterLoses(); // function that identifies when the character loses

    startScreen(); // starting screen shows right at the beginning

}

// the starting screen that shows rules and game info
function startScreen() {

    // right at start of game, this statement is true UNTIL SPACE IS PRESSED
    if (showStart === true) {

        noLoop();

        // the frame/popup screen
        fill(235, 131, 131);
        rect(width / 6, height / 6, width * 2 / 3, height * 2 / 3);

        fill(255);
        rect(width / 6 + borderW / 2, height / 6 + borderW / 2, width * 2 / 3 - borderW, height * 2 / 3 - borderW);

        // Start screen
        fill(0);
        textSize(20);

        textAlign(CENTER); // center the text
        textFont('Avenir'); // text font

        // starting screen text: the intro!
        textStyle(NORMAL);
        text("Welcome to Crossy Chicken!", width / 2, 225);
        text("Watch out for cars, and", width / 2, 255);
        text("don't let your feet get wet!", width / 2, 285);

        text("Use the WASD keys to move, and", width / 2, 345); 
        textStyle(BOLD);
        text("press SPACE to get started!", width / 2, 375);

    }

}

// triggers to start and reset the game
function keyPressed() {

    if (keyCode === 32) { // if SPACEBAR is pressed, let the game begin!
        showStart = false;
        loop();
    }
    if (keyCode === ENTER) { // if ENTER is pressed, the level restarts!
        restart(); // restart function resets the stage
    }

    return false; // prevents the webpage from moving when arrows are pressed

}

// this function initializes all variables and then reruns the setup function, effectively restarting the game
function restart() {

    firstCars = []; // initialize all of the car arrays so they can be redrawn/setup
    secondCars = [];
    thirdCars = [];
    fourthCars = [];

    setup(); // re-run setup, which resets the stage
    loop(); // start running the code again!

}

// define all character properties (the chicken)
function makeCharacter() {

    return {
        x: 300,
        y: 550,
        c: 255,
        w: 25,
        h: 40,
        r: 25,
        hX: 300,
        hY: 490,
        hW: 15,
        hH: 20,
        eyeC: 0,
        eyeW: 2,
        beakC: color(252, 202, 3),
        crownW: 5,
        crownH: 10,
        crownC: color(207, 58, 58),
        footW: 7,
        footH: 4,
        draw: drawCharacter,
        move: moveCharacter
    }

}

// uses the obj characteristics and var myChar to draw the chicken
function drawCharacter() {

    var headOffset = 18;
    var eyeOffsetX = 3;
    var eyeOffsetY = 21;
    var beakOffsetX = 4;
    var beakOffsetY = 19;
    var beakOffsetY2 = 15;
    var crownOffsetY = 27;
    var footOffset = 5;

    // red crown on head
    fill(myChar.crownC);
    arc(myChar.x, myChar.y - crownOffsetY, myChar.crownW, myChar.crownH, PI, TWO_PI);

    // feet
    fill(myChar.beakC);
    arc(myChar.x - footOffset, myChar.y, myChar.footW, myChar.footH, 0, PI);   
    arc(myChar.x + footOffset, myChar.y, myChar.footW, myChar.footH, 0, PI);  

    // head and body   
    fill(myChar.c);
    arc(myChar.x, myChar.y, myChar.w, myChar.h, PI, TWO_PI);
    arc(myChar.x, myChar.y - headOffset, myChar.hW, myChar.hH, PI, TWO_PI);

    // eyes
    fill(myChar.eyeC);
    ellipse(myChar.x - eyeOffsetX, myChar.y - eyeOffsetY, myChar.eyeW, myChar.eyeW);
    ellipse(myChar.x + eyeOffsetX, myChar.y - eyeOffsetY, myChar.eyeW, myChar.eyeW);

    // beak
    fill(myChar.beakC);
    triangle(myChar.x - beakOffsetX, myChar.y - beakOffsetY, myChar.x + beakOffsetX, myChar.y - beakOffsetY,
            myChar.x, myChar.y - beakOffsetY2);

}

// this function allows the character to move
function moveCharacter() {

    if (keyIsDown(65)) { // if pressing the A key
        myChar.x -= charSpeed; // move the character's x position left
        if (myChar.x + myChar.r < 0) { // if the character moves off the screen, have it wrap to the other side
            myChar.x = width + myChar.r;
        }
    }

    if (keyIsDown(68)) { // if pressing the D key 
        myChar.x += charSpeed; // move the character's x position right
        if (myChar.x - myChar.r > width) { // if the character moves off the screen, have it wrap to the other side
            myChar.x = -myChar.r;
        }
    }

    if (keyIsDown(87)) { // if pressing the W key
        myChar.y -= charSpeed; // move the character's y position up
    }

    if (keyIsDown(83)) { // if pressing the S key
        myChar.y += charSpeed; // move the character's y position down
        if (myChar.y + myChar.r > height) { // if it hits the bottom, don't let it go off the page
            myChar.y = height - myChar.r;
        }
    }

}

// what happens if the character reaches the egg
function characterWins() {

    // if the character reaches the egg successfully
    if (myChar.y > eggLocY - eggH / 2 & myChar.y < nestLocY + nestH / 2) {
        if (myChar.x > width / 2 - nestW / 2 && myChar.x < width / 2 + nestW / 2) {
            noLoop(); // game stops
            winningScreen(); // display winning screen
            winSound.play(); // play winning jingle!
        }
    }

}

// this function determines if the character loses using two other functions: hit by car, or fall in river
function characterLoses() {

    characterHitByCar(); // function if myChar is hit by a car
    characterFallsInRiver(); // function if myChar falls into the river

}

// if the character is hit by a car
function characterHitByCar() {

    // if char is hit by first row of cars
    for (var i = 0; i < firstCars.length; i++) {

        var carMiddleX = firstCars[i].x + (firstCars[i].w / 2);
        var carMiddleY = firstCars[i].y + (firstCars[i].h / 2);

        var charMiddleX = myChar.x;
        var charMiddleY = myChar.y - (myChar.h / 2);

        // if the edges of the character fall within the edges of the car, you lose!
        if (charMiddleX - myChar.w / 4 < carMiddleX + firstCars[i].w / 2 & charMiddleX + myChar.w / 4 > carMiddleX - firstCars[i].w / 2
            && myChar.y > carMiddleY - firstCars[i].h / 2 && charMiddleY - myChar.h / 5 < carMiddleY + firstCars[i].h / 2) {
            noLoop(); // game stops
            losingScreenCar(); // show losing screen
            loseSound.play(); // losing sound plays
        }

    }

    // if char is hit by second row of cars
    for (var j = 0; j < secondCars.length; j++) {

        var carMiddleX = secondCars[j].x + (secondCars[j].w / 2);
        var carMiddleY = secondCars[j].y + (secondCars[j].h / 2);

        var charMiddleX = myChar.x;
        var charMiddleY = myChar.y - (myChar.h / 2);

        // if the edges of the character fall within the edges of the car, you lose!
        if (charMiddleX - myChar.w / 4 < carMiddleX + secondCars[j].w / 2 & charMiddleX + myChar.w / 4 > carMiddleX - secondCars[j].w / 2
            && myChar.y > carMiddleY - secondCars[j].h / 2 && charMiddleY - myChar.h / 5 < carMiddleY + secondCars[j].h / 2) {
            noLoop(); // game stops
            losingScreenCar(); // show losing screen
            loseSound.play(); // losing sound plays
        }

    }

    // if char is hit by third row of cars
    for (var k = 0; k < thirdCars.length; k++) {

        var carMiddleX = thirdCars[k].x + (thirdCars[k].w / 2);
        var carMiddleY = thirdCars[k].y + (thirdCars[k].h / 2);

        var charMiddleX = myChar.x;
        var charMiddleY = myChar.y - (myChar.h / 2);

        // if the edges of the character fall within the edges of the car, you lose!
        if (charMiddleX - myChar.w / 4 < carMiddleX + thirdCars[k].w / 2 & charMiddleX + myChar.w / 4 > carMiddleX - thirdCars[k].w / 2
            && myChar.y > carMiddleY - thirdCars[k].h / 2 && charMiddleY - myChar.h / 5 < carMiddleY + thirdCars[k].h / 2) {
            noLoop(); // game stops
            losingScreenCar(); // show losing screen
            loseSound.play(); // losing sound plays
        }

    }

    // if char is hit by fourth row of cars
    for (var l = 0; l < fourthCars.length; l++) {

        var carMiddleX = fourthCars[l].x + (fourthCars[l].w / 2);
        var carMiddleY = fourthCars[l].y + (fourthCars[l].h / 2);

        var charMiddleX = myChar.x;
        var charMiddleY = myChar.y - (myChar.h / 2);

        // if the edges of the character fall within the edges of the car, you lose!
        if (charMiddleX - myChar.w / 4 < carMiddleX + fourthCars[l].w / 2 & charMiddleX + myChar.w / 4 > carMiddleX - fourthCars[l].w / 2
            && myChar.y > carMiddleY - fourthCars[l].h / 2 && charMiddleY - myChar.h / 5 < carMiddleY + fourthCars[l].h / 2) {
            noLoop(); // game stops
            losingScreenCar(); // show losing screen (car version)
            loseSound.play(); // losing sound plays
        }
    }

}

// if the strays from the bridge and falls into the river
function characterFallsInRiver() {

    // if the character is crossing the river...
    if (myChar.y > riverStart & myChar.y < riverEnd) {

        // first segment of the bridge (vertical)
        if (myChar.y > bridgeMiddle + bridgeWidth && myChar.y < riverEnd) {

            // if char is outside the bridge boundaries
            if (myChar.x < randomBridgeStart || myChar.x > randomBridgeStart + bridgeWidth) {
                noLoop(); // game stops
                losingScreenRiver(); // show losing screen (splash version)
                loseSound.play(); // play losing sound
            }
        }
        
        // the bridge randomly goes left or right based on starting position...
        // if the bridge goes rightward:
        if (randomBridgeStart < width / 2) {

            // second segment of the bridge (horizontal)
            if (myChar.y > bridgeMiddle & myChar.y < bridgeMiddle + bridgeWidth) {
                // if char is outside the boundaries of the horizontal bridge segment
                if (myChar.x < randomBridgeStart || myChar.x > randomBridgeStart + randomBridgeLength + bridgeWidth) {
                    noLoop(); // game stops
                    losingScreenRiver(); // show losing screen (splash version)
                    loseSound.play(); // play losing sound
                }
            }
        
            // last segment of the bridge (vertical)
            if (myChar.y > riverStart & myChar.y < bridgeMiddle) {
                if (myChar.x < randomBridgeStart + randomBridgeLength || myChar.x > randomBridgeStart + randomBridgeLength + bridgeWidth) {
                    noLoop(); // game stops
                    losingScreenRiver(); // show losing screen (splash version)
                    loseSound.play(); // play losing sound
                }
            }

        // if the bridge goes leftward:
        } else {

            // second segment of the bridge (horizontal)
            if (myChar.y > bridgeMiddle & myChar.y < bridgeMiddle + bridgeWidth) {
                if (myChar.x < randomBridgeStart - randomBridgeLength || myChar.x > randomBridgeStart + bridgeWidth) {
                    noLoop(); // game stops
                    losingScreenRiver(); // show losing screen (splash version)
                    loseSound.play(); // play losing sound
                }
            }

            // last segment of the bridge (vertical)
            if (myChar.y > riverStart & myChar.y < bridgeMiddle) {
                if (myChar.x < randomBridgeStart - randomBridgeLength || myChar.x > randomBridgeStart - randomBridgeLength + bridgeWidth) {
                    noLoop(); // game stops
                    losingScreenRiver(); // show losing screen (splash version)
                    loseSound.play(); // play losing sound
                }
            }
        }
    }
}

// the popup screen when you win
function winningScreen() {

	soundtrack.stop(); // stop background sound

    // border + screen
    fill(255, 213, 0);
    rect(width / 4, height / 4, width / 2, height / 2);

    fill(255);
    rect(width / 4 + borderW / 2, height / 4 + borderW / 2, width / 2 - borderW, height / 2 - borderW);

    // winning screen text for when you make it to the egg
    fill(0);
    textSize(20);
    textAlign(CENTER); // center the text
    textFont('Avenir'); // text font

    // text:
    textStyle(NORMAL);
    text("You protected your egg;", width / 2, 270);
    textStyle(BOLD);
    text("Congratulations!", width / 2, 300);
    textStyle(NORMAL);
    text("Hit ENTER to play again!", width / 2, 330);

}

// the popup screen when you're hit by a car
function losingScreenCar() {

    soundtrack.stop();

    fill(235, 131, 131);
    rect(width / 4, height / 4, width / 2, height / 2);

    fill(255);
    rect(width / 4 + borderW / 2, height / 4 + borderW / 2, width / 2 - borderW, height / 2 - borderW);

    // losing screen text for when a car runs into you
    fill(0);
    textSize(20);
    textAlign(CENTER); // center the text
    textFont('Avenir'); // text font

    // text:
    textStyle(NORMAL);
    text("Oh no, a car got you!", width / 2, 285);
    text("Hit ENTER to try again!", width / 2, 315);

}

// the popup screen when you fall into the river
function losingScreenRiver() {

    soundtrack.stop();

    fill(53, 99, 240);
    rect(width / 4, height / 4, width / 2, height / 2);

    fill(255);
    rect(width / 4 + borderW / 2, height / 4 + borderW / 2, width / 2 - borderW, height / 2 - borderW);

    // losing screen text for when you fall into the water
    fill(0);
    textSize(20);

    textAlign(CENTER); // center the text
    textFont('Avenir'); // text font

    // text:
    textStyle(BOLD); // bold the sound effect (splash)
    text("Splash...", width / 2, 270);
    textStyle(NORMAL);
    text("You fell into the water!", width / 2, 300);
    text("Hit ENTER to try again!", width / 2, 330);

}

// draw in the bottom (first) road
function drawFirstRoad() {

    var roadLocX = 0;
    var roadLocY = 385;
    var lineLocY = 434;

    fill(180);
    rect(roadLocX, roadLocY, width, roadWidth); // draws the road

    fill(245, 224, 66);
    rect(roadLocX, lineLocY, width, roadLine); // the dividing yellow line

}

// draw in the top (second) road
function drawSecondRoad() {

    var roadLocX = 0;
    var roadLocY = 100;
    var lineLocY = 149;

    fill(180);
    rect(roadLocX, roadLocY, width, roadWidth); // draws the road

    fill(245, 224, 66);
    rect(roadLocX, lineLocY, width, roadLine); // the dividing yellow line

}

// draw in the river
function drawRiver() {

    var riverEdgeL = 0;

	fill(100, 100, 255); // blue color
    rect(riverEdgeL, riverStart, width, riverWidth);

    drawBridge(); // draw in bridge using bridge function

}

// draws the bridge the crosses the river. the bridge is randomly generated each time the game is played!
// it will go rightward if the starting point is on the left of the screen, and leftward if the starting point is to the right
function drawBridge() {

    fill(186, 145, 104); // brown bridge color

    rect(randomBridgeStart, bridgeMiddle, bridgeWidth, riverWidth / 2); // first bridge segment
    
    // this if-else statement determines which direction the bridge goes in
    if (randomBridgeStart < width / 2) {
        rect(randomBridgeStart, bridgeMiddle, randomBridgeLength, bridgeWidth); // second bridge segment
        rect(randomBridgeStart + randomBridgeLength, riverStart, bridgeWidth, riverWidth / 2 + bridgeWidth); // final bridge segment
    } else {
        rect(randomBridgeStart - randomBridgeLength, bridgeMiddle, randomBridgeLength, bridgeWidth); // second bridge segment
        rect(randomBridgeStart - randomBridgeLength, riverStart, bridgeWidth, riverWidth / 2 + bridgeWidth); // final bridge segment
    }

}

// characteristics of the car objects
function makeCars(px, py) {
    return {
        x: px,
        y: py,
        c: color(random(255), random(255), random(255)),
        w: 50,
        h: 30,
        lightsW: 2,
        lightsH: 5,
        lightsC: color(242, 235, 19),
        tireW: 9,
        tireH: 3,
        tireC: color(0, 0, 0),
        windshieldW: 4,
        windshieldH: 25,
        windshieldC: color(100, 100, 100),
        draw: drawCars
    }
}

// uses the obj characteristics to draw cars
function drawCars() {

	var tireOffsetX = 6;
	var tireOffsetY = 2;
    var windshieldOffsetX = 10;
    var windshieldOffsetY = 2.5;

    // car body
    fill(this.c);
    rect(this.x, this.y, this.w, this.h);

    // car headlights (x4)
    fill(this.lightsC);
    rect(this.x + this.w - this.lightsW, this.y, this.lightsW, this.lightsH);
    rect(this.x + this.w - this.lightsW, this.y + this.h - this.lightsH, this.lightsW, this.lightsH);
    rect(this.x, this.y, this.lightsW, this.lightsH);
    rect(this.x, this.y + this.h - this.lightsH, this.lightsW, this.lightsH);

    // car tires
    fill(this.tireC);
    rect(this.x + this.w - (2.5 * tireOffsetX), this.y - tireOffsetY, this.tireW, this.tireH);
    rect(this.x + tireOffsetX, this.y - tireOffsetY, this.tireW, this.tireH);
    rect(this.x + this.w - (2.5 * tireOffsetX), this.y + this.h - (tireOffsetY / 2), this.tireW, this.tireH);
    rect(this.x + tireOffsetX, this.y + this.h - (tireOffsetY / 2), this.tireW, this.tireH);

    // car windshields
    fill(this.windshieldC);
    rect(this.x + (3.5 * windshieldOffsetX), this.y + windshieldOffsetY, this.windshieldW, this.windshieldH);
    rect(this.x + (1.2 * windshieldOffsetX), this.y + windshieldOffsetY, this.windshieldW, this.windshieldH);

    if (this.x > width) {
        this.x = -this.w;
    }

    if (this.x + this.w < 0) {
        this.x = width;
    }
}

// draws the nest using nest variables
function drawNest() {

    // the nest
    fill(219, 181, 75);
    ellipse(width / 2, nestLocY, nestW * 1.3, nestH);

    fill(163, 129, 34);
    ellipse(width / 2, nestShadowY, nestW, nestH / 2);
 
    // the egg
    fill(250, 242, 220);
    ellipse(width / 2, eggLocY, eggW, eggH);

}

// setting up the first row of cars and placing them (very bottom row of cars)
function firstCarSetup() { 

    var carPlacement; // car placement variable
    var numCars = 5; // number of cars in the first row of cars
    var carLocY = 445;

    var newCar = makeCars();
    firstCars.push(newCar); // add each new car to the empty array firstCars

    // place each car in the first row of cars and add to the array firstCars
    for (var i = 0; i < numCars; i++) {
        carPlacement = random((i * width / numCars) + carSpacing, ((i + 1) * width / numCars) - carSpacing);
        firstCars[i] = makeCars(carPlacement, carLocY); // placement of the first row of cars
    }

}

// setting up the second row of cars and placing them
function secondCarSetup() {

    var carPlacement; // car placement variable
    var numCars = 5; // number of cars in the first row of cars
    var carLocY = 395;

    var newCar = makeCars();
    secondCars.push(newCar); // add each new car to the empty array secondCars

    // place each car in the second row of cars and add to the array secondCars
    for (var i = 0; i < numCars; i++) {
        carPlacement = random((i * width / numCars) + carSpacing, ((i + 1) * width / numCars) - carSpacing);
        secondCars[i] = makeCars(carPlacement, carLocY);
    }

}

// setting up the third row of cars and placing them
function thirdCarSetup() {

    var carPlacement; // car placement variable
    var numCars = 5; // number of cars in the first row of cars
    var carLocY = 160;

    var newCar = makeCars();
    thirdCars.push(newCar); // add each new car to the empty array secondCars

    // place each car in the third row of cars and add to the array secondCars
    for (var i = 0; i < numCars; i++) {
        carPlacement = random((i * width / numCars) + carSpacing, ((i + 1) * width / numCars) - carSpacing);
        thirdCars[i] = makeCars(carPlacement, carLocY);
    }

}

// setting up the fourth row of cars and placing them
function fourthCarSetup() {

    var carPlacement; // car placement variable
    var numCars = 5; // number of cars in the first row of cars
    var carLocY = 110;

    var newCar = makeCars();
    fourthCars.push(newCar); // add each new car to the empty array secondCars

    // place each car in the fourth row of cars and add to the array secondCars
    for (var i = 0; i < numCars; i++) {
        carPlacement = random((i * width / numCars) + carSpacing, ((i + 1) * width / numCars) - carSpacing);
        fourthCars[i] = makeCars(carPlacement, carLocY);
    }

}

// draws the first row of cars (closest to the bottom of the screen)
function showFirstCars() {

    for (var i = 0; i < firstCars.length; i++) {
        firstCars[i].draw(); // draws in cars
        firstCars[i].x += 1; // makes the cars drive rightward on the first road (bottom)
    }

}

// draws the second row of cars (next row up from bottom of the screen)
function showSecondCars() {

    for (var i = 0; i < secondCars.length; i++) {
        secondCars[i].draw();
        secondCars[i].x -= 1; // makes the cars move in the opposite direction (leftward) on the first road
    }

}

// draws the third row of cars (next row up from bottom of the screen)
function showThirdCars() {

    for (var i = 0; i < thirdCars.length; i++) {
        thirdCars[i].draw();
        thirdCars[i].x += 1.8; // faster cars to increase the difficulty (rightward, top road)!
    }

}

// draws the fourth row of cars (row of cars near top of screen)
function showFourthCars() {

    for (var i = 0; i < fourthCars.length; i++) {
        fourthCars[i].draw();
        fourthCars[i].x -= 1.8; // faster cars in opposite direction to increase the difficulty (leftward, top road)!
    }

}

SooA Kim – Final Project


sketch

Mouse click the screen!

For this final project, I created a variety of sprite textures to apply into a particle effect animation that replaces the green pixels in the webcam screen. The sprite texture changes when the mouse is clicked.  I approached this project with the basis of environmental concern. I wanted to convey a message on what happens with climate change with the imagery of air pollution covered by these sprite texture that imitates dusts and smokes.
I tried to be be playful in terms of this project as well by providing different textures. I started this code by tracking the green pixels first, then I moved onto figuring out how to replace the image texture to the particle effect.

The coding process was very challenging, but I am really happy with the outcome.

sprite textures

first try on getting the sprite texture to the image

/* SooA Kim
sookim@andrew.cmu.edu
Final Project
Section C
*/


//webcam and image texture variables 
var myCaptureDevice;
var img = [];
var frames = [];
var framelist = 0;
var link1= [];
var link2 = [];
var link3 = [];

//particle variables
var gravity = 0.1;   // downward acceleration
var springy = 0.9; // how much velocity is retained after bounce
var drag = 0.0001;    // drag causes particles to slow down
var np = 50;
var particles = [];

var Image1 = [];
var Image2 = [];
var Image3 = [];


var link1 = [
    "https://i.imgur.com/qAayMED.png",
    "https://i.imgur.com/86biLWe.png",
    "https://i.imgur.com/r5UE5kg.png",
    "https://i.imgur.com/I1BBRfH.png",
    "https://i.imgur.com/D1l3aXi.png",
    "https://i.imgur.com/NAHr7yB.png",
    "https://i.imgur.com/F0SB9GM.png",
    "https://i.imgur.com/B1kvNaM.png",
    "https://i.imgur.com/Th9sahL.png",
    "https://i.imgur.com/c9cuj2k.png"
  ]

var link2 = [ 
    "https://i.imgur.com/z7BAFyp.png",
    "https://i.imgur.com/8Ww7Vuo.png",
    "https://i.imgur.com/QFjlLr2.png",
    "https://i.imgur.com/0sdtErd.png",
]

var link3 = [ 
    "https://i.imgur.com/ZD7IoUP.png"
]


var textures = [link1, link2, link3];

var textureIndex = 0;

function preload() {
    for (var z = 0; z < link1.length; z++){
      link1[z] = loadImage(link1[z]);
    }
    
    for (var z = 0; z < link2.length; z++){
      link2[z] = loadImage(link2[z]);
    }
  
    for (var z = 0; z < link3.length; z++){
      link3[z] = loadImage(link3[z]);
    }
  }

function setup() {
    createCanvas(600, 480);
    myCaptureDevice = createCapture(VIDEO);
    myCaptureDevice.size(600, 480); // attempt to size the camera. 
    myCaptureDevice.hide(); // this hides an extra view.
}
  
function mousePressed(){
    textureNew = floor(random(textures.length)); //to prevent from same number(texture) that's selected again,
      while(textureNew == textureIndex){
        textureNew = floor(random(textures.length));
      } 
      textureIndex = textureNew;
    }
    
function particleStep() {
    this.age++;
    this.x += this.dx;
    this.y += this.dy;
    this.dy = this.dy + gravity; // force of gravity
    var vs = Math.pow(this.dx, 2) + Math.pow(this.dy, 2);
    // d is the ratio of old velocty to new velocity
    var d = vs * drag; 
    d = min(d, 1);
    // scale dx and dy to include drag effect
    this.dx *= (1 - d);
    this.dy *= (1 - d);
}
 
function makeParticle(px, py, pdx, pdy, imglink) {
    p = {x: px, y: py,
       dx: pdx, dy: pdy,
       age: 0,
       step: particleStep,
       draw: particleDraw,
       image: imglink  //making image as a parameter 
      }
  return p;
}

function particleDraw() {
    //tint(255, map(this.age, 0, 30, 200, 0)); //fade out image particles at the end
    image(this.image, this.x, this.y);
}

function isColor(c) {
    return (c instanceof Array);
  } 
  
function draw() {
    tint(255, 255);
    background(220);
    imageMode(CORNER);
    myCaptureDevice.loadPixels(); // this must be done on each frame.
    image(myCaptureDevice, 0, 0);
    framelist += 1;
    imageMode(CENTER);
    // get the color value of every 20 pixels of webcam output
    for (var i = 0; i < width; i += 20) {
      for (var j = 0; j < height; j+= 20) {
          var currentColor = myCaptureDevice.get(i, j);

          var rd = red(currentColor);
          var gr = green(currentColor);
          var bl = blue(currentColor);
           // targetColor: green 
           if (rd < 90 & gr > 100 && bl < 80){
             for (var s = 0; s < textures[textureIndex].length; s++) { 
                var pImage = makeParticle(i, j, random(-10, 10), random (-10, 10),textures[textureIndex][s]); //replaced position with i and j that tracks green pixels
                if (particles.length < 10000) {
                  particles.push(pImage);
                }
              }
            }
          }
        }
        newParticles = [];
        for (var i = 0; i < particles.length; i++) { // for each particle
          var p = particles[i];
          p.step();
          p.draw();
          if (p.age < 20) { //to last good amount of particle (due to laggy actions)
            newParticles.push(p);
          }
        }
        particles = newParticles;
      }

// moving frames 
function framePush(){
    framelist += 1;
    image(frames[framelist % frames.length], 0, 0);
  }

Shariq M. Shah | Paul Greenway – Final Project


This project functions as a Climate Change Visualizer, where the user is able to understand the extents of their environmental impact in a game like exaggeration of excessive consumption. The user’s actions to increase their ecological footprint, are translated to an increased rate of change in the number of tonnes of CO2 emitted. Each aspect of the Anthropocene (transport, fossil fuels, and agriculture) are represented by icons and are associated with facts that are cycled through in an array. At the end of the clicking cycle, there is a sarcastic game-like “YOU WIN!” message that points to the absurdity of excessive human consumption that ultimately leads to the degradation of the planet. The hope is that this climate change visualizer helps people to understand the gravity and severity of their impact relative to climate change.  Part of the user’s involvement makes it necessary to refresh the page in order to restart the game. The squares that change in size and movement based on mouse location are indicative of icecaps that get smaller in size as “ecological footprint” increases. 

shariqs-pgreenwa

// Shariq Shah | Paul Greenway
// shariqs | pgreenwa
// shariqs@andrew.cmu.edu | pgreenwa@andrew.cmu.edu

// Project-12-Final Project
// Climate Change Visualizer

//Variables


//Ice movement intensity variable
let movementIntensity = 0;


// Environmental impacts multipliers
var impactSize = [6,9,12,400];

//Step Variable
var n = 0;

//Starting C02 Value
var co2 = 2332947388070;

//Images


//Impact Icon Links
var impactImageLinks = [
"https://i.imgur.com/BQBpdiv.png", "https://i.imgur.com/De2zFwf.png", "https://i.imgur.com/a0ab0DC.png", 'https://i.imgur.com/SRVUOkn.png'
  ]
//Impact Facts
var impactFacts = [ "","                               Livestock contributes nearly 2/3 of agriculture's greenhouse & 78% of its methane emissions", "                       In total, the US transportation sector produces nearly thirty percent of all US global warming emissions", "The consumption of fossil fuel by the United States has been 80%. Fossil fuels are the most dangerous contributors to global warming"
  ]
//Impact Tags
var impactNames = [
  "agriculture", "transportation", "fossil        fuels", "you win"];

var impactImages;

//Images Preload

function preload() {
    impactImages =         loadImage(impactImageLinks[n]);
    currentImpactImageIndex = 0;
}

//Setup, Load in Images
function setup() {
  createCanvas(600, 600);
 
  currentImpactImage = impactImages;
  currentImpactImage.loadPixels();
 
  strokeWeight(0.25);
  stroke(dist(width/2, height/2, mouseX, mouseY), 10, 200);
  noFill();
}
 
 
function draw() {
 
  //Background color + opacity setup  
  background(255,0,0,10);
  tint(500, 20);

  //Dynamic ice grid setup 
  for (let x = 0; x <= width; x = x + 50) {
    for (let y = 0; y <= height; y = y + 50) {

      const xAngle = map(45, 0, width, -4 *         PI* 3, 4 * PI * 3, true);
      const yAngle = map(45, 0, height, -4 *         PI, 4 * PI, true);

      const angle = xAngle * (x / width) +           yAngle * (y / height);


      const myX = x + 20 * cos(2 * PI/4 *           movementIntensity + angle/10);
      const myY = y + 20 * sin(2 * PI/2 *           movementIntensity + angle/10);     
      stroke(0,255,255,200);
      strokeWeight(n);
      fill('white');
      rectMode(CENTER);
     
      rect(myX, myY, dist(myX, myY, mouseX,         mouseY)/impactSize[n],dist(myX, myY,           mouseX, mouseY)/impactSize[n]); // draw       particle5
    }
  }
 
  //Impact Icons Config + draw
  imageMode(CENTER);
  currentImpactImage.resize(100, 100);
  image(currentImpactImage, mouseX, mouseY);
 

  
  textFont('Helvetica');
  
  noStroke();
  
  fill('white');
  
  rect (width/2, 500, width, 60);
  
  fill('black');
  
  textSize(25);
  
  //C02 Emmited updating Text

  text('TONNES OF CO2 EMITTED : ' + co2, 40, 509);
  fill(255);
  //C02 emmision text backdrop

  rect(width/2, 100, width, 40);
  fill(0);
  textSize(10);
  text(impactFacts[n], width/2 - 295, 105);
  
  //C02 Count Multiplier
  co2 = co2 + 10 * n * 20;
  
  textSize(32);
    
  
  //game over text
  if (n == 4) {
    fill(0);
    text('YOU WIN! ICE CAPS MELTED.', mouseX - 200,           mouseY + 100);
  
    textFont('Helvetica');
    }
  
    else {
    
    textSize(6 * n);
    text('PRESS TO INCREASE YOUR ECOLOGICAL FOOTPRINT!',       mouseX - 120 * n, mouseY + 100);
  
  }
  
  //Ice Movement Intensity Multiplier
  movementIntensity = movementIntensity + 0.01 +n/150;

}

//Mouse Click functions

function mousePressed(){
   
    //change impact icons on click
    impactImages = loadImage(impactImageLinks[n]);
    currentImpactImage = impactImages;

    currentImpactImage.loadPixels();
    //increase step on click
    n = n + 1;
   
    if (n > 4) {
        n = 0;
    }

}

Nawon Choi— Final Project

INSTRUCTIONS: Blocks will begin to appear on the screen and approach you! When blocks turn yellow, type the letter displayed on the block to earn a point. You have 5 lives.

sketch

// Nawon Choi
// Section C
// nawonc@andrew.cmu.edu
// Final Project

var numOfStars = 700;
var starSize = 1.2;
var sx = [];
var sy = [];
var sz = [];

var newBlockChance = 100;
var blocks = [];
var blockSize = 50;
var speed = 5;
var lastBlk = 0;
var alphabet = ["a", "b", "c", "d", "e", "f", "g",
                 "h", "i", "j", "k", "l", "m", "n", 
                 "o", "p", "q", "r", "s", "t", "u", 
                 "v", "w", "x", "y", "z"];
var txt;
var ripeBlocks = [];

var level = 1;
var points = 0;
var showPoints;
var lives = 5;
var heart;
var gameOver;
var resetTxt;
var yourScore;
var lvl;

function preload() {
    heart = loadImage("https://i.imgur.com/YvJC0vj.png");
}

function setup() {
    createCanvas(480, 500, WEBGL);
        
    // generate a bunch of randomly placed stars
    for (var i = 0; i < numOfStars; i++) {
        sx.push(random(-width, width));
        sy.push(random(-height, height));
        sz.push(random(-height, height));
    }

    // used for drawing letter on block
    txt = createGraphics(200, 200);
    txt.textSize(75);

    // show points/level at the corner
    showPoints = createGraphics(width, 100);
    showPoints.textSize(60);
    showPoints.fill(255);
    showPoints.textStyle(BOLD);
    lvl = createGraphics(width, 100);
    lvl.textSize(50);
    lvl.fill(255);

    // game over screen text
    gameOver = createGraphics(width + 150, 100);
    gameOver.textSize(100);
    gameOver.fill(255);
    gameOver.textStyle(BOLD);
    resetTxt = createGraphics(width + 150, 100);
    resetTxt.textSize(40);
    resetTxt.fill(255);
    yourScore = createGraphics(width + 150, 100);
    yourScore.textSize(40);
    yourScore.fill(255);


}

function draw() {
    background("#1c2736");
    drawStars();

    if (lives > 0) {
        // draw hearts to represent lives
        for (var i = 0; i < lives; i++) {
            push();
            translate(width / 2 - (30 * (i + 1)), -height / 2 + 25);
            texture(heart);
            plane(30, 30);
            pop();
        }

        // show points/level at the top corner
        push();
        showPoints.background("#1c2736");
        showPoints.text("SCORE = " + points, 10, 60);
        texture(showPoints);
        translate(-width / 2 + 85, -height / 2 + 25);
        plane(150, 30);

        lvl.background("#1c2736");
        lvl.text("LEVEL = " + level, 10, 60);
        texture(lvl);
        translate(0, 25);
        plane(150, 30);
        pop();


        // long road
        push();
        fill("#104670");
        translate(0, 30, -600);
        rotateX(blockSize);
        box(width, 10, 8000);
        pop();

        // highlight zone
        push();
        fill("#497291");
        rotateX(blockSize);
        translate(0, 150);
        box(400, 10, 330); 
        pop();

        // increase level based on points
        var determineLvl = floor(points / 10) + 1;
        if (determineLvl > 3) {
            // only up to 3 levels
            level = 3;
        } else {
            level = determineLvl;
        }

        if (level < 3) {
            // make sure blocks don't overlap
            if (frameCount > (lastBlk + 30)) {
                var newBlock = floor(random(newBlockChance));
                if (newBlock == 1) {
                    lastBlk = frameCount;
                    blocks.push(makeNewBlock());
                }
            }
        } else {
            // blocks can overlap
            var newBlock = floor(random(newBlockChance));
            if (newBlock == 1) {
                lastBlk = frameCount;
                blocks.push(makeNewBlock());
            }
        }

        // draw blocks
        for (var i = 0; i < blocks.length; i++) {
            if (blocks[i].z < 330) {
                blocks[i].draw();
                if (blocks[i].ripe()) {
                    ripeBlocks.push(blocks[i]);
                }
            } else {
                // remove blocks that have gone off the page
                if (blocks[i].scored == 0) {
                    lives--;
                }
                blocks.shift();
            }
        }
    } else {
        // game over screen
        push();
        translate(0, -25);
        gameOver.text("GAME OVER", 0, 80);
        texture(gameOver);
        plane(300, 50);

        resetTxt.text("Press 'R' to restart", 0, 100);
        translate(70, 40);
        texture(resetTxt);
        plane(300, 50);

        yourScore.text("Your score = " + points, 0, 100);
        translate(0, 50);
        texture(yourScore);
        plane(300, 50);
        pop();
    }
}

function resetGame() {
    lives = 5;
    points = 0;
    level = 1;
    blocks = [];
    newBlockChance = 100;
}

function levelUp () {
    // increase difficulty
    newBlockChance -= 5;
}

function blockIsScored() {
    this.scored = 1;
    ripeBlocks.shift();
}

function blockIsRipe() {
    // if block is on the highlighted zone
    if (this.z == 70) {
        return true;
    }
    return false;
}

function drawBlock() {
    push();
    // block changes color based on nearness/if scored
    if (this.scored == 0) {
        if (this.z >= 70) {
            txt.background("yellow");
        } else {
            txt.background("red");
        }
    } else {
        txt.background("green");
    }
        
    // displays letter on the block
    var upper = this.letter.toUpperCase();
    txt.text(upper, 80, 120);
    stroke("#104670");
    texture(txt);
    rotateX(blockSize);
    translate(this.x, this.y, this.z);
    box(blockSize);
    pop();

    // move forward
    this.z += speed;
}

function makeNewBlock() {
    var block = {
        x: floor(random(-100, 100)),
        y: floor(random(-150, 25)),
        z: -2000,
        letter: alphabet[floor(random(26))],
        ripe: blockIsRipe,
        score: blockIsScored,
        scored: 0,
        draw: drawBlock
    }
    return block;
}

function drawStars() {
    fill(200);
    noStroke();
    for (var i = 0; i < numOfStars; i++) {
        push();
        translate(sx[i], sy[i], sz[i]);
        sphere(starSize);
        pop();
    }
}

function keyTyped() {
    if (lives == 0) {
        // reset game
        if (key == "r") {
            resetGame();
        }
    } else {
        // check if a block was pressed correctly/timely
        for (var i = 0; i < ripeBlocks.length; i++) {
            if (key == ripeBlocks[i].letter) {
                points++;
                ripeBlocks[i].score();
                if (points % 10 == 0) {
                    levelUp();
                }   
            }
        }
    }   
}

For my final project I created a 3D typing game. I tried to recreate Blade Saber, a similar VR game where players slash through the blocks using a VR “saber”. For a long time, I tried to have the player score points by dragging their mouse through the block, but due to complexities of 3D in p5js (translations, rotations, etc) it was way out-of-scope for this project. I decided to simplify this to be a keyboard-based game instead. Enjoy!

Nawon Choi— Project Proposal

For my project, I want to create a game inspired by a VR game called Blade Saber. This game will use p5.js’s WEBGL library and have 3D blocks that can be slashed using the player’s mouse.

Blocks will start at a far distance from the player and slowly get closer and closer. The player must slash the blocks before they reach the player. As the game advances, more blocks will come at an increasingly faster rate. Moreover, each block will have an arrow indicating which direction the player must “slash” in order to get the point. If they do not slash in the right direction, they will lose a life.

Joanne Chui – Final Project

TILES

sketch

// Joanne Chui
// Section C
// Final Project

var gridLength = 400;

var myLBlue = 0
var myBlue = 0
var myOrange = 0

var tileCount = 0; //amount of columns and rows
var crtPattern = 0; //amount of tiles highlighted
var counter = 0;
var roundNum = 1;

let ready = false;
let wrongAnswer = false;

myGrid = [];
userGrid = [];

function setup(){
  createCanvas(550, 405);
  background(80, 20, 10);
  textSize(32);
  fill('white');
  text("T I L E S", 415, 40);
  textSize(10);
  text("MEMORIZE THE PATTERN", 415, 70);
  text("PRESS R WHEN READY", 420, 90);
  text("CLICK TO CHANGE TILE", 418, 110);
  textSize(20);
  text("ROUND", 440, 180);

  tileCount = int(random(3, 6));
  crtPattern = int(.4 * (tileCount * tileCount));

  myLBlue = color(25, 120, 145);
  myBlue = color(15, 60, 100);
  myOrange = color(200, 80, 50);

  stroke(90, 100, 90);
  for(i = 0; i < tileCount; i++){
    myGrid.push([]);
    userGrid.push([]);
    for(j = 0; j < tileCount; j++){
      myGrid[i].push(myBlue);
      userGrid[i].push(myBlue);
    }
  }
}

function draw(){
  strokeWeight(5);
  rect(440, 185, 70, 70);
  fill('white');
  noStroke();
  textSize(50);
  if(roundNum < 10){
    text(roundNum, 462, 237);
  }else{
    text(roundNum, 447, 237);
  }

  if(ready){
    drawGrid();
  }else{
    drawPatternGrid();
  }

  //GAME OVER
  if(wrongAnswer){
    //have solution pop up
    drawPatternGrid();
    //game over text
    noStroke();
    fill(80, 20, 10)
    rect(0, 170, 410, 60);
    fill("white");
    textSize(34);
    text("GAME OVER", 115, 210);
  }

  //border
  fill(myBlue);
  strokeWeight(10);
  line(4, 4, gridLength, 4);
  line(gridLength, 4, gridLength, gridLength);
  line(gridLength, gridLength, 4, gridLength);
  line(4, gridLength, 4, 4);

}


function drawPatternGrid(){
  stroke(10, 35, 60);
  strokeWeight(7);
  for(i = 0; i < tileCount; i++){
    for(j = 0; j < tileCount; j++){
      patternGenerator();
      fill(myGrid[i][j])
      rect(i * (gridLength/tileCount), j * (gridLength/tileCount), gridLength/tileCount, gridLength/tileCount);
    }
  }

}

//creates the solution pattern of tiles
var keyI = 0;
var keyJ = 0;
var x = 0;
function patternGenerator(){
  while(x < crtPattern){
    keyI = int(random(tileCount));
    keyJ = int(random(tileCount));
    if(myGrid[keyI][keyJ] != myOrange){
      myGrid[keyI][keyJ] = myOrange;
      x += 1;
    }
  }
}

//base grid for guessing
function drawGrid(){
  stroke(10, 35, 60);
  strokeWeight(7);
  for(i = 0; i < tileCount; i++){
    for(j = 0; j < tileCount; j++){
      mouseHover();
      fill(userGrid[i][j]);
      rect(i * (gridLength/tileCount), j * (gridLength/tileCount), gridLength/tileCount, gridLength/tileCount);
    }
  }
}

//highlights tiles if mouse hovers over them
function mouseHover(){
  if(mouseX < (i+1) * gridLength/tileCount & 
     mouseX > i * gridLength/tileCount && 
     mouseY < (j+1) * gridLength/tileCount &&
     mouseY > j * gridLength/tileCount){
    if(userGrid[i][j] != myOrange){
      userGrid[i][j] = myLBlue;
    }
 
  }else{
    if(userGrid[i][j] != myOrange){
      userGrid[i][j] = myBlue;
    }
  }
}


//selecting tiles
function mousePressed(){
 for(i = 0; i < tileCount; i++){
    for(j = 0; j < tileCount; j++){
      if(mouseX < (i+1) * gridLength/tileCount & 
       mouseX > i * gridLength/tileCount && 
       mouseY < (j+1) * gridLength/tileCount &&
       mouseY > j * gridLength/tileCount){
        if(counter < crtPattern - 1){
          if(userGrid[i][j] != myOrange){
            if(myGrid[i][j] == myOrange){
              userGrid[i][j] = myOrange;
              counter += 1;
              print(counter);
              print(crtPattern);
              }else{
              wrongAnswer = true;
              }
            }
          }else{
            ready = false;
            myGrid = [];
            userGrid = [];
            roundNum += 1;
            for(i = 0; i < tileCount; i++){
              myGrid.push([]);
              userGrid.push([]);
              for(j = 0; j < tileCount; j++){
                myGrid[i].push(myBlue);
                userGrid[i].push(myBlue);
              }
            }
            counter = 0;
            x = 0;
        }
      }
    }
  }
}

//switch grid
function keyTyped(){
  if(key === "r" || key === "R"){
    ready = true;
    }else{
      ready = false;
  }
}











This is a memorization game to test a user’s spatial recall ability. A pattern of tiles is first shown, and once the user has committed the pattern to memory, pressing the “r” key will render the grid blank, and the user must recreate the pattern by clicking on the tiles. If the pattern matches the original pattern, a new pattern is generated. Once the user clicks an incorrect tile, the game is over. The division of the grid is randomized.

Aaron Lee – Final Project

sketch

/*
Aaron Lee
Section C
sangwon2@andrew.cmu.edu
Final Project
*/

var canvaswidth = 640;
var canvasheight = 480;

var myCaptureDevice;
var brightnessThreshold = 50;
var darknessThreshold = 45;
var theBrightnessOfTheColorAtPxPy = 100;
var px = 30;
var py = 30;

var gamelogo;
var spacefighter;
var invader = [];
var missile = [];
var score = 0;

var img_Invader, img_spacefighter, img_Missile,img_background;


function setup() {//creating camera for the background
   createCanvas(canvaswidth, canvasheight);
   myCaptureDevice = createCapture(VIDEO);
   myCaptureDevice.size(canvaswidth, canvasheight); // attempt to size the camera. 
   myCaptureDevice.hide(); // this hides an unnecessary extra view.
   
}

function preload(){ //preloading the reference images
    img_Invader = loadImage("https://i.imgur.com/hBZOUxR.png");
    img_spacefighter = loadImage("https://i.imgur.com/wJPVqdo.png");
    img_Missile = loadImage("https://i.imgur.com/0O0UGN4.png");
    gamelogo = loadImage("https://i.imgur.com/hBZOUxR.png");
}

spacefighter = {
    x : 140,
    width : 30,
    y : 400,
    height: 30,
    draw : function() {
        image(img_spacefighter, this.x, this.y, this.width, this.height);
    }
}

//--------------------------------------//invader
function Invader(I) {
    I.active = true;
    I.x =random(640);
    I.y = 0;
    I.width = 50;
    I.height = 50;
    I.speed = 2;
    I.inBounds = function(){
        return I.x >= 0 & I.y >= 0 && I.x < canvaswidth - I.width && I.y < canvasheight - I.height;
    }
    I.draw = function(){
        image(img_Invader, I.x, I.y, I.width, I.height);
    }
    I.update = function(){
        I.active = I.active & I.inBounds();
        I.y += I.speed;
    }
    return I;

    function trlUpdate() { //this is the part where the code unfortunately doesn't work
   //fetch the color of the pixel at the (px,py)
   var theColorAtPxPy = myCaptureDevice.get(this.px, this.py);
   //compute its birghtness
   theBrightnessOfTheColorAtPxPy = brightness(theColorAtPxPy);
   //if the textrainletter is in a bright area, move downwards
   if (theBrightnessOfTheColorAtPxPy > brightnessThreshold) {
   this.py++;
   }
   //else if it's in a dark area, move up until we're in a light area
   while (theBrightnessOfTheColorAtPxPy < darknessThreshold & py > 0) {
     theColorAtPxPy = myCaptureDevice.get(px, py)
     theBrightnessOfTheColorAtPxPy = brightness(theColorAtPxPy);
     this.py--;  
   }
   //reset to initial place if falls off the screen
   if (this.py >= height) {
   this.py = 0;
   }
}
}

//--------------------------------------//missile
function Missile(I){
    I.active = true;
    I.x = spacefighter.x;//because both the missile and spacefighter share same origin
    I.y = spacefighter.y;
    I.width = 30;
    I.height = 30;
    I.speed = 10;
    I.inBounds = function(){
        return I.x >= 0 & I.y >= 0 && I.x < canvaswidth - I.width && I.y < canvasheight -  I.height;
    }
    I.update = function(){
        I.active  = I.active & I.inBounds();
        I.y -= I.speed;
    }
    I.draw = function(){
        image(img_Missile, I.x, I.y, I.width, I.height);
    }
    return I;
}


//--------------------------------------//shooting
function shooting(Invader, Missile){
    return Missile.x + Missile.width >= Invader.x & Missile.x < Invader.x + Invader.width &&
            Missile.y + Missile.height >= Invader.y && Missile.y < Invader.y + Invader.height;
}

function draw(){
    clear();
    myCaptureDevice.loadPixels();
   image(myCaptureDevice, 0,0);
   image(gamelogo, 30, 30);
    fill("red");
    textStyle(BOLD);
    textFont('Sprint2');
    textSize(20);
    text("HIGH SCORE : " + score, canvaswidth / 2 - 80, 20);
    if(keyIsDown(LEFT_ARROW)){              //left key configuration
        if(spacefighter.x - 3 >= 0)
            spacefighter.x -= 3;
        else
            spacefighter.x = 0;
    }
    if(keyIsDown(RIGHT_ARROW)){              //right key configuration
        if(spacefighter.x + 3 <= canvaswidth-spacefighter.width)
            spacefighter.x += 3;
        else
            spacefighter.x = canvaswidth - spacefighter.width;
    }
    if(keyIsDown(UP_ARROW)){              //up key configuration
        if(spacefighter.y - 5 >= 0)
            spacefighter.y -= 5;
        else
            spacefighter.y = 0;
    }
    if(keyIsDown(DOWN_ARROW)){              //down key configuration
        if(spacefighter.y + 5 <= canvasheight-spacefighter.height)
            spacefighter.y += 5;
        else
            spacefighter.y = canvasheight - spacefighter.height;
    }
    if(keyIsDown(32)){
        missile.push(Missile({}));
    }
    spacefighter.draw();              //draw spacefighter
    missile.forEach(function(Missile){//draw and update the missiles
        Missile.update();
        Missile.draw();
    });

    if(Math.random()<0.07){
        invader.push(Invader({}));
    }
    invader = invader.filter(function(Invader){
        return Invader.active;
    });
    invader.forEach(function(Invader){
        Invader.update();
        Invader.draw();
    });

    missile.forEach(function(Missile){
        invader.forEach(function(Invader){
            if(shooting(Invader, Missile)){
                Invader.active = false;
                Missile.active = false;
                score++;
            }
        });
    });

    invader.forEach(function(Invader){
        if(shooting(Invader, spacefighter)){
            Invader.active = false;
            noLoop();
            textStyle(BOLD);
            textFont('Sprint2');
            textSize(40);
            fill("red");
            text("continue? press f5", width / 2 - 160, height /2);
        }
    });
}

Instruction: 1) please allow camera access 2) you are a space fighter on an important mission. Use arrows keys for maneuver, and space bar to terminate space invaders 3) player who gets the higher score wins

*what didn’t work: 1)the pixels of the player in the background were supposed to buffer the velocity of the space invaders 2)the camera notification setting distracts the player in the beginning when the page is refreshed

I personally had lot of fun making this game as it really provoked my child experience with retro arcade games. I wanted to be selective in use of color, font and art in order to mimic the retro feelings.