I decided to take the global warming route that was mentioned for my project. I looked at the 2050 flood map that was offered and started researching some of the areas there. Basically, I wanted to show the amazing places that may be completely lost if something isn’t done to slow/stop the temperature rise.
Category: SectionC
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.
// 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
/*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
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:
- Character movement
- Introducing cars
- Adding roads
- Creating the river and bridge
- Adding the next 3 rows of cars
- Losing screen
- Winning screen
- Reset button (ENTER)
- Start button (SPACE)
- 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
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.
// 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.
// 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
// 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
/*
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.