// ICE VAR
var terraine = []; // array for ice terraine y coordinates
var noiseParam = 0; // x value of noise func
var noiseStep = .02; // how much x increases by
var icesize = noiseStep*2500;
// OBJECT VAR
var washer;
var handwasher;
var bubble;
var bubs = [];
var waterbottle;
var reusebottle;
var car;
var bus;
var shoponline;
var shoplocal;
var allobj = []; // holds all objects once they are created
var len; // length of allobj array
var n = 0; // initial spot in allobj array
var q;
var isMouseClicked = -1; // boolean for click
// AUDIO VAR
var waterdrop;
var watersplash;
// OBJECT FUNCTIONS
function makeWasher(cx, cy) {
washer = { x: cx,
y: cy,
show: drawWasher,
melt: 4,
txt: "Use electric washer" }
return washer;
}
function makeHandwasher(cx, cy) {
for (var i=0; i<30; i++) {
bubx = random(-35, 35);
buby = -random(-.5, 3)*i;
var bub = makeBubble(bubx, buby);
bubs.push(bub);
}
handwasher = { x: cx,
y: cy,
show: drawHandwasher,
melt: 1,
txt: "Hand-wash clothing" }
return handwasher;
}
function makeBubble(bubx, buby) {;
bubble = {x: bubx,
y: buby,
sz: random(2, 6),
show: drawBubble }
return bubble;
}
function makeWaterbottle(cx, cy) {
waterbottle = {x: cx,
y: cy,
show: drawWaterbottle,
melt: 3,
txt: "Plastic water bottle"}
return waterbottle;
}
function makeReusebottle(cx, cy) {
reusebottle = {x: cx,
y: cy,
show: drawReusebottle,
melt: 1,
txt: "Reusable water bottle"}
return reusebottle;
}
function makeCar(cx, cy) {
car = {x: cx,
y: cy,
show: drawCar,
melt: 5,
txt: "Drive a car"}
return car;
}
function makeBus(cx, cy) {
bus = {x: cx,
y: cy,
show: drawBus,
melt: 1,
txt: "Take public transportation"}
return bus;
}
function makeOnline(cx, cy) {
shoponline = {x: cx,
y: cy,
show: drawOnline,
melt: 10,
txt: "Shop online"}
return shoponline;
}
function makeLocal(cx, cy) {
shoplocal = {x: cx,
y: cy,
show: drawLocal,
melt: 1,
txt: "Shop local"}
return shoplocal;
}
// PRELOAD FOR AUDIO FILES
function preload() {
waterdrop = loadSound("https://courses.ideate.cmu.edu/15-104/f2021/wp-content/uploads/2021/11/splash.wav");
watersplash = loadSound("https://courses.ideate.cmu.edu/15-104/f2021/wp-content/uploads/2021/12/bigsplash.mp3");
}
// SETUP
function setup() {
createCanvas(400, 400);
strokeWeight(2);
rectMode(CENTER);
textAlign(CENTER);
textFont('Georgia');
textSize(13);
useSound();
// fill terraine array w coordinates for ice:
for (var i=0; i<icesize; i++) {
var n = noise(noiseParam); // n is between 0 and 1
var value = map(n, 0, 1, height/3, height); // draws onto canvas nicely
terraine.push(value); // value is the y coordinate at x=i
noiseParam += noiseStep;
}
// create objects:
washer = makeWasher(125, 100);
handwasher = makeHandwasher(275, 100);
waterbottle = makeWaterbottle(265, 100);
reusebottle = makeReusebottle(135, 100);
car = makeCar(125, 100);
bus = makeBus(275, 100);
shoponline = makeOnline(290, 100);
shoplocal = makeLocal(115, 100);
allobj = [washer, handwasher,
reusebottle, waterbottle,
car, bus,
shoplocal, shoponline];
len = allobj.length;
}
// SOUND SETUP
function soundSetup() { // setup for audio file
waterdrop.setVolume(0.5);
watersplash.setVolume(0.5);
}
// DRAW
function draw() {
// blue sky:
background(100, 175, 240);
// ice:
drawIce();
// it melts:
meltIce();
// draw objects:
if (terraine.length > 1) {
q = n%(len); // loops through array by 2s
allobj[q].show();
allobj[q+1].show();
}
}
// WHEN USER CLICKS
function mousePressed() {
let onObj = checkClick();
if (onObj == true) {
isMouseClicked = -isMouseClicked;
n += 2; // loops through array by 2s
isMouseClicked = -isMouseClicked;
}
}
function checkClick() {
if (mouseX>75 & mouseX<175 && mouseY>50 && mouseY<150) {
meltMore(allobj[q]);
return true;
}
if (mouseX>225 & mouseX<325 && mouseY>50 && mouseY<150) {
meltMore(allobj[q+1]);
return true;
}
}
// DRAW ICE
function drawIce() {
fill(255);
stroke(0);
strokeWeight(1);
beginShape();
vertex(-5, height);
vertex(-5, terraine[0]);
for (var j=0; j<terraine.length-1; j++) {
vertex(j*5, terraine[j]);
}
vertex((terraine.length-1)*icesize, terraine[terraine.length]);
vertex((terraine.length-1)*5, height);
endShape();
// check if completely melted:
checkIce();
}
// MELT ICE constantly
function meltIce() {
// when ice is melted, 'game' over
if (terraine.length == 1) { return }
// as ice melts, water drop audio is played
if (frameCount%250==0) {
terraine.pop();
waterdrop.play();
}
}
// MELT MORE! when environmentally more harmful choice is picked
function meltMore(obj) {
for (var k=0; k<obj.melt; k++) {
terraine.pop();
}
if (obj.melt == 1) { waterdrop.play() }
else { watersplash.play() }
}
// CHECK IF COMPLETELY MELTED
function checkIce() {
if (terraine.length <= 1) {
rect(width/2, height/2, 350, 350);
textAlign(CENTER);
textSize(35);
fill(0);
text("Oh No!", width/2, 150);
textSize(16);
fill(150, 0, 0);
text("Looks like your choices caused global warming.", width/2, 175);
textSize(20);
fill(0);
text("Next time, try to make better choices.", width/2, 215);
textSize(13);
text("You were already trying to make good choices?", width/2, 300);
text("You picked the best options that were avaiable to you?", width/2, 325);
textSize(18);
fill(150, 0, 0);
text("Have better options next time I guess.", width/2, 350);
textSize(11);
noStroke();
fill(255);
text("Refresh this page for another try!", width/2, 390);
noLoop();
}
}
// DRAW OBJECTS:
// washer
function drawWasher() {
push();
translate(this.x, this.y);
fill(220);
square(0, 0, 100, 5);
fill(150);
circle(0, 0, 75);
// water in washer:
noStroke();
fill(75, 125, 225);
beginShape();
vertex(-30, 0);
vertex(-30, 0);
curveVertex(-24, 2);
curveVertex(-18, 0);
curveVertex(-12, 2);
curveVertex(-6, 0);
curveVertex(0, 2);
curveVertex(6, 0);
curveVertex(12, 2);
curveVertex(18, 0);
curveVertex(24, 2);
vertex(30, 0);
curveVertex(26, 16);
curveVertex(15, 26);
curveVertex(0, 30);
curveVertex(-15, 26);
curveVertex(-26, 16);
vertex(-30, 0);
vertex(-30, 0);
endShape();
// door partially see-through:
stroke(0);
fill(175, 200, 255, 175);
circle(0, 0, 60);
// glare on glass door
noStroke();
push();
rotate(radians(40));
fill(200, 215, 255);
ellipse(0, -15, 30, 5);
ellipse(0, -20, 15, 3);
pop();
// bottons + stuff
fill(200, 0, 0);
circle(40, -40, 3);
fill(75);
circle(35, -40, 3);
circle(30, -40, 3);
fill(150);
circle(35, -33, 7);
// text
fill(0);
text(this.txt, 0, 62);
pop();
}
// handwash basin
function drawHandwasher() {
push();
translate(this.x, this.y);
// basin
fill(200);
quad(-50, 0, 50, 0, 35, 40, -35, 40);
arc(0, 0, 100, 25, 0, PI);
arc(0, 0, 100, 15, PI, 2*PI);
// clothes
noStroke();
fill(58, 101, 74);
quad(-30, -7, -15, -8, -15, 10, -32, 8);
fill(200, 100, 150);
quad(-20, -8, -7, -9, -5, 10, -25, 10);
// water
fill(100, 140, 240);
beginShape();
vertex(-45, 6);
vertex(-45, 5);
curveVertex(-36, 5);
curveVertex(-27, 6);
curveVertex(-18, 5);
curveVertex(-9, 6);
curveVertex(0, 5);
curveVertex(9, 6);
curveVertex(18, 5);
curveVertex(27, 6);
curveVertex(36, 5);
vertex(45, 5);
curveVertex(29, 10);
curveVertex(0, 12);
curveVertex(-29, 10);
vertex(-45, 5);
vertex(-45, 6);
endShape();
// suds + bubbles
fill(200, 215, 255);
stroke(100, 140, 240);
strokeWeight(.25);
for (var i=0; i<30; i++) {
bubs[i].show()
}
// text
fill(0);
strokeWeight(1);
text(this.txt, 0, 62);
pop();
}
// bubble
function drawBubble() {
circle(this.x, this.y, this.sz);
}
// plastic bottle
function drawWaterbottle() {
// cap
stroke(0);
strokeWeight(1);
fill(240);
rect(this.x, this.y-45, 15, 10);
// body of bottle
fill(225, 230, 255);
arc(this.x, this.y-18, 42, 47, radians(290), radians(250), OPEN);
beginShape();
vertex(this.x+21, this.y-15);
vertex(this.x+21, this.y-15);
curveVertex(this.x+20, this.y+6);
curveVertex(this.x+23, this.y+30);
curveVertex(this.x+19, this.y+50);
curveVertex(this.x, this.y+50);
curveVertex(this.x-19, this.y+50);
curveVertex(this.x-23, this.y+30);
curveVertex(this.x-20, this.y+6);
vertex(this.x-21, this.y-15);
vertex(this.x-21, this.y-15);
endShape();
// lines on cap
stroke(190);
for (var l=0; l<14; l+=2) {
line(this.x-6 + l, this.y-41, this.x-6 + l, this.y - 47);
}
// wrapper
fill(75, 125, 200);
noStroke();
beginShape();
vertex(this.x+21, this.y-15);
vertex(this.x+21, this.y-15);
vertex(this.x+20, this.y-2);
vertex(this.x+20, this.y+8);
vertex(this.x+22, this.y+20);
vertex(this.x-22, this.y+20);
vertex(this.x-20, this.y+8);
vertex(this.x-20, this.y-2);
vertex(this.x-21, this.y-15);
vertex(this.x-21, this.y-15);
endShape();
// details
stroke(190, 210, 250);
noFill();
arc(this.x, this.y-18, 30, 47, radians(-65), radians(-10));
arc(this.x, this.y-18, 10, 47, radians(-80), radians(-20));
arc(this.x, this.y-18, 10, 47, radians(200), radians(260));
arc(this.x, this.y-18, 30, 47, radians(190), radians(245));
arc(this.x, this.y+23, 70, 9, radians(10), radians(170));
arc(this.x, this.y+30, 60, 10, radians(10), radians(170));
arc(this.x, this.y+37, 55, 11, radians(10), radians(170));
//wrapper details
strokeWeight(2);
stroke(90, 145, 220);
beginShape();
vertex(this.x+15, this.y+10);
vertex(this.x+15, this.y+10);
curveVertex(this.x+10, this.y);
curveVertex(this.x-8, this.y-5);
vertex(this.x-15, this.y-10);
vertex(this.x-15, this.y-10);
endShape();
beginShape();
vertex(this.x+15, this.y+10);
vertex(this.x+15, this.y+10);
curveVertex(this.x+8, this.y+5);
curveVertex(this.x-10, this.y);
vertex(this.x-15, this.y-10);
vertex(this.x-15, this.y-10);
endShape();
// text
fill(0);
strokeWeight(1);
text(this.txt, this.x, this.y+62);
}
// reusable bottle
function drawReusebottle() {
stroke(0);
strokeWeight(1);
// water
fill(100, 155, 230);
stroke(90, 145, 220);
beginShape();
vertex(this.x-23, this.y-18);
vertex(this.x-23, this.y-18);
curveVertex(this.x-17, this.y-19);
curveVertex(this.x-10, this.y-18);
curveVertex(this.x, this.y-19);
curveVertex(this.x+10, this.y-18);
curveVertex(this.x+17, this.y-19);
vertex(this.x+23, this.y-18);
vertex(this.x+23, this.y-18);
endShape();
noStroke();
beginShape();
vertex(this.x+23, this.y-18);
vertex(this.x+23, this.y-18);
curveVertex(this.x+24, this.y+30);
curveVertex(this.x+19, this.y+50);
curveVertex(this.x-19, this.y+50);
curveVertex(this.x-24, this.y+30);
vertex(this.x-23, this.y-18);
vertex(this.x-23, this.y-18);
endShape();
// body of bottle
noStroke();
fill(200, 100, 150, 100);
arc(this.x, this.y-18, 46, 45, radians(185), radians(-5), OPEN);
noFill();
stroke(0);
arc(this.x, this.y-18, 46, 45, radians(290), radians(0), OPEN);
arc(this.x, this.y-18, 46, 45, radians(180), radians(250), OPEN);
fill(200, 100, 150, 100);
beginShape();
vertex(this.x+23, this.y-20);
vertex(this.x+23, this.y-20);
curveVertex(this.x+24, this.y+30);
curveVertex(this.x+19, this.y+50);
curveVertex(this.x-19, this.y+50);
curveVertex(this.x-24, this.y+30);
vertex(this.x-23, this.y-20);
vertex(this.x-23, this.y-20);
endShape();
// lines on body of bottle
stroke(190, 90, 140, 200);
for (var u=0; u<12; u+=1) {
line(this.x-15, this.y-15+(u*5), this.x-22, this.y-15+(u*5));
}
// stickers!
// smiley face
stroke(0);
strokeWeight(.5);
fill(250, 225, 0);
push();
translate(this.x+16, this.y+20);
rotate(radians(32));
arc(0, 0, 20, 20, radians(8), radians(287), OPEN);
arc(0, 0, 13, 13, radians(10), radians(170));
strokeWeight(1);
line(-2, 0, -2, -2);
line(2, 0, 2, -2);
pop();
// flower
push();
fill(225, 125, 255);
translate(this.x+9, this.y+36);
rotate(radians(-20));
let rot = 0;
for (var r=0; r<7; r++) {
push();
rotate(radians(rot));
ellipse(6, 0, 8, 4);
pop();
rot += 360/7;
}
fill(175, 200, 100);
circle(0, 0, 5);
pop();
// cap
noFill();
strokeWeight(2);
stroke(200);
arc(this.x+14, this.y-50, 29, 25, radians(200), radians(90));
stroke(0)
strokeWeight(1);
arc(this.x+14, this.y-50, 28, 23, radians(200), radians(90));
arc(this.x+14, this.y-50, 33, 27, radians(200), radians(90));
fill(200);
rect(this.x, this.y-52, 15, 5, 2);
rect(this.x, this.y-45, 25, 15, 2);
stroke(225);
strokeWeight(6);
line(this.x, this.y-40, this.x, this.y-45);
line(this.x-8, this.y-40, this.x-8, this.y-45);
line(this.x+8, this.y-40, this.x+8, this.y-45);
stroke(0);
strokeWeight(1);
rect(this.x, this.y-38, 28, 2, 3);
// text
fill(0);
text(this.txt, this.x, this.y+62);
}
// car
function drawCar() {
let carcol = color(150, 0, 0);
let detailcol = color(100, 0, 0);
// exhaust pipe
fill(120);
noStroke();
rect(this.x+60, this.y+27, 5, 3);
// body of car
stroke(detailcol);
fill(carcol);
beginShape();
vertex(this.x-40, this.y);
vertex(this.x-40, this.y+1);
curveVertex(this.x-20, this.y-22);
curveVertex(this.x+25, this.y-22);
curveVertex(this.x+48, this.y);
curveVertex(this.x+57, this.y+3);
curveVertex(this.x+58, this.y+10);
curveVertex(this.x+59, this.y+25);
curveVertex(this.x+45, this.y+30);
curveVertex(this.x-60, this.y+30);
curveVertex(this.x-65, this.y+8);
vertex(this.x-40, this.y+1);
vertex(this.x-40, this.y+1);
endShape();
// windows
stroke(150, 160, 200);
fill(150, 160, 200);
beginShape();
vertex(this.x-35, this.y+1);
vertex(this.x-35, this.y+1);
curveVertex(this.x-15, this.y-19);
curveVertex(this.x+20, this.y-19);
curveVertex(this.x+40, this.y-1);
curveVertex(this.x+30, this.y+1);
vertex(this.x-35, this.y+1);
vertex(this.x-35, this.y+1);
endShape();
fill(carcol);
stroke(carcol);
strokeWeight(4);
line(this.x+2, this.y+2, this.x+2, this.y-22);
strokeWeight(2);
line(this.x+33, this.y+2, this.x+33, this.y-16);
// lights
noStroke();
fill(230, 230, 175);
push();
translate(this.x-62, this.y+9);
rotate(radians(-35));
ellipse(0, 0, 10, 7);
ellipse(-7, 1, 3, 3);
pop();
rect(this.x+57, this.y+21, 5, 7, 2);
rect(this.x+57, this.y+15, 2, 4, 2);
// handles
stroke(detailcol);
line(this.x-5, this.y+7, this.x, this.y+7);
line(this.x+4, this.y+7, this.x+9, this.y+7);
// wheels
fill(25);
stroke(0);
ellipse(this.x+35, this.y+30, 25, 25);
ellipse(this.x-40, this.y+30, 25, 25);
let wrot = 0;
for (var j=0; j<5; j++) {
push();
translate(this.x+35, this.y+30);
rotate(radians(wrot));
line(-10, 0, 10, 0);
pop();
push();
translate(this.x-40, this.y+30);
rotate(radians(wrot));
line(-10, 0, 10, 0);
pop();
wrot+=360/10;
}
// text
fill(0);
strokeWeight(1);
text(this.txt, this.x, this.y+62);
}
// bus
function drawBus() {
let buscol = color(50, 75, 255);
let busdetails = color(20, 40, 200);
// exhaust pipe
fill(120);
noStroke();
rect(this.x+50, this.y-27, 3, 5);
// bus body
fill(buscol);
stroke(busdetails);
strokeWeight(2);
rect(this.x, this.y, 120, 50, 2);
//details
line(this.x-45, this.y+8, this.x+55, this.y+8);
strokeWeight(1);
line(this.x-45, this.y+10, this.x+55, this.y+10);
// windows
stroke(150, 160, 200);
strokeWeight(2);
fill(150, 160, 200);
rect(this.x-57, this.y-5, 5, 25);
for (var r=0; r<10; r++) {
rect(this.x-41+(10*r), this.y-3, 7, 15);
}
// wheels
fill(25);
stroke(0);
ellipse(this.x+38, this.y+30, 15, 15);
ellipse(this.x+22, this.y+30, 15, 15);
ellipse(this.x-45, this.y+30, 15, 15);
ellipse(this.x-29, this.y+30, 15, 15);
strokeWeight(1);
let wrot = 0;
for (var j=0; j<5; j++) {
push();
translate(this.x+38, this.y+30);
rotate(radians(wrot));
line(-7, 0, 7, 0);
pop();
push();
translate(this.x+22, this.y+30);
rotate(radians(wrot));
line(-7, 0, 7, 0);
pop();
push();
translate(this.x-45, this.y+30);
rotate(radians(wrot));
line(-7, 0, 7, 0);
pop();
push();
translate(this.x-29, this.y+30);
rotate(radians(wrot));
line(-7, 0, 7, 0);
pop();
wrot+=360/10;
}
// lights
noStroke();
fill(230, 230, 175);
ellipse(this.x-58, this.y+15, 7, 10);
rect(this.x-59, this.y+15, 5, 10, 3);
rect(this.x+57, this.y+21, 5, 7, 2);
// text
fill(0);
strokeWeight(1);
text(this.txt, this.x, this.y+62);
}
// plane
function drawOnline() {
fill(200);
stroke(0);
push();
translate(this.x, this.y)
//back wing
arc(-9, 10, 150, 16, radians(90), radians(270));
push();
translate(-25, 13);
rotate(radians(31));
arc(-6, 0, 40, 14, radians(270), radians(90));
circle(-6, 0, 14);
pop();
//small tail wing back
arc(55, 35, 45, 10, radians(90), radians(270));
push();
rotate(radians(31));
//body
arc(10, 0, 150, 40, radians(175), radians(357));
rotate(radians(-6));
arc(10, 0, 150, 40, radians(4), radians(185));
noStroke();
ellipse(-60, -3, 10, 14);
stroke(0);
line(-65, -1, -65, -6);
rotate(radians(6));
// windows
strokeWeight(.5);
fill(150, 160, 200);
quad(-61, -6, -65, -1, -40, -1, -40, -6);
for (var r=0; r<10; r++) {
ellipse((10*r)-30, -7, 6, 7);
}
pop();
//back fin thingy
stroke(0);
fill(200);
push();
translate(52, 13);
rotate(radians(22));
beginShape();
vertex(0, 0);
vertex(0, 0);
curveVertex(15, -3);
vertex(30, -15);
vertex(30, 16);
vertex(30, 16);
endShape();
pop();
//small tail wing front
arc(70, 35, 45, 10, radians(260), radians(100));
//front wing
arc(12, 8, 175, 20, radians(220), radians(0));
arc(12, 8, 175, 15, radians(0), radians(50));
push();
translate(25, 13);
rotate(radians(31));
arc(-10, 0, 40, 14, radians(270), radians(90));
circle(-10, 0, 14);
pop();
// text
fill(0);
strokeWeight(1);
text(this.txt, 0, 62);
pop();
}
// store
function drawLocal() {
square(this.x, this.y, 100);
noFill();
push();
translate(this.x, this.y);
// building
fill(150, 170, 150);
stroke(0);
rect(0, 15, 100, 70);
stroke(125, 145, 125);
for(var l=0; l<11; l++) {
line(-49, -15+(l*6), 49, -15+(l*6));
}
//roof
stroke(0);
fill(150, 100, 50);
quad(-60, -20, 60, -20, 50, -50, -50, -50);
//door
stroke(15, 75, 15);
fill(15, 75, 15);
rect(0, 37, 16, 25);
arc(0, 25, 16, 30, radians(180), radians(0));
stroke(0);
line(7, 30, 2, 30);
//windows
strokeWeight(.5);
fill(150, 160, 200);
rect(-30, 15, 30, 40);
rect(30, 15, 30, 40);
for (var m=0; m<3; m++) {
line(-38+(m*8), 35, -38+(m*8), -5);
line(38-(m*8), 35, 38-(m*8), -5);
line(-45, 5+(m*10), -15, 5+(m*10));
line(45, 5+(m*10), 15, 5+(m*10));
}
//signs in windows
fill(180);
line(15, 15, 30, 25);
line(45, 15, 30, 25);
rect(30, 25, 15, 8);
line(-15, 15, -30, 25);
line(-45, 15, -30, 25);
rect(-30, 25, 15, 8);
noStroke()
fill(150, 0, 0);
textAlign(CENTER);
textSize(4);
text('OPEN!', -30, 26);
fill(0);
textSize(3);
text('Hours:', 28, 25);
stroke(0);
line(26, 26, 35, 26);
line(26, 27, 35, 27);
fill(100);
// text
fill(0);
textSize(13);
strokeWeight(1);
text(this.txt, 0, 62);
pop();
}
My program depicts an iceberg that is melting, and presents the player with common choices that people have to make on a regular basis. The canvas reacts to the decisions that the player makes. The choices all have varying environmental impacts, and they correspond to the melting of the iceberg. If the player picks the environmentally more harmful choice, more of the ice falls into the ocean. If the player chooses an environmentally friendly option, the ice continues to melt at the same rate. This interactive program is not a winnable game. The ice will always melt. As it melts, a splash sound is played. **
I chose this design as my project because I wanted to highlight the way that industries pressure individuals to make good personal choices while producing products that only cause more damage. Individual choices will not stop climate change. To help preserve our planet, we need to rethink the entire system of the global enocomy.
The program must use a local server to run, and two sound files should be included in the folder to make the splashing noises when the ice melts. There are instructions included on the opening page.
I am really happy with how this project turned out. I only have a few choice that are presented to the player, but my goal was to make at least four sets of options, and that is what I have done. I screen recorded a majority of my process, and I have included a time-lapsed video at the bottom of this post.
** for the WordPress upload, my splash sound file was not working so I used a substitute file with a similar sound.