// Alec Albright
// aalbrigh
// Section B
// Project 12 - Final Project
var video;
var nPoints = 100; // number of points used to draw curves
var previous; // previous image from camera
var threshold = 150; // used to determine if a pixel is different enough to consider it "moving"
var motionX = 0; // main coordinate of X motion
var motionY = 0; // main coordinate of Y motion
var lerpX = 0; // X coordinate for smoothing of motion
var lerpY = 0; // Y coordinate for smoothing of motion
var higherLove; // Whitney Houston's "Higher Love"
var low; // Flo Rida's "Low"
var irreplaceable; // Beyonce's "Irreplaceable"
var newBooty; // Bubba Sparxxx's "Ms. New Booty"
var higherVol; // Higher love volume
var lowVol; // low volume
var irrepVol; // irreplaceable volume
var bootyVol; // new booty volume
function preload(){
higherLove = loadSound("https://courses.ideate.cmu.edu/15-104/f2019/wp-content/uploads/2019/12/Higher-Love.wav");
low = loadSound("https://courses.ideate.cmu.edu/15-104/f2019/wp-content/uploads/2019/12/Low.wav");
irreplaceable = loadSound("https://courses.ideate.cmu.edu/15-104/f2019/wp-content/uploads/2019/12/Irreplaceable.wav");
newBooty = loadSound("https://courses.ideate.cmu.edu/15-104/f2019/wp-content/uploads/2019/12/New-Booty.wav");
}
function soundSetup() { // setup for audio generation
// making sine
sine = new p5.Oscillator();
sine.setType("sine");
//sine.start();
// making sawtooth
sawtooth = new p5.Oscillator();
sawtooth.setType("sawtooth");
//sawtooth.start();
// making square wave
square = new p5.Oscillator();
square.setType("square");
square.freq(440);
//square.start();
}
function setup(){
createCanvas(480, 480);
angleMode(RADIANS);
video = createCapture(VIDEO);
video.size(480, 480); // attempt to size the camera.
video.hide(); // this hides an unnecessary extra view.
// prepping to copy previous frame for difference in motion
previous = createImage(480, 480, RGB);
useSound();
higherLove.play();
higherLove.setVolume(0);
higherLove.loop();
low.play();
low.setVolume(0);
low.loop();
irreplaceable.play();
irreplaceable.setVolume(0);
irreplaceable.loop();
newBooty.play();
newBooty.setVolume(0);
newBooty.loop();
}
function draw(){
var count = 0; // number of pixel instances we've looped through
var sumX = 0; // sum of motion X coordinates
var sumY = 0; // sum of motion X coordinates
// making camera actually mirror user
push();
translate(width, 0);
scale(-1, 1);
image(previous, 0, 0);
pop();
loadPixels();
video.loadPixels(); // this must be done on each frame.
previous.loadPixels();
// comparing all pixels to previous image
for (var x = 0; x < video.width; x ++) {
for (var y = 0; y < video.height; y ++) {
var location = (x + y * video.width) * 4;
// finding previous and current colors
// previous
var red1 = previous.pixels[location];
var green1 = previous.pixels[location + 1];
var blue1 = previous.pixels[location + 2];
// current
var red2 = video.pixels[location];
var green2 = video.pixels[location + 1];
var blue2 = video.pixels[location + 2];
var diff = distSquared(red1, green1, blue1, red2, green2, blue2);
// checking whether they are different enough to call motion
if (diff > threshold * threshold) {
sumX += x;
sumY += y;
count ++;
}
}
}
updatePixels();
// only count it as a different frame if more than 30 pixels have changed
// find main X and Y coordinate of motion, this will be our control for everything
if (count > 100) {
motionX = sumX / count;
motionY = sumY / count;
}
// maintaining mirrored scale for user interface
push();
translate(width, 0);
scale(-1, 1);
// smoothing out how the point of focus is travelling
lerpX = lerp(lerpX, motionX, 0.1);
lerpY = lerp(lerpY, motionY, 0.1);
// drawing point so user knows where the main motion point is
stroke("black");
fill("white");
ellipse(lerpX, lerpY, 20, 20);
pop();
push();
translate(width / 2, height / 2);
// draw all the shapes
drawHippopede();
drawEpicycloid();
drawHypotrochoid();
pop();
previous.copy(video, 0, 0, video.width, video.height, 0, 0, video.width, video.height);
// creating slight boundaries for better sound isolation
if(lerpY < 200 || lerpY > 320) {
// letting contrast between top and bottom come through
bootyVol = 0;
irrepVol = 0;
newBooty.setVolume(bootyVol);
irreplaceable.setVolume(irrepVol);
// as we move up, more higher love/less low
// volume 0 to 1
higherVol = map(lerpY, 0, width, 0, 1);
higherLove.setVolume(1 - higherVol);
lowVol = map(lerpY, 0, width, 0, 1);
low.setVolume(lowVol);
} else {
// letting contrast between right and left come through
higherVol = 0;
lowVol = 0;
higherLove.setVolume(higherVol);
low.setVolume(lowVol);
// as we move right, more new booty/less irreplaceable
// volume 0 to 1
bootyVol = map(lerpX, 0, width, 0, 1);
newBooty.setVolume(1 - bootyVol);
irrepVol = map(lerpX, 0, width, 0, 1);
irreplaceable.setVolume(irrepVol);
}
}
// draws Hippopede
function drawHippopede() {
var x; // x coordinate of vertex
var y; // y coordinate of vertex
var r; // polar coordinate
var a = lerpX / 3 // main parameter of the curve
var b = map(a, 0, 480, 100, 200); // circle radius
var rotation = map(lerpY, 0, 480, 0, TWO_PI); // amount of rotation
// thickness of line proportional to the circle radius
strokeWeight(b / 6);
stroke(255, 255, 255, 150);
noFill();
// rotate shape
push();
rotate(rotation);
// start drawing the shape, one point at a time
beginShape();
for(var i = 0; i < nPoints; i++){
var t = map(i, 0, nPoints, 0, TWO_PI);
// find r (polar equation)
r = sqrt(4 * b * (a - b * sinSq(t)));
// convert to x and y coordinates
x = r * cos(t);
y = r * sin(t);
// draw a point at x, y
vertex(x, y);
}
endShape();
pop();
}
// draws hypotrochoid
function drawHypotrochoid() {
var x; // x coordinate of vertex
var y; // y coordinate of vertex
var a = map(lerpX, 0, 480, 20, 100); // radius of the interior circle
var b = 3; // radius of the petals
var h = lerpX / 10; // distance from center of interior circle
var red = map((lerpX + lerpY) / 2, 0, 480, 0, 255); // how much red
var blue = map(lerpY, 0, 480, 0, 255); // how much blue
var alpha = map(lerpX, 0, 480, 50, 150); // how opaque
var rotation = map(lerpY, 100, 300, 0, TWO_PI); // amount of rotation
strokeWeight(2)
stroke(255, 255, 255, 150);
// control color and opacity with mouse location
fill(red, 0, blue, alpha);
// control rotation with lerpY
push();
rotate(rotation);
// create the shape itself
beginShape();
for(var i = 0; i < nPoints; i++) {
var t = map(i, 0, nPoints, 0, TWO_PI);
// use parametric euqations for hypotrochoid to find x and y
x = (a - b) * cos(t) + h * cos((a - b) / b * t);
y = (a - b) * sin(t) - h * sin((a - b) / b * t);
// draw a point at x, y
vertex(x, y)
}
endShape(CLOSE);
pop();
}
// draws an epicycloid
function drawEpicycloid() {
var x; // x coordinate of vertex
var y; // y coordinate of vertex
var a = map(lerpX, 0, 480, 20, 100); // radius of interior circle
var b = map(lerpY, 0, 480, 5, 30); // radius of petals
var blue = map((lerpX + lerpY) / 2, 0, 480, 0, 255); // how much blue
var red = map(lerpY, 0, 480, 0, 255); // how much red
var rotation = map(lerpY, 100, 300, 0, TWO_PI); // how muhc rotation
// control color with mouse location
strokeWeight(10)
stroke(red, 0, blue, 150);
// control rotation with mouse location
push();
rotate(rotation);
// start drawing shape
beginShape();
for(var i = 0; i < nPoints; i++) {
var t = map(i, 0, nPoints, 0, TWO_PI);
// find coordinates using epicycloid parametric equations
x = (a + b) * cos(t) - b * cos((a + b) / b * t);
y = (a + b) * sin(t) - b * sin((a + b) / b * t);
// draw a point at x, y
vertex(x, y);
}
endShape();
pop();
}
// defines sin^2 using trigonometric identities
function sinSq(x) {
return((1 - cos(2 * x)) / 2);
}
function distSquared(x1, y1, x2, y2) {
let dx = x2 - x1;
let dy = y2 - y1;
return (dx * dx) + (dy * dy);
}
For the Final Project, I wanted to do something that had to deal with music, as it is my primary focus at CMU, but I also wanted to try doing something computationally complex that I would certainly be challenged by. Thus, the idea to create a motion detection music player was born! I also wanted to keep it fun, so I decided to play music related to each quadrant: “Irreplaceable” – Beyonce for the left (to the left, to the left…), “Higher Love” – Whitney Houston for the top, “Low” – Flo Rida for the bottom, and “Ms. New Booty” – Bubba Sparxxx for the right (get it right, get it right get it tight). For better user interfacing, I also included a floating point that denotes where the program has tracked the average motion.
The process was most difficult in rendering and comparing one frame to its previous frame in order to create a successful motion detection program. It was interesting mapping the visualization to the motion detection as well, giving each song its own visual identity.
In order to run the program, you must allow the website to access your video camera. However, I have included a demo video below for those who would rather see that.
//Minjae Jeong
//Section B
//minjaej@andrew.cmu.edu
//Final Project
var human = []; //array to save x y position
var count = 0;
var transp = 255; //transparency
function preload() {
//simplified world map
worldMap = loadImage("https://i.imgur.com/Kviuun0.png");
}
function setup() {
createCanvas(600, 348);
textAlign(CENTER);
}
function draw() {
background(132, 181, 229); //rgb of image's body of water
tint(255, transp); //this makes the water to fill up as you click
image(worldMap, 0, 30);
//title
textSize(24);
fill('red');
text("UNTIL WHEN CAN THE EARTH SUPPORT US?", width / 2, 27);//Have you thought about it
//Draw human + CO2
strokeWeight(1);
for (var i = 0; i < human.length; i++) {
xx = human[i][0];
yy = human[i][1];
fill('black');
drawHuman(xx, yy);
drawco2(xx, yy - 5);
}
//display global warming text
if (count > 20 & count < 40) {
textSize(20);
fill("red");
text("GLOBAL WARMING", 350, 330);
// count = count + 1;
}
//display "No more penguins and polar bears." Could be very soon
if (count > 40 & count < 60) {
textSize(20);
fill("red");
text("NO MORE PENGUINS & POLAR BEARS", 320, 50);
}
//display "No more place to live"
//(Elon Musk please)
if (count > 70 & count < 100) {
textSize(27);
fill("red");
text("NO MORE PLACE TO LIVE", width / 2, height / 2);
}
//changes background color to black
if (count > 90 & count < 100) {
fill("black");
rect(0, 0, width, height);
fill("red");
text("NO MORE PLACE TO LIVE", width / 2, height / 2);
}
//"What will you do?"
if (count >= 100){
fill('black');
rect(0, 0, width, height);
fill('red');
textSize(30);
text("WHAT WILL YOU DO?", width / 2, height /2);
}
}
//draw human
function drawHuman(x, y) {
ellipse(x, y, 5);
rect(x - 2, y + 5, 4, 11);
line(x - 2, y + 7, x - 8, y + 2);
line(x + 2, y + 7, x + 8 , y + 2);
}
//CO2
function drawco2(x, y) {
textSize(9);
text("+ CO2", x, y);
}
function mousePressed() {
human.push([mouseX, mouseY]); //Save mouseX, mouseY
transp = transp - 3.3; //Water fills up the land
count += 1;
}
For the final project, I made an interactive map that I wish to deliver some message. Click to add people on the map, keep on adding as it represents human population on the Earth.
My final project is little different from the proposal, because the original idea, which was to indicate temperature rise and deforestation, did not have clear message compared to what it actually shows. So I rather made a simple interactive map that shows what is happening as we grow on the Earth.
]]>//Julia Nishizaki
//Section B
//jnishiza@andrew.cmu.edu
//Final Project - ABCs of Climate Change
//variables for climate change effects, uses true/false statements to make effects appear/disappear
var cE = {
lakeColor: false, //algae blooms
flowers: false, //biodiversity
clouds: false, //carbon footprint
trees: true, //deforestation
gas: false, //greenhouse gases
mtn: false, //hazardous waste
hill: false, //invasive species
jeopardy: false, //jeopardy
house: false, //population growth
sky: false, //smog
field: false, //toxic pollutants
building: false, //urban sprawl
lake: false, //water levels
deadz: false, //zones that are dead
}
//variables to help with effects of climate change
var lakeWidth = 200; //initial width of lake
var lakeHeight = 35; //height of lake
var skyHeight = 0; //height of sky for smog
var cloudX = 475; //starting x position for cloud
var gasX = 475; //starting x position for gas cloud
var jtranslu = 0; //starting opacity for "smoke screen", jeopardy
//variables for the window and its dimensions
var win = {
ws: 125, //distance from window to right and left sides of canvas
wt: 40, //distance from window to top of canvas
wb: 160, //distance from window to bottom of canvas
frm: 5, //width of window frame
dis: 5, //displacement outside of canvas
}
//variables for colors used throughout, especially for objects that change color
var colors = {
hill: 80, //light green hill (HSB)
mtnb: 70, //purple mountains (HSB)
sky: '#8ED9EF', //light blue of sky
smogop: 80, //opacity of smog in background
field: 'green', //green
lakeh: 55, //hue value of lake (HSB)
lakes: 50, //saturation value of lake (HSB)
lakeb: 100, //brightness value of lake (HSB)
fieldb: 60, //brightness value of field (HSB)
wall: 230, //light gray of walls
table: 150, //dark gray of table
flower: 'purple', //purple flowers
plnflw: 'green', //green flowers
type: '#8BC63F', //light green of type
}
//arrays that store letters and each letter's climate change info
var alphabetLetters = ["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 letterInfo = [ //stores copy on climate change ABCs
"Algae Blooms: toxic algae can poison water and create dead zones",
"Biodiversity: the variety of life on the planet",
"Carbon Footprint: the amount of carbon dioxide each of us produces",
"Deforestation: the reduction of trees due to natural forces or human activity",
"Extinctions: the death of all individuals of a species",
"Fossil Fuels: nonrenewable fuels that are burned for energy like coal and gas",
"Greenhouse Gases: gases like methane, trap heat and warm the atmosphere",
"Hazardous Waste: waste that pose a risk to human health and the environment",
"Invasive Species: any organism not native to an ecosystem that causes harm",
"Jeopardy: danger of loss, harm, or failure",
"Keystone Species: a species that is critical to the survival of other species",
"Landfill: a site where wastes are dumped for permanent disposal",
"Microplastics: very small pieces of plastic that pollute the environment",
"Nonrenewable: resources that can't be replenished onced used",
"Ozone Layer: a thin protective layer of gas above Earth that filters UV radiation",
"Population Growth: an increase in the total human population",
"Quotas Insufficient: limits and restrictions in environmental policies not met",
"Runoff: stormwater from cities or farms that carry pollutants into water systems",
"Smog: dust, smoke, or chemical fumes that pollute the air",
"Toxic Pollutants: contaminate areas and cause death, disease, or birth defects",
"Urban Sprawl: expansion of auto-dependent developments over large areas of land",
"Vulnerability: susceptibility to harm from exposure to stresses",
"Water Level Rise: rising sea levels due to global warming and melting glaciers",
"Xenobiotic: non-natural or man-made substances found in the environment",
"Yield: amount of crops produced per unit area",
"Zones that are Dead: dead zones are caused by hypoxia, or lack of oxygen in water",
];
function setup() {
createCanvas(600, 480);
frameRate(30);
}
function draw() {
background(colors.sky); //sets the background as a light blue
prepEnvironEffects(); //connects each key to a variable
drawLandscape(); //creates all elements of the landscape
drawRoom(); //creates all elements of the room
typedLetterAlphabet(); //displays the alphabet
typedLetterInfo(); //displays the words and definitions
}
function prepEnvironEffects() {//when a particular key is pressed, the variable associated with that key switches from false to true
if (key == 'a') { //algae blooms
cE.lakeColor = true;
}
if (key == 'b') { //biodiversity
cE.flowers = true;
}
if (key == 'c') { //carbon footprint
cE.clouds = true;
}
if (key == 'd') { //deforestation
cE.trees = false;
}
if (key == 'g') { //greenhouse gases
cE.gas = true;
}
if (key == 'h') { //hazardous waste
cE.mtn = true;
}
if (key == 'i') { //invasive species
cE.hill = true;
}
if (key == 'j') { //jeopardy
cE.jeopardy = true;
}
if (key == 'p') { //population growth
cE.house = true;
}
if (key == 's') { //smog
cE.sky = true;
}
if (key == 't') { //toxic pollutants
cE.field = true;
}
if (key == 'u') { //urban sprawl
cE.building = true;
}
if (key == 'w') { //water level rise
cE.lake = true;
}
if (key == 'z') { //zones that are dead
cE.deadz = true;
}
}
//Creates elements in the landscape, and calls for changes to those elements
function drawLandscape() {
makeClouds(); //creates clouds for carbon footprint and greenhouse gas, letters c and g
changeSky(); //creates smog screen in background, letter s
changeMtnHill(); //changes color of mountains and hills, letters h and i
var landscapeScale1 = 0.004; //detail in hills
var landscapeScale2 = 0.018; //detail in mountains
//creates mountains in the background
colorMode(HSB, 100);
stroke(70, 30, colors.mtnb);
strokeWeight(1);
for (var x = 0; x < width; x ++) {
var l = (x * landscapeScale2);
var y = map(noise(l), 0, 1, height * 0.2, height * 0.5); //constrains hills
line(x, y, x, height); //creates vertical lines, forming a solid shape
}
colorMode(RGB);
makeBuilding(); //creates buildings when letter u is pressed
//creates hills in the middleground
colorMode(HSB, 100); //switches color mode to HSB to help with color changes
stroke(25, 50, colors.hill);
for (var x = 0; x < width; x ++) {
var l = (x * landscapeScale1);
var y = map(noise(l), 0, 1, height * 0.4, height * 0.6); //constrains hills
line(x, y, x, height); //creates vertical lines, forming a solid shape
}
//creates field in foreground
noStroke();
fill(28, 100, colors.fieldb); //field color
rect(0, height * 0.55, width, height * 0.35);
changeField(); //changes color of field, letter t
//creates lake
fill(colors.lakeh, colors.lakes, colors.lakeb);
ellipse(175, 286, lakeWidth, lakeHeight);
changeLake(); //changes color and size of lake, letters a, w, and z
colorMode(RGB); //switches color mode back to RGB
makeFlowers(); //makes flowers, alternating colors, becomes one color with letter b
makeHouse(); //makes houses, letter p
makeTrees(); //makes single yellow tree in foreground, tree disappears with letter d
smokeScreen(); //creates a semi-transparent grey layer with letter j
}
//makes and moves the clouds
function makeClouds() {
if (cE.clouds == true) { //makes a cloud for carbon footprint, letter c
drawClouds(cloudX, 120, 150, 50, 150);
cloudX = cloudX - 2;
if (cloudX < win.ws - 150) {
cloudX = 475;
}
}
if (cE.gas == true) { //makes a cloud for greenhouse gases, letter g
drawClouds(gasX, 180, 220, 75, 100);
gasX = gasX - 1;
if (gasX < win.ws - 220) {
gasX = 475;
}
}
}
//draws the clouds
function drawClouds(locationX, locationY, width, height, color) {
push();
fill(color);
translate(locationX, locationY);
rect(0, - height, width, height, 50, 50, 50, 50);
pop();
}
//adds "smog" in background
function changeSky() {
if (cE.sky == true) {
fill(80, colors.smogop);
noStroke();
rect(0, height * 0.55 - skyHeight, width, skyHeight);
skyHeight = constrain(skyHeight, 0, 300) + 3;
}
}
//changes colors of mountains and hills
function changeMtnHill() {
if (cE.mtn == true) { //changes mountain colors
colors.mtnb = constrain(colors.mtnb, 20, 70) - 5;
}
if (cE.hill == true) { //changes hill colors
colors.hill = constrain(colors.hill, 30, 80) - 5;
}
}
//makes the buildings for urban sprawl
function makeBuilding() {
if (cE.building == true) { //from left to right
drawBuildings(125, 37, 130, 180);
drawBuildings(162, 62, 110, 150);
drawBuildings(224, 50, 120, 200);
drawBuildings(274, 37, 100, 130);
}
}
//draws buildings
function drawBuildings(locationX, buildW, buildH, color) {
push();
noStroke();
fill(color);
translate(locationX, height * 0.58);
rect(0, - buildH, buildW, buildH);
//windows in buildings
fill(255);
for (var x = 0; x < (buildW / 6) - 1; x ++) {
for (var y = 0; y < 200; y ++) {
rect(2 + x * 6, - buildH + 4 + y * 8, 3, 4);
}
}
pop();
}
//changes lake size and color
function changeLake() {
if (cE.lake == true) { //makes larger for water level
lakeWidth = constrain(lakeWidth, 200, 350) + 5;
}
if (cE.lakeColor == true) { // becomes green for algae
colors.lakeh = constrain(colors.lakeh, 30, 55) - 0.75;
}
if (cE.deadz == true) { //becomes dark for dead zones
colors.lakes = constrain(colors.lakes, 50, 100) + 5;
colors.lakeb = constrain(colors.lakeb, 35, 100) - 5;
}
}
//changes color of field
function changeField() {
if (cE.field == true) {
colors.fieldb = constrain(colors.fieldb, 20, 60) - 2;
}
}
//makes row of flowers at base of hills
function makeFlowers() {
for (var i = 0; i < 20; i ++) {
strokeWeight(2);
for (var g = 0; g < 6; g ++) {
if (i % 2) { //alternates colors of flowers to represent biodiversity
stroke(colors.plnflw);
push();
translate(win.ws + i * 25, height * 0.55);
rotate(g * 60);
line(0, 0, 0, 3);
pop();
} else {
stroke(colors.flower);
push();
translate(win.ws + i * 25, height * 0.55);
rotate(g * 60);
line(0, 0, 0, 3);
pop();
}
}
}
if (cE.flowers == true) { //changes color of flowers to single color, removal of biodiversity
colors.flower = colors.plnflw;
}
}
//makes houses for population growth
function makeHouse() {
if (cE.house == true) {
drawHouses(320, height * 0.58, '#FCAD77', '#E8762A', 0.9); //left
drawHouses(375, height * 0.57,'#7BE0FF', '#1CB8E8', 0.8); //right
drawHouses(350, height * 0.59,'#B09DCC', '#9274C1', 1.0); //middle
}
}
//draws houses
function drawHouses(locationX, locationY, colorHouse, colorDoor, houseScale) {
push();
noStroke();
fill(colorHouse);
translate(locationX, locationY);
scale(houseScale);
rect(0, -20, 40, 20);
fill('white');
triangle(-5, -20, 45, -20, 20, -30);
rect(24, -15, 10, 10);
fill(colorDoor);
rect(8, -15, 8, 15);
pop();
}
//makes tree in foreground of landscape
function makeTrees() {
if (cE.trees == true) { //the tree disapears when key is pressed, as key switches variable to false
push();
rectMode(CENTER);
translate(440, 110);
noStroke();
fill(249, 176, 30); //yellow of tree
rect(0, 0, 130, 200, 40, 40, 40, 40);
//tree branches
strokeWeight(15);
stroke(124, 20, 22);
line(0, -50, 0, 225);
strokeWeight(10); //thinner branches
line(0, 80, 40, 40);
line(0, 20, -40, -20);
pop();
}
}
function smokeScreen() { //creates grey screen
if (cE.jeopardy == true) {
noStroke();
jtranslu = constrain(jtranslu, 0, 80) + 15;
fill(150, jtranslu);
rect(win.ws, win.wt, 350, 300)
}
}
//creates all aspects of the room - window, window frame, walls, table, poster, instructions
function drawRoom() {
rectMode(CORNER);
noStroke();
//walls of the room
fill(colors.wall);
rect(-win.dis, -win.dis, win.ws + win.dis, height + win.dis * 2);
rect(width - win.ws, -win.dis, win.ws + win.dis, height + win.dis * 2);
rect(0, height - win.wb, width, win.wb);
rect(0, 0, width, win.wt);
//gray "table" below window where alphabet and definitions appear
fill(colors.table); //medium gray
rect(0, height - 130, width, 130);
//white lines that divide window
stroke(255);
strokeWeight(4);
line(width / 2, win.wt - win.frm, width / 2, height - win.wb + win.frm);
line(win.ws - win.frm, (height - win.wb) / 2 + win.wt / 2, width - win.ws + win.frm, (height - win.wb) / 2 + win.wt / 2);
//white border around the window
strokeWeight(10);
line(win.ws - win.frm, win.wt - win.frm, win.ws - win.frm, height - win.wb + win.frm);
line(width - win.ws + win.frm, win.wt - win.frm, width - win.ws + win.frm, height - win.wb + win.frm);
line(win.ws - win.frm, win.wt - win.frm, width - win.ws + win.frm, win.wt - win.frm);
line(win.ws - win.frm, height - win.wb + win.frm, width - win.ws + win.frm, height - win.wb + win.frm);
drawPoster(); //draws the poster to the left of window
drawInstructions(); //draws instructions on the gray "table"
}
function drawPoster() { //creates poster telling you to refresh
push();
noStroke();
rectMode(CENTER);
angleMode(DEGREES);
textAlign(CENTER);
fill(255);
translate(60, 165);
rotate(-3); //rotates poster slightly
rect(0, 0, 75, 100);
//pin at the top of the poster
fill(colors.table);
ellipse(0, -40, 5, 5);
//writing in gray telling you to refresh
textSize(12);
textLeading(15); //sets leading
var poster = "Refresh\npage\nto\nrestart"; //puts each word on a new line
text(poster, 0, -17);
pop();
}
function drawInstructions() { //creates instructions
textAlign(CENTER);
noStroke();
fill(255);
var textLocation = 375;
textSize(12);
text("Press and hold down keys to see some ABCs of climate change", width / 2, textLocation);
}
//creates alphabet
function typedLetterAlphabet() {
for (var i = 0; i < 26; i ++) {
textAlign(CENTER);
textSize(20);
noStroke();
var alphabetLocation = 400;
var x = map(i, 0, alphabetLetters.length, 50, width - 30);
if (keyCode === 65 + i) { //when key is down, letter turns grey
fill(100);
text(alphabetLetters[i], x, alphabetLocation);
} else { //all letters green when not pressed
fill(colors.type);
text(alphabetLetters[i], x, alphabetLocation);
}
}
}
//creates climate change info
function typedLetterInfo() {
textAlign(CENTER);
textSize(15);
fill(255);
for (var i = 0; i < 26; i ++) {
if (keyCode === 65 + i) {
text(letterInfo[i], width / 2, height - 45);
}
}
}
//when key released, already pressed letters disapear
function keyReleased() { //replaces already clicked letters and their info with spaces in the respective arrays
for (var i = 0; i < 26; i ++) {
if (keyCode == 65 + i) {
alphabetLetters.splice(i, 1, " ");
letterInfo.splice(i, 1, " ");
}
}
}
For this project, I initially wanted to use sound and the typing keyboard in order to create an instrument out of sounds related to the environmental crisis. However, I decided to instead pivot towards creating an interactive and visual display of some “ABCs” of climate change. When you type and hold down a key, a word that starts with that letter will appear, along with that word’s definition. For a little less than half of the letters in the alphabet, something related to the word will happen on the landscape that is visible outside of the window. I wanted to keep the interactions fairly simple, while still conveying some of the effects of climate change, global warming, and our actions as a society.
]]>
var imgPhone;
var imgLogo;
var imgAlb;
var imgBg;
var imgLike;
var song;
var amp;
var fft;
var volhistory = [];
var scrWid = 270; // width of the iphone screen
function preload(){
//preloading images and sound source
imgPhone = loadImage("https://i.imgur.com/Mb4yoMB.jpg?2");
imgLogo = loadImage("https://i.imgur.com/sFAzrlV.jpg?3");
imgAlb = loadImage("https://i.imgur.com/EAsdXzG.png?1");
imgBg = loadImage("https://i.imgur.com/Ztd0x2o.jpg")
imgLike = loadImage("https://i.imgur.com/vX1cA55.jpg?1");
song = loadSound("https://courses.ideate.cmu.edu/15-104/f2019/wp-content/uploads/2019/12/instagram.wav");
song.setVolume(0.5);
}
function setup() {
createCanvas(323, 635);
song.play();
amp = new p5.Amplitude();
fft = new p5.FFT(0.9, 256);
angleMode(DEGREES);
}
function draw() {
background(255);
var vol = amp.getLevel();
volhistory.push(vol);
//setup constrains for bountries
var topWall = scrWid / 2 + 130;
var botWall = height - scrWid / 2 - 100;
var yc = constrain(mouseY, topWall, botWall);
//draw iphne and instagram logo
image(imgPhone, 0, 0);
image(imgLogo, width / 2 - 50, 47);
drawPost();
push();
translate((width - scrWid) / 2, yc-height + scrWid - 40);
drawfft();
pop();
push();
translate((width - scrWid) / 2, -height + scrWid / 2 + yc);
drawAmp();
pop();
push();
translate(width / 2, yc);
drawCir();
pop();
}
function drawfft(){
//setup colors
var colorvar = frameCount;
if (colorvar >= 255) {
colorvar = 0;
} else {colorvar = frameCount;}
var rC = map(colorvar, 0, width, random(255), 255);
var g = map(colorvar, 0, width, random(255), 100);
var b = map(colorvar, 0, height, random(255), 255);
strokeWeight(2);
stroke(rC, g, b, 90);
var spectrum = fft.analyze();
for (var i = 0; i < spectrum.length; i++) {
var y = map(spectrum[i], 0, 250, 0, scrWid);
//spacings
var w = scrWid/64;
line((i)*w, scrWid, (i)*w , y+scrWid);
}
}
function drawAmp(){
//setup colors
var colorvar = frameCount;
if (colorvar >= 255) {
colorvar = 0;
} else {colorvar = frameCount;}
var rC = map(colorvar, 0, width, random(255), 255);
var g = map(colorvar, 0, width, random(255), 100);
var b = map(colorvar, 0, height, random(255), 255);
noStroke();
fill(rC, g, b, 90);
//draw amplitutes
beginShape();
vertex(0, height);
for (var i = 0; i < volhistory.length; i++) {
var y = map(volhistory[i], 0, 1, height, 0);
vertex(i, y);
}
vertex(scrWid, height);
endShape();
//make sure the graphs stays inside the width of the screen
if (volhistory.length > scrWid) {
volhistory.splice(0, 1);
}
}
function drawPost(){
rectMode(CENTER);
stroke(255);
//setup constrains
var topWall = scrWid / 2 + 130;
var botWall = height - scrWid / 2 - 100;
var yc = constrain(mouseY, topWall, botWall);
//draw post images
fill(100);
rect(width/2, yc, scrWid, scrWid);
//draw album profile image
image(imgAlb, 35, yc - scrWid / 2 - 60, 45, 45);
//draw background image
image(imgBg, width / 2 - scrWid / 2, yc - scrWid / 2, scrWid, scrWid);
//draw like image
image(imgLike, 250, yc, 40 + random(0, 5) ,32 + random(0, 5));
//draw post texts
textSize(14);
text("DEAN", 100, yc-scrWid / 2 - 30);
textSize(12);
text(frameCount, 30, yc + scrWid / 2 + 30);
text("likes", 70, yc + scrWid / 2 + 30);
text("#dean #instagram #soundvisualization", 30, yc + scrWid / 2 + 45);
text("All night just wasting time like this", 30, yc + scrWid / 2 + 60);
text("Inside your Instagram", 30, yc + scrWid / 2 + 75);
}
function drawCir(){
noFill();
stroke(255);
strokeWeight(1.5);
beginShape();
for (var i = 0; i < 360; i++) {
var r = map(volhistory[i], 0, 1, 10, 300);
var x = r*cos(i);
var y = r*sin(i);
vertex(x,y);
}
endShape();
//make sure that the the lines don't overlap
if (volhistory.length > 360) {
volhistory.splice(0, 1);
}
}
This Project is about visualization of the song “Instagram”. The main theme is to deliver the message of social media anxiety among teenagers that everything is about the obsession of “likes” and how to gain popularity through posts. I mimicked the Instagram app page to set the base of this project. And as the progression of the song is represented by the intensity of the graphics. The anxiety also levels up when the “like” number goes up over time.
Instructions: move the net with the mouse and click on the litter to remove it from the ocean! Make sure not to harm any of the sea creatures in the process. Collect as many points as possible in one minute.
/* Claire Lee
15-104 Section B
seoyounl@andrew.cmu.edu
Final Project */
var bubbles = [];
var fish = [];
var bottle = [];
var seaTurtle = [];
var candyWrapper = [];
var candyColors = ["pink", "red", "gold", "plum", "dodgerblue"];
var score = 0;
var time = 60;
// arrays to store objects
function setup() {
createCanvas(600, 240);
frameRate(10);
for (i = 0; i < 50; i++) {
bubbleX = random(width);
bubbleY = random(height);
bubbles[i] = blowBubbles(bubbleX, bubbleY);
}
for (k = 0; k < 10; k++) {
fishX = random(width);
fishY = random(0, 220);
fish[k] = makeFish(fishX, fishY);
}
for (j = 0; j < 6; j++) {
bottleX = random(width);
bottleY = random(20, 140);
bottle[j] = makeBottle(bottleX, bottleY);
}
for (l = 0; l < 4; l++) {
seaTurtleX = random(width);
seaTurtleY = random(60, 140);
seaTurtle[l] = makeSeaTurtle(seaTurtleX, seaTurtleY);
}
for (m = 0; m < 4; m++) {
candyWrapperX = random(width);
candyWrapperY = random(20, 140);
candyWrapper[m] = makeCandyWrapper(candyWrapperX, candyWrapperY);
}
}
function keyPressed() {
if (time == 0 & keyCode === 32) {
bubbles = [];
fish = [];
bottle = [];
seaTurtle = [];
candyWrapper = [];
score = 0;
time = 60;
setup();
loop();
}
// pressing space bar will restart the game
}
function draw() {
background(140, 225, 255);
showBubbles();
showFish();
showBottle();
showSeaTurtle();
showCandyWrapper();
showSand();
netW = 40;
netH = 20;
netX = mouseX;
netY = mouseY;
if (netX > width / 2) {
netX = width / 2;
}
if (netY > height - 20) {
netY = height - 20;
}
if (frameCount % 10 == 0 & time > 0) {
time--;
} // using frameCount to create a 60s timer
if (time == 0) {
noStroke();
fill(120, 205, 235, 120);
rect(0, 0, width, height);
noStroke();
fill(0);
textSize(15);
text('The End!', (width / 2) - 50, height / 2 - 30);
text('Final Score: ' + score, width / 2 - 70, height / 2 - 10);
text('Press SPACE to restart', width / 2 - 90, 3 * height / 4);
} // end of game
if(time != 0) {
stroke(0);
strokeWeight(2);
line(netX, 0, netX, netY - (netH / 2));
stroke(255);
strokeWeight(1);
fill(255, 255, 255, 100);
arc(netX, netY, netW, netH, HALF_PI, -HALF_PI);
ellipse(netX, netY, netW / 4, netH);
noStroke();
fill(0);
textSize(15);
text('Score: ' + score, 500, 230);
text('Time Remaining: ' + time + ' s', 325, 230);
} // during game: code for user-controlled net and bottom text
}
var sandSpeed = 0.0004;
function showSand() {
var sand = 0.01;
stroke(235, 220, 180);
strokeWeight(2);
beginShape();
for (var i = 0; i < width; i++) {
var x = (i * sand) + (millis() * sandSpeed);
var s = map(noise(x), 0, 1, 190, 210);
line(i, s, i, height);
// using map() and noise() function to generate randomized sand terrain
}
endShape();
}
function blowBubbles (bubbleX, bubbleY) {
// making bubble object
var bubble = {
x: bubbleX,
y: bubbleY,
radius: random(8, 12),
speed: -0.5,
float: floatBubbles,
draw: drawBubbles
}
return bubble;
}
function floatBubbles() {
// making the bubbles move horizontally and float
this.x += (this.speed * 2);
this.y += this.speed;
if (this.x <= 0) {
this.x = width;
}
if (this.y <= 0) {
this.y = height;
} // bubbles "pop" when they reach top of screen
}
function drawBubbles() {
push();
translate(this.x, this.y);
strokeWeight(1);
stroke(255, 255, 255, 90);
fill(160, 245, 255, 90);
ellipse(1, 10, this.radius, this.radius);
noStroke();
fill(255, 255, 255, 90);
ellipse(-1, 8, 2, 2);
pop();
}
function showBubbles() {
for (i = 0; i < bubbles.length; i++) {
bubbles[i].float();
bubbles[i].draw();
}
}
function makeFish(fishX, fishY) {
// making the fish object
var fish = {
fx: fishX,
fy: fishY,
fishspeed: random(-3, -8),
fishmove: moveFish,
fishcolor: random(100, 150),
fishdraw: drawFish
}
return fish;
}
function moveFish() {
this.fx += this.fishspeed;
if (this.fx <= -10) {
this.fx += width;
this.fy = random(height);
this.fspeed = random(-3, -8);
}
}
function drawFish() {
var tailLength = 8;
noStroke();
fill(this.fishcolor);
ellipse(this.fx, this.fy, 12, 6);
triangle(this.fx + (tailLength / 2), this.fy, this.fx + tailLength, this.fy - 3, this.fx + tailLength, this.fy + 3);
}
function showFish() {
for (i = 0; i < fish.length; i++) {
fish[i].fishmove();
fish[i].fishdraw();
}
}
function makeBottle(bottleX, bottleY) {
//making the bottle object
var bottle = {
bx: bottleX,
by: bottleY,
bspeed: random(-3, -8),
bmove: moveBottle,
bdraw: drawBottle,
}
return bottle;
}
function moveBottle() {
this.bx += this.bspeed;
if (this.bx <= -15) {
this.bx += width;
this.by = random(20, 160);
this.bspeed = random(-3, -8);
if (time != 0) score -= 20;
// if bottle isn't clicked, points are deducted
}
}
function drawBottle() {
bottleW = 7;
bottleH = 14;
bottleCapW = 3;
bottleCapH = 2;
stroke(255);
strokeWeight(0.5);
fill(255, 255, 255, 50);
rect(this.bx, this.by, bottleW, bottleH);
triangle(this.bx, this.by, this.bx + (bottleW / 2), this.by - 4, this.bx + bottleW, this.by);
noStroke();
fill(0, 160, 0);
rect(this.bx + (bottleW / 2) - (bottleCapW / 2), this.by - 5, bottleCapW, bottleCapH);
noStroke();
fill("turquoise");
rect(this.bx, this.by, bottleW, bottleH / 3);
}
function showBottle() {
for (i = 0; i < bottle.length; i++) {
bottle[i].bmove();
bottle[i].bdraw();
}
}
function makeSeaTurtle (seaTurtleX, seaTurtleY) {
//making the sea turtle object
var seaTurtle = {
stx: seaTurtleX,
sty: seaTurtleY,
stspeed: random(-3, -6),
stmove: swimSeaTurtle,
stdraw: hatchSeaTurtle
}
return seaTurtle;
}
function swimSeaTurtle() {
this.stx += this.stspeed;
if (this.stx <= -15) {
this.stx += width;
this.sty = random(60, 140);
this.stspeed = random(-3, -6);
}
}
function hatchSeaTurtle() {
shellW = 30;
shellH = 20;
stroke(110, 150, 75);
strokeWeight(0.5);
fill(175, 200, 90);
ellipse(this.stx - 17, this.sty - 3, 12, 5);
arc(this.stx - 5, this.sty, 7, 15, HALF_PI - QUARTER_PI, PI + HALF_PI - QUARTER_PI, CHORD);
arc(this.stx + 10, this.sty, 5, 15, HALF_PI - QUARTER_PI, PI + HALF_PI - QUARTER_PI, CHORD);
triangle(this.stx + (shellW / 4) + 3, this.sty - 4, this.stx + (shellW / 2) + 3, this.sty - 5, this.stx + (shellW / 4) + 3, this.sty);
stroke(70, 125, 30);
strokeWeight(0.5);
fill(110, 150, 75);
arc(this.stx, this.sty, shellW, shellH, -PI, 0, CHORD);
noStroke();
fill(50, 105, 10);
ellipse(this.stx - 18, this.sty - 2, 1, 1);
stroke(175, 200, 90);
strokeWeight(0.5);
noFill();
arc(this.stx, this.sty - 8, 15, 5, 0, PI, OPEN);
line(this.stx - 4, this.sty - 5.5, this.stx - 6, this.sty - 0.5);
line(this.stx + 4, this.sty - 5.5, this.stx + 6, this.sty - 0.5);
}
function showSeaTurtle() {
for (i = 0; i < seaTurtle.length; i++) {
seaTurtle[i].stmove();
seaTurtle[i].stdraw();
}
}
function makeCandyWrapper() {
// making the caondy wrapper object
var colorRand = Math.floor(random(0,5));
var candyWrapper = {
cwx: candyWrapperX,
cwy: candyWrapperY,
cwspeed: random(-3, -6),
cwmove: floatCandyWrapper,
cwdraw: tossCandyWrapper,
cwcolor: candyColors[colorRand]
}
return candyWrapper;
}
function floatCandyWrapper() {
this.cwx += this.cwspeed;
if (this.cwx <= -15) {
this.cwx += width;
this.cwy = random(60, 140);
this.cwspeed = random(-3, -6);
if (time != 0) score -= 5;
// if candy wrapper isn't clicked, points are deducted
}
}
function tossCandyWrapper() {
candyW = 7;
candyH = 5;
noStroke();
fill(this.cwcolor);
ellipse(this.cwx, this.cwy, candyW, candyH);
triangle(this.cwx - (candyW / 2), this.cwy, this.cwx - (candyW / 2) - 3, this.cwy - 2, this.cwx - (candyW / 2) - 3, this.cwy + 2);
triangle(this.cwx + (candyW / 2), this.cwy, this.cwx + (candyW / 2) + 3, this.cwy - 2, this.cwx + (candyW / 2) + 3, this.cwy + 2);
}
function showCandyWrapper() {
for (i = 0; i < candyWrapper.length; i++) {
candyWrapper[i].cwmove();
candyWrapper[i].cwdraw();
}
}
function mousePressed() {
//making the objects "reset" when pressed on by mouse
for(var i = 0; i < fish.length; i++) {
if (dist(netX - 7, netY, fish[i].fx, fish[i].fy) < 10 & mouseIsPressed && time != 0) {
fish[i].fx = width;
fish[i].fy = random(height);
fish[i].fspeed = random(-3, -8);
score -= 5;
// if fish are clicked, points are deducted
}
}
for(var i = 0; i < seaTurtle.length; i++) {
if (dist(netX - 7, netY, seaTurtle[i].stx, seaTurtle[i].sty) < 15 & mouseIsPressed && time != 0) {
seaTurtle[i].stx = width;
seaTurtle[i].sty = random(height);
seaTurtle[i].stspeed = random(-3, -6);
score -= 25;
// if turtle is clicked, points are deducted
}
}
for(var i = 0; i < bottle.length; i++) {
if (dist(netX - 7, netY, bottle[i].bx, bottle[i].by) < 10 & mouseIsPressed && time != 0) {
bottle[i].bx = width;
bottle[i].by = random(height);
bottle[i].bspeed = random(-3, -6);
score += 20;
}
}
for(var i = 0; i < candyWrapper.length; i++) {
if (dist(netX - 7, netY, candyWrapper[i].cwx, candyWrapper[i].cwy) < 10 & mouseIsPressed && time != 0) {
candyWrapper[i].cwx = width;
candyWrapper[i].cwy = random(height);
candyWrapper[i].cwspeed = random(-3, -8);
score += 5;
}
}
}
For my final project, I made an interactive game that’s supposed to serve as a commentary on the ocean pollution crisis. The game itself is a metaphor: we have a limited amount of time to solve the problem of ocean pollution, and finding a solution requires awareness and active work.
Working more with using objects in code and figuring out how to make objects interactive was a really fun process. I also liked testing out my game and thinking about the ways to improve the user-oriented aspects of my game design.
]]>I created a smart watch interface for someone with anxiety and panic disorder. It reads someone’s pulse and breath rate and can detect when someone is having a panic attack. I used real-time biometric data from a pulse sensor and a wind sensor and hooked it up to an ardiuno, then I used serial communication to send the data and trigger & display different calming graphics using p5.js. I will go through the project interface and design first, then I will show you how I went about doing it.
Depending on the kind of panic attack you are having, there are 3 modes/techniques. The good thing is that any of these techniques would work during a panic attack. I weighted certain techniques to be triggered based on someone’s sensor data. But for the purpose of the scope of this project, and because this requires more knowledge, time and research, I used keyboard keys to to demonstrate the different interfaces.
These techniques and methods were based on my research on medical articles here, here, and here.
“Recently I found that trying to focus on something else such as counting numbers… works, because it requires concentration.”
Melanie Luxenberg
I used the step by step guide provided by NYU’s physical computing department here to learn how to use ardiuno and p5.js together. If you follow that guide, you can know how to download and run the project. You will need to download the P5.js complete library, the P5.serialserver, and the ardiuno software.
You can download all the files you need to run the project here:
galsanea_FinalProject_allFiles
If you just want to see the p5.js interaction, I modified the project to run on WordPress using mouse X location to simulate breathing pattern, and mouse Y location to simulate pulse patterns.
Use the following to replace the sensor data interaction:
keys:
b: breathing blobs interface
c: counting interface
g: game interface
any other key: regular clock interface
mouse:
x: move mouse in x directions to simulate breathing
y: move mouse in y directions to simulate heart beat
/*
Ghalya Alsanea
Section B
galsanea@andrew.cmu.edu
Final Project
normal mode:clock
date
sensor data: heart rate with beating heart
panic mode: deep breathing --- moving blob objects reacting to your breathing
distraction technique --- use pingpong-like game to distract you
counting technique --- count till a number to calm down
*/
// ----------------------------------------------------------------------------
// overall global variables
var proportion = 500; //canvas proportion
var MX, MY; //mouse x and y locations
// ----------------------------------------------------------------------------
//SENSOR global variables
var pulseSensor; //global variable for pulse sensor data
var breathSensor; //global variable for breath sensor data
var toggle = false; //toggle to help sort the incoming sensor data from the serial
var pulseData = []; //array container for incoming pulse data
// ----------------------------------------------------------------------------
// SERIAL global variables
var serial; // variable to hold an instance of the serialport library
var portName = 'COM11'; // fill in your serial port name here
var inData; // for incoming serial data. NOTE: it comes a byte at a time
var options = {baudrate: 9600}; // change the data rate to whatever you wish
// ----------------------------------------------------------------------------
// CLOCK global variables
var diameter = proportion * 0.75; //clock diameter
//configure clock hands based on clock diameter
var secHand = diameter * 0.45;
var minHand = diameter * 0.35;
var hourHand = diameter * 0.2;
// ----------------------------------------------------------------------------
// BLOBS global variables
var blobs = []; //used to store instances of blob objects
var change = 0; //stores the rate of rotation and the y coordinate for noise later
var colorsPalette; //color palette for blob objects
var numBlobs = 120; //number of blobs to make
// ----------------------------------------------------------------------------
// GAME global variables
var ballX; //ball x position
var ballY; //ball y position
var ballSize = 40; //diameter of ball
var xvel; //horizontal velocity
var yvel; //vertical velocity
var d; //distance between mouse and circle center
var dangerX; //danger zone
var ballColor; //what to color the ball
var score; //keep score of hits
var scores = []; //keep a list of all final scores to get the highest score
// ----------------------------------------------------------------------------
// CLICKER global variables
var gClickCount = 0; //mouse click counter
var inc = 20; //x increments
var yInc = 100; //y increments
var yPos1 = 25 //top y position
var yPos2 = yPos1 + 50; //bottom y position
// ----------------------------------------------------------------------------
function setup() {
createCanvas(proportion, proportion);
// setupSerial(); ..comment out to disable serial
setupBlobs();
setupGame();
}
// ----------------------------------------------------------------------------
function draw() {
//constrain mouse x and y to within the canvas
MX = constrain(mouseX, 0, width);
MY = constrain(mouseY, 0, height);
if (key === "b") {
//draw the blobs
displayBlobs();
} else if (key === "g") {
//draw the game
drawGame();
} else if (key === "c") {
//draw clicker
displayClicker();
} else {
clockDisplay();
}
}
////////////////////////////////////////////////////////////////////////
//**************************** SERIAL ********************************//
////////////////////////////////////////////////////////////////////////
function setupSerial() {
serial = new p5.SerialPort(); // make a new instance of the serialport library
serial.on('data', serialEvent); // callback for when new data arrives
serial.open(portName); // open the serial port
}
function serialEvent() {
// read a byte from the serial port, convert it to a number
// NOTE: the inData comes in a byte at a time (so range is 0-255)
inData = serial.read();
// sort the data incoming by distributing every other byte to their respective variable
// serial data comes in 2 at a time, so using a toggle you can store the data correctly
if (toggle) {
pulseSensor = inData;
} else {
breathSensor = inData;
}
toggle = !toggle;
}
////////////////////////////////////////////////////////////////////////
//*********************** base/CLOCK Display *************************//
////////////////////////////////////////////////////////////////////////
function clockDisplay() {
background(255, 228, 225); //mistyRose
noStroke();
//show the date
dateDisplay();
//for testing: using mouse data instead of sensor data
var size = map(MY, 0, height, 20, 40);
var BPM = 88;
// // for serial: map heart size to pulse sensor data
// var size = map(pulseSensor, 0, 255, 20, 40);
//
// //calculate beats per min (BPM) based on average pulse data
// pulseData.push(pulseSensor);
// //total pulse values read
// var total = 0;
// //find the total of all the readings
// for(var i = 0; i < pulseData.length; i++) {
// total += pulseData[i];
// }
// //divide by the length of the data to get the average
// var avg = total / pulseData.length;
// //map the average number onto a range of BPM hear rates
// var BPM = map(avg, 0, 255, 70, 130);
//
// //only read the last 100 readings
// if (pulseData.length > 100) {
// pulseData.shift();
// }
//show the heart rate data
heartDisplay(size, BPM);
//draw from the center of the canvas
push();
translate(width / 2, height / 2);
//draw clock base
fill(255);
circle(0, 0, diameter);
//convert time to angles
//subtract half a circle so the clock starts at the top, rather than 6 o'clock
var s = map(second(), 0, 60, 0, TWO_PI) - PI;
var m = map(minute(), 0, 60, 0, TWO_PI) - PI;
//the % takes the remainder of the hours/12, which makes the dif between AM/PM
var h = map((hour() % 12), 0, 12, 0, TWO_PI) - PI;
//draw the hands
drawClockHands(s, secHand, 1); //seconds hand
drawClockHands(m, minHand, 3); //minutes hand
drawClockHands(h, hourHand, 5); //hour hand
//draw the ticks every 6 degrees (6 * 60 = 360)
for (i = 0; i < 360; i += 6) {
//make every 5th tick thicker
if(i % 30 == 0) {
strokeWeight(2);
} else {
strokeWeight(1);
}
push();
rotate(radians(i));
line(0, diameter / 2, 0, secHand + 5);
pop();
}
pop()
}
//draw the clock hands
function drawClockHands(angle, hand, weight) {
stroke("PaleVioletRed");
push();
rotate(angle);
strokeWeight(weight);
line(0, 0, 0, hand);
pop();
}
//display today's date
function dateDisplay() {
var d = day();
var m = month();
var y = year();
fill("PaleVioletRed");
textAlign(LEFT);
textSize(18);
text(m + "." + d + "." + y, 10, height - 15);
}
//display heart rate with heart graphic
function heartDisplay(size, BPM) {
textSize(12);
textAlign(CENTER);
text(floor(BPM), width - 50, height - 60);
drawHeart(width - 50, height - 50, size);
}
function drawHeart(x, y, size) {
beginShape();
vertex(x, y);
bezierVertex(x - size / 2, y - size / 2, x - size, y + size / 3, x, y + size);
bezierVertex(x + size, y + size / 3, x + size / 2, y - size / 2, x, y);
endShape(CLOSE);
}
////////////////////////////////////////////////////////////////////////
//******************** breathing BLOB Display ************************//
////////////////////////////////////////////////////////////////////////
function setupBlobs() {
//create color paletter for the blob objects
colorsPalette = [color(146, 167, 202,30),
color(186, 196, 219,30),
color(118, 135, 172,30),
color(76, 41, 81,30),
color(144, 62, 92,30),
color(178, 93, 119,30),
color(215, 118, 136,30),
color(246, 156, 164,30)];
// create new blob object for every num of blobs
for (var i = 0; i < numBlobs; i++){
var temp = makeBlob(i + 0.1, i, i * random(90),
colorsPalette[floor(random(colorsPalette.length))]);
blobs.push(temp);
}
}
function displayBlobs() {
background(240, 200, 240, 30);
//for testing: using mouse data instead of sensor data
var addToRadius = map(MX, 0, width, 0, 100);
// //for serial: map the breathing sensor data to blob radius
// //it's inversely mapped because when you breath out it decreases and vice versa
// var addToRadius = map(breathSensor, 255, 0, 0, 100);
for(var i = 0; i < blobs.length; i++){
blobs[i].r = i + addToRadius;
blobs[i].draw(change);
}
//create rotation change and noise change
change+=0.01;
}
function makeBlob(radius, roughness, angle, color){
var blob = {r: radius, // radius of blob
x: width / 2, // x position of blob
y: height / 2, // y position of blob
rough: roughness, // magnitude of how much the circle is distorted
ang: angle, // how much to rotate the circle by
c: color, // color of the blob
draw: drawBlob, //draw the blobs
}
return blob;
}
function drawBlob(change) {
noStroke();
fill(this.c); //color to fill the blob
push();
translate(this.x, this.y); //move to xpos, ypos
rotate(this.ang + change); //rotate by this.angle+change
beginShape(); //begin a shape based on the vertex points below
//create vertex points
var off = 0;
for (var i = 0; i < TWO_PI; i += 0.1) {
var offset = map(noise(off, change), 0, 1, -this.rough, this.rough);
var R = this.r + offset;
var x = R * cos(i);
var y = R * sin(i);
vertex(x, y);
off += 0.1;
}
endShape();
pop();
}
////////////////////////////////////////////////////////////////////////
//************************ GAME display ******************************//
////////////////////////////////////////////////////////////////////////
function setupGame() {
noStroke();
//ball starts at the center of the canvas
ballX = width / 2;
ballY = height / 2;
//define danger zone
dangerX = width / 4;
//set x and y velocity
xvel = 5;
yvel = 0;
//set score
score = 0;
}
function drawGame() {
background (100);
//draw the hitting paddle
strokeWeight(5);
stroke(255);
line(mouseX, mouseY + 10, mouseX, mouseY - 10);
//instructions
noStroke();
fill(255);
textAlign(LEFT);
writeText(20, "Let's play a game!", 20, 25);
writeText(12, "Don't let the ball go left of the screen", 20, 50);
//define the playing area
fill(0);
rect(width / 2, 0, width / 2, height);
//warn if ball is too close to edge
if(ballX - ballSize / 2 <= dangerX & xvel < 0){
ballColor = "yellow";
} else {
ballColor = "white";
}
//draw the circle
fill (ballColor);
circle(ballX, ballY, ballSize);
//makes the ball move in x and y directions
ballX = ballX + xvel;
ballY = ballY + yvel;
//When it hits the right edge of the canvas,
//it reverses horizontal velocity
if (ballX >= width - ballSize / 2) {
xvel = -1 * xvel;
}
//when it hits the bottom or top edge of the canvas,
//it reverses vertical velocity
if (ballY <= ballSize / 2) {
yvel = -1 * yvel;
} else if (ballY >= height - ballSize / 2) {
yvel = -1 * yvel;
}
//find the distance between ball center and mouse
d = dist(mouseX, mouseY, ballX, ballY);
//if (1) the mouse hits the edge of the ball
//and (2) the mouse is on the left half of the canvas
//and (3) the x velocity is negative
//then switch directions and get a random yvel.
if (d <= ballSize / 2 & mouseX <= width / 2 && xvel < 0){
yvel = random(-3, 3);
xvel = -1 * xvel;
yvel = -1 * yvel;
score = score + 1;
}
//keep current score
fill("white");
writeText(30, score, width - 50, height - 50);
//reset game if th eball went to far out the canvas
if (ballX < -150) {
resetGame();
}
}
//reset game
function resetGame() {
scores.push(score) //push final score to scores
background (0, 51, 102);
fill(255);
textAlign(LEFT);
writeText(80, "IT'S OK!", 10, height / 4);
writeText(30, "click to try again", 20, height / 2);
//cureent score
writeText(20, "score: " + score, 20, height / 2 + 50);
//highest score
writeText(20, "best score: " + max(scores), 20, height / 2 + 100);
if (mouseIsPressed){
setupGame();
}
}
function writeText(size, words, x, y) {
textSize(size);
text(words, x, y);
}
////////////////////////////////////////////////////////////////////////
//*********************** COUNT display ******************************//
////////////////////////////////////////////////////////////////////////
function displayClicker() {
background("pink");
if (gClickCount === 0) {
noStroke();
fill(0);
textAlign(LEFT);
textSize(20);
text("tap me and count to fives...", 20, 50);
}
stroke (0);
strokeWeight(3);
//everytime you click, draw a line
for(var i = 0; i < gClickCount; i++){
//variable to show which line the tickmarks are on
var a = floor((i * inc + inc - 1) / width);
//x and y locations depending on which line
var x = i * inc + inc - width * a;
var y1 = yPos1 + a * yInc;
var y2 = yPos2 + a * yInc;
if ( (i + 1) % 5 === 0) {
//draw diagonal line every fifth tick
line(x, y1, x - 5 * inc, y2);
} else {
//otherwise draw a straight line
line(x, y1, x, y2);
}
}
}
function mousePressed() {
//everytime you click, you increase the amount of times the loop runs
gClickCount++;
}
function keyPressed() {
//reset gClickCount to zero whenever a keyboard key is pressed
gClickCount = 0;
}
]]>// Nadia Susanto
// nsusanto@andrew.cmu.edu
// Section B
// Final Project
var terrainSpeed = 0.0005;
var terrainDetail = 0.003;
var clouds = [];
var pipes = [];
var birdY = 300;
var birdX = 65;
var birdGravity = 0.75;
var birdVelocity = 0;
var birdUpForce = -18;
var secs = 0;
function setup() {
createCanvas(600, 600);
background(135,235,250);
pipes.push(makePipe());
//initalizing clouds
for (var x = 0; x < 5; x++) {
var cx = random(width);
clouds[x] = makeClouds(cx);
}
frameRate(60);
}
function draw() {
//blue sky
background(135,235,250);
//show mountains and waves
makeMountains();
makeWaves();
//show clouds
addNewClouds();
updateAndDisplayClouds();
removeCloudsOutofView();
//pipes
addNewPipes();
updateAndDisplayPipes();
//bird
drawBird();
updateBird();
//collision detection for bird with pipe
topCollision();
bottomCollision();
if (frameCount % 60 == 0) {
secs += 1;
}
//score
fill("black");
textSize(30);
textAlign(LEFT);
text("TIME = " + secs, 5, 50);
}
//collision for bird with top pipe
function topCollision() {
for (var i = 0; i < pipes.length; i++) {
if ((birdY < pipes[i].top + 30) & (birdX > pipes[i].x && (birdX < pipes[i].x + pipes[i].w))) {
gameOver();
}
}
}
//collision for bird with bottom pipe
function bottomCollision() {
for (var i = 0; i < pipes.length; i++) {
if ((birdY > pipes[i].top + 150) & (birdX > pipes[i].x && (birdX < pipes[i].x + pipes[i].w))) {
gameOver();
}
}
}
//game over function that stops the game when triggered
function gameOver() {
fill("red");
textAlign(CENTER);
textSize(60);
text("GAME OVER", width/2, height/2);
noLoop();
}
//constantly calling the pipes
function updateAndDisplayPipes() {
for (var i = 0; i < pipes.length; i++) {
pipes[i].move();
pipes[i].draw();
}
}
function addNewPipes() {
if (frameCount % 150 == 0) {
pipes.push(makePipe());
}
}
//moving the pipes right to left
function movePipe() {
this.x -= this.speed;
}
function drawPipe() {
noStroke();
//top pipe
fill(112,186,45);
rect(this.x, 0, this.w, this.top);
rect(this.x - 5, this.top - 2, this.w + 10, 20, 10);
//bottom pipe
rect(this.x, height, this.w, this.top - height + 150);
rect(this.x - 5, this.top + 135, this.w + 10, 20, 10);
}
//object for pipe
function makePipe() {
var pipe = {x: width,
w: 50,
top: random(50, height/2),
speed: 1.5,
move: movePipe,
draw: drawPipe
}
return pipe;
}
//drawing the bird
function drawBird() {
//legs
fill(0);
ellipse(birdX - 10, birdY + 15, 3, 10);
ellipse(birdX + 10, birdY + 15, 3, 10);
//halo
noFill();
stroke("gold");
ellipse(birdX, birdY - 20, 40, 5);
//body
fill("yellow");
ellipse(birdX, birdY, 35, 35);
//eye
fill(0);
ellipse(birdX + 5, birdY - 5, 5);
//beak
fill("orange");
triangle(birdX + 18, birdY - 4, birdX + 23, birdY, birdX + 18, birdY + 4);
}
//implementing velocity and gravity
function updateBird() {
birdVelocity += birdGravity;
//air resistance
birdVelocity *= 0.9;
birdY += birdVelocity;
//making sure it doesnt go beyond top and bottom of canvas
if (birdY > height) {
birdY = height;
birdVelocity = 0;
}
if (birdY < 0) {
birdY = 0;
birdVelocity = 0;
}
}
//adding up force to bird velocity
function birdUp() {
birdVelocity += birdUpForce;
}
//if space bar pressed, trigger the bird to go up
function keyPressed() {
if (key == ' ') {
birdUp();
}
}
//constantly calling the clouds
function updateAndDisplayClouds() {
for (var x = 0; x < clouds.length; x++){
clouds[x].move();
clouds[x].draw();
}
}
function removeCloudsOutofView() {
var cloudsKeep = [];
for (var x = 0; x < clouds.length; x++) {
if (clouds[x].x > 0) {
cloudsKeep.push(clouds[x]);
}
}
clouds = cloudsKeep; //remember the clouds
}
function addNewClouds() {
var cloudProb = 0.01;
//if random number less than probability then a new cloud is shown
if (random(0, 1) < cloudProb) {
clouds.push(makeClouds(width));
}
}
function cloudsMove() {
//move the clouds from right to left
this.x -= this.speed;
}
function drawClouds() {
//draw the white clouds
fill("white");
ellipse(this.x, this.y, this.width, 10);
ellipse(this.x - 25, this.y - 10, 35, 30);
ellipse(this.x + 25, this.y - 10, 30, 30);
ellipse(this.x + 5, this.y - 15, 40, 25);
}
function makeClouds(cloudX) {
//creating object for cloud
var cloud = {x: cloudX,
y: 50,
width: random(50, 100),
speed: 0.50,
move: cloudsMove,
draw: drawClouds}
return cloud;
}
//adopted the terrain starter code to make mountains in background
function makeMountains() {
noStroke();
fill(127,221,136);
beginShape();
for (var x = 0; x < width; x++) {
var t = (x * terrainDetail*3) + (millis() * terrainSpeed);
var y = map(noise(t), 0, 1, height/4, height/3);
vertex(x, y);
}
vertex(width, height);
vertex(0, height);
endShape(CLOSE);
}
//adopted the terrain starter code to make ocean waves
function makeWaves() {
noStroke();
fill(1, 108, 194);
beginShape();
for (var x = 0; x < width; x++) {
var t = (x * terrainDetail/3) + (millis() * terrainSpeed/2);
var y = map(noise(t), 0, 1, height/2, height);
vertex(x, y);
}
vertex(width, height);
vertex(0, height);
endShape(CLOSE);
}
For my final project I made angel bird! I have always been a gamer at heart and love competition whether it be beating my peers or my personal bests. Originally for my proposal I wanted to do scream go hero but had difficulties with voice recognition. Flappy bird was my backup and I am really happy with the way it turned out. I used my knowledge of objects to create the generative backgrounds and the pipes, and I believe my angel bird with a halo is pretty cute.
How the game works:
The game automatically starts and all you have to do is press the space bar to make the bird go up. Instead of scoring, you want to beat your personal best times as you want to see how long you can go without the dreaded “GAME OVER.” Enjoy!
]]>//Sewon Park
//sewonp@andrew.cmu.edu
//Final Project
//Section B
var counter = 0;
var oceanamp = 100;
var oceanspeed = 0.001;
var x = 150;
var polarbearURL = "https://i.imgur.com/dQOjRSb.png"
function preload() {
polarbear = loadImage(polarbearURL);
}
function setup() {
createCanvas(600, 400);
background(180, 240, 250);
fill(150, 75, 0);
rect(380, 365, 10, 30);
rect(440, 365, 10, 30);
rect(500, 365, 10, 30); //Treetrunks
fill(0, 255, 0)
triangle(370, 365, 385, 325, 400, 365);
triangle(430, 365, 445, 325, 460, 365);
triangle(490, 365, 505, 325, 520, 365); //Tree Leaves
}
function draw() {
fill(255, 100, 0);
rect(400, 30, 100, 50); //button
fill(255);
textSize(13);
text("Click", 435, 50); //click text on button
strokeWeight(2);
stroke(255);
line(300, 0, 300, 400); //Divider down the middle
noStroke();
fill(255, 204, 51)
ellipse(150, 100, 30, 30) //sun
fill(255);
rect(50, 250, 200, 200); //Iceberg
noFill();
rect(360, 330, 40, 20);
rect(420, 330, 40, 20);
rect(480, 330, 40, 20); //Windows
ocean();
imageMode(CENTER);
image(polarbear, 150, 240, 50, 50);
if (counter == 1) {
fire1();
}
if (counter == 2) {
fire2();
}
if (counter == 3) {
fire3();
}
if (counter == 4) {
factory();
ocean2(); //Increase water levels afer iceberg sinks
newsun(); //New sun after factory is built
textSize(30)
text("GAME OVER", 210, 200)
}
}
function ocean() {
fill(0, 0, 250);
noStroke();
beginShape();
for (var x = 0; x < width/2; x++) {
var position2 = x * oceanamp + (millis() * oceanspeed);
var y = map(noise(position2), 0, 10, height/1.4, height);
vertex(x, y);
}
vertex(width/2, height);
vertex(0, height);
endShape(); //The Ocean
}
function ocean2() {
fill(0, 0, 250);
noStroke();
beginShape();
for (var x = 0; x < width/2; x++) {
var position2 = x * oceanamp + (millis() * oceanspeed);
var y = map(noise(position2), 0, 10, height/2, height);
vertex(x, y);
}
vertex(width/2, height);
vertex(0, height);
endShape(); //The new ocean after the iceberg sinks
}
function newsun() {
fill(255, 51, 51);
ellipse(150, 100, 60, 60); //bigger and stronger sun to appear
}
function factory() {
fill(100);
rect(350, 300, 180, 200); //factory body
triangle(350, 300, 410, 250, 410, 300);
triangle(410, 300, 470, 250, 470, 300);
triangle(470, 300, 530, 250, 530, 300); //Ceiling
fill(250);
rect(360, 330, 40, 20);
rect(420, 330, 40, 20);
rect(480, 330, 40, 20); //Windows
}
function fire1() {
fill(255,0,0);
ellipse(385, 375, 46, 46);
triangle(361, 375, 385, 310, 409, 375)
fill(255, 255, 0);
ellipse(385, 375 ,30 ,30); //fire 1
}
function fire2() {
fill(255,0,0);
ellipse(445, 375, 46, 46);
triangle(421, 375, 445, 310, 469, 375)
fill(255, 255, 0);
ellipse(445, 375, 30, 30); //fire 2
}
function fire3() {
fill(255,0,0);
ellipse(505,375,46,46);
triangle(481,375,505,310,529,375);
fill(255,255,0);
ellipse(505,375,30,30); //fire 3
}
function mousePressed() {
if (mouseX < 500 & mouseX > 400 && mouseY < 80 && mouseY > 30 ){
counter = counter + 1;
} //Counter that keeps track of elemets to be appeared when button is clicked
}
As I initially had trouble selecting a theme for my final project, I decided to follow Professor Dannenburg’s suggestion to make a climate change themed work. As I appreciated art not only for its aesthetics but also for the message it sends to the public, I thought creating a project that sends a message about global warming could be rewarding.
I wanted to show how the actions of humans can have a detrimental impact on the livelihood of polar bears. Although it was already a common topic, I wanted to express that human beings ultimately have the power to preserve or destroy the environment that these animals were dependent on.
As such, I created a “button” that emulated real life actions that caused global warming and the destruction of the ice caps. Each click creates a fire that would burn the trees and finally create a factory. After the action sequence is completed, the player will have destroyed the ozone layer, causing the ice cap to melt and kill the polar bear.
// Xiaoyu Kang & Xu (Claire) Xu
// Section B
// xkang@andrew.cmu.edu & xux1@andrew.cmu.edu
// Final Project
var hatShapeX = 130; //BY BOTH XIAOYU AND XU
var hatShapeY = 128;
var heartShapeX = 205;
var heartShapeY = 128;
var moneyShapeX = 270;
var moneyShapeY = 140;
var textboxShapeX = 330;
var textboxShapeY = 128;
var backgroundColor = 0;
var gameStart = false;
var badEnd = false;
var goodEnd = false;
var img1 = [];
var imageCount = 0;
var testLinks = ["https://i.imgur.com/VWoGtbO.jpg",
"https://i.imgur.com/OpLGpha.jpg",
"https://i.imgur.com/1Ik4bPx.jpg",
"https://i.imgur.com/vM6ZK9c.jpg",
"https://i.imgur.com/8J6Gxdo.jpg",
"https://i.imgur.com/yk3tV27.jpg",
"https://i.imgur.com/FH9qhm4.jpg",
"https://i.imgur.com/E7a5TDu.jpg",
"https://i.imgur.com/9vRGrKE.jpg",
"https://i.imgur.com/C4XhQaI.jpg",
"https://i.imgur.com/NgIAvpS.jpg",
"https://i.imgur.com/QRXsh9S.jpg",
"https://i.imgur.com/04EkVKf.jpg",
"https://i.imgur.com/0dOYwZv.jpg",
"https://i.imgur.com/xc56D6n.jpg",
"https://i.imgur.com/0sk94O3.jpg"];
var questionCount = 0;
let testQuestions = ["I can't code for life,", " but do you want to collaborate on this 15104 project?",
"Did you hear about the history exam tomorrow?", "I can’t believe it’s on EVERYTHING!",
"I heard Vivian got a 104/100", " on the recent physics test!",
"I really need to get some sleep,", "will you sign me in for lecture today?",
"There’s a frat party tonight,", "do you want to go and hang out?",
"OMG I heard there’s a new pho place downtown,", "are you interested??",
"There’s a library part time job on Handshake", "let’s apply together!",
"Let’s play","League of Legends tonight!",
"<Introduction to Javascript> is a required textbook,", "please get it by next class.",
"Re: Please see the HW attached,", "submit all to autolab by tonight at midnight.",
"I noticed that you have not paid attention in class.", "Is there something wrong?",
"Class, this project", "will be due in a week’s time.",
"Re: Would you like to be a generous supporter", "of the CMU community by becoming a donor?",
"The total amount of your food will be $15.76.", "Paid in card or cash?",
"New: Pittsburgh popcorn & smokey barbeque", "flavored mac and cheese",
"It’s 2am in the mornin’,","why are y’all still workin’?"]
var imageposX = 115;
var imageposY = 80;
var questionposY = 380;
var answerCount = 0;
let answerX = ["Um… Sure?",
"What exam?",
"Ha, I can do better",
"Sure, go sleep",
"Sure, I’ll go!",
"YES LET’S GO",
"Sure I’ll join",
"I'll grab my laptop",
"I’ll buy it",
"Grinds through HW",
"It won’t happen again",
"Complete the task",
"Fine I’ll donate",
"Takes out debit card",
"Gives it a try",
"We have a deadline"];
let answerY = ["I prefer someone better",
"Ya I studied all night",
"She must’ve worked hard",
"I’m also not going",
"Nah, I’ll pass",
"No I’m broke",
"I’m too lazy",
"My heart is in the work",
"I’ll find a fake one",
"Ignores this email",
"Your class is boring",
"Request for extension",
"I’m not generous",
"Gets something cheaper",
"Gets something normal",
"Cries hysterically"];
var answerposX = 90;
var answerposY = 430;
var timeTracker = 0;
var choices = [0,1,2,3,4,5,6,7,8,9,10,11,12,13,14,15];
var choices_A = [];
var academicsHP = 50;
var sanityHP = 50;
var financeHP = 50;
var socialHP = 50;
var HPposX = 35;
var HPposY = 40;
var logoPosX = 15;
var logoPosY = 30;
var subtract = true;
var monthCount = 0;
var star = [];
var cloud = [];
var crow = [];
function preload(){ //BY XU
for (var i = 0; i < testLinks.length; i++){
img1[i] = loadImage(testLinks[i]);
}
}
function setup(){ //BY XIAOYU
createCanvas(480,480);
frameRate(10);
//setup stars that will display at night
for (var i = 0; i < 50; i ++) {
var starX = random(width);
var starY = random (0, height/2);
star[i] = makeStar(starX, starY);
}
//setup clouds that will display at morning
for (var i = 0; i < 10; i ++) {
var cloudX = random(width);
var cloudY = random (0, height/2);
cloud[i] = makeCloud(cloudX, cloudY);
}
//setup crows that will display at afternoon
for (var i = 0; i < 20; i ++) {
var crowX = random(width);
var crowY = random (0, height/2);
crow[i] = makeCrow(crowX, crowY);
}
}
function draw(){ //BY XU
//starting page text
background(0);
fill("yellow");
textSize(70);
textFont('Courier');
textAlign(CENTER);
text('REIGNS', width/2, height/2);
fill("white");
textSize(20);
text('- CMU EDITION -', width/2, height/2 + 50);
textSize(15);
text('Press Mouse To Start', width/2, height - 70);
//starting page logo
fill("yellow");
rectMode(CENTER);
rect(width/2, 130, 250, 60);
//academics logo
noStroke();
fill(0);
beginShape();
vertex(hatShapeX, hatShapeY);
vertex(hatShapeX + 20, hatShapeY - 5);
vertex(hatShapeX + 40, hatShapeY);
vertex(hatShapeX + 20, hatShapeY + 5);
vertex(hatShapeX, hatShapeY);
endShape();
rectMode(CORNER);
rect(hatShapeX, hatShapeY, 1, 10);
rect(hatShapeX + 10, hatShapeY, 20, 10);
//sanity logo
circle(heartShapeX, heartShapeY, 15);
circle(heartShapeX + 10, heartShapeY, 15);
triangle(heartShapeX - 8, heartShapeY, heartShapeX + 18, heartShapeY, heartShapeX + 6, heartShapeY + 15);
//finance logo
textSize(33);
text('S', moneyShapeX, moneyShapeY);
rect(moneyShapeX - 1.5, moneyShapeY - 22, 3, 25);
//social life logo
ellipse(textboxShapeX, textboxShapeY, 30, 20);
triangle(textboxShapeX, textboxShapeY, textboxShapeX + 10, textboxShapeY, textboxShapeX + 10, textboxShapeY + 15);
if(gameStart){
backgroundChange();
fill(backgroundColor);
rectMode(CENTER);
rect(width/2, height/2, 480, 480);
//display clouds when background is light gray
if (timeTracker == 0) {
displayCloud();
}
//display crows when background is dark gray
if (timeTracker == 1) {
displayCrow();
addCrow();
removeCrow();
}
//display stars when background is black
if (timeTracker == 2) {
displayStars();
}
gameInterface();
}
//check if the game has ended
checkStatus();
//bad end
if (badEnd){
gameStart = false;
background(0);
fill("yellow");
textSize(70);
textFont('Courier');
textAlign(CENTER);
text('GAME OVER', width/2, height/2);
fill("white");
textSize(20);
text('- SADLY YOU DIDNT GRADUATE -', width/2, height/2 + 50);
textSize(15);
text('Refresh Page to Start Over', width/2, height - 70);
}
//goodEnd
if (goodEnd){
gameStart = false;
background(0);
fill("yellow");
textSize(50);
textFont('Courier');
textAlign(CENTER);
text('CONGRATULATIONS', width/2, height/2);
fill("white");
textSize(20);
text('- YOU SUCCESFULLY GRADUATED CMU -', width/2, height/2 + 50);
textSize(15);
text('Refresh Page to Play Again', width/2, height - 70);
}
}
function mouseClicked(){ //BY XU
gameStart = true;
}
function gameInterface(){ //BY XU
//frame
fill("yellow");
rectMode(CENTER);
rect(width/2, height/2 - 35, 280, 280);
//implement images
putImage();
//implement question
putQuestion();
//implement answers
putAnswers();
putDate();
hpTracker();
}
function putImage(){ //BY XIAOYU
image(img1[imageCount], imageposX, imageposY);
}
function putQuestion(){ //BY XIAOYU
fill("white");
textFont('Courier');
textAlign(CENTER);
textSize(12.5);
text(testQuestions[questionCount], width/2, questionposY);
text(testQuestions[questionCount + 1], width/2, questionposY + 15);
}
function putAnswers(){ //BY XIAOYU
//left side
fill("yellow");
textFont('Courier');
textSize(12.5);
text("A. " + answerX[answerCount], answerposX, answerposY);
text("B. " + answerY[answerCount], answerposX + 280, answerposY);
}
function putDate(){ //BY XIAOYU
fill("yellow");
textFont('Courier');
textAlign(CENTER);
textSize(12.5);
text(monthCount*2 + " months survived", width/2, 460);
}
function keyTyped(){ //BY XU
if (key === 'a'){
//time passes
timeTracker ++;
imageCount = floor(random(choices));
questionCount = imageCount *2;
answerCount = imageCount;
subtractHP();
monthCount ++;
} else if (key === 'b'){
//time passes
timeTracker ++;
imageCount = floor(random(choices));
questionCount = imageCount *2;
answerCount = imageCount;
subtractHP();
monthCount ++;
}
if (timeTracker >= 3){
timeTracker = 0;
}
}
function notRepeat(){ //BY XU
while (choices_A.includes(imageCount)){
imageCount = floor(random(choices));
}
if(choices_A.length < 6){
choices_A.push(imageCount);
}else {
choices_A.shift();
choices_A.push(imageCount);
}
}
function hpTracker(){ //BY XIAOYU
//academics logo
noStroke();
fill("yellow");
beginShape();
vertex(logoPosX, logoPosY);
vertex(logoPosX + 20, logoPosY - 5);
vertex(logoPosX + 40, logoPosY);
vertex(logoPosX + 20, logoPosY + 5);
vertex(logoPosX, logoPosY);
endShape();
rectMode(CORNER);
rect(logoPosX, logoPosY, 1, 10);
rect(logoPosX + 10, logoPosY, 20, 10);
//academics hp
fill("white");
textFont('Courier');
textAlign(CENTER);
textSize(12.5);
text(academicsHP + "/100", HPposX + 50, HPposY);
//sanity logo
fill("yellow");
circle(logoPosX + 130, logoPosY, 15);
circle(logoPosX + 130+ 10, logoPosY, 15);
triangle(logoPosX + 130 - 8, logoPosY, logoPosX + 130 + 18, logoPosY, logoPosX + 130 + 6, logoPosY + 15);
//sanity hp
fill("white");
text(sanityHP + "/100", HPposX + 130 + 30, HPposY);
//finance logo
fill("yellow");
textSize(33);
text('S', logoPosX + 250, logoPosY + 10);
rect(logoPosX - 1.5 + 250, logoPosY + 10 - 22, 3, 25);
//finance hp
fill("white");
textSize(12.5);
text(financeHP + "/100", HPposX + 250 + 25, HPposY);
//social life logo
fill("yellow");
ellipse(logoPosX + 370, logoPosY, 30, 20);
triangle(logoPosX + 370, logoPosY, logoPosX + 370 + 10, logoPosY, logoPosX + 370 + 10, logoPosY + 15);
//social hp
fill("white");
text(socialHP + "/100", HPposX + 400, HPposY);
}
function subtractHP(){ //BY XU
//subtract hp
if (imageCount <= 3){
var randomNum = random(0, 1);
if (randomNum < 0.5){
academicsHP -= 10;
sanityHP += 10;
}else{
academicsHP += 10;
sanityHP -= 10;
}
}
if (imageCount >= 4 || imageCount <= 7){
var randomNum = random(0, 1);
if (randomNum < 0.5){
financeHP -= 10;
socialHP += 10;
}else{
financeHP += 10;
socialHP -= 10;
}
}
if (imageCount >= 8 || imageCount <= 11){
var randomNum = random(0, 1);
if (randomNum < 0.5){
academicsHP += 10;
sanityHP -= 10;
}else{
academicsHP -= 10;
sanityHP += 10;
}
}
if (imageCount >= 12 || imageCount <= 15){
var randomNum = random(0, 1);
if (randomNum < 0.5){
financeHP += 10;
socialHP -= 10;
}else{
financeHP -= 10;
socialHP += 10;
}
}
}
function checkStatus(){ //BY XU
if (academicsHP == 100 || sanityHP == 100 || financeHP == 100 || socialHP == 100){
badEnd = true;
}
if (academicsHP == 0 || sanityHP == 0 || financeHP == 0 || socialHP == 0){
badEnd = true;
}
if (monthCount >= 24){
goodEnd = true;
}
}
function backgroundChange(){ //BY XIAOYU
if (timeTracker == 0) {
backgroundColor = 130;
}
if (timeTracker == 1) {
backgroundColor = 95;
}
if (timeTracker == 2) {
backgroundColor = 0;
}
}
function drawStar(){ //BY XIAOYU
noStroke();
fill("yellow");
push();
translate(this.x, this.y);
ellipse(20, 20, random(1,5), random(1,5));
pop();
}
function makeStar(starX, starY){ //BY XIAOYU
var makeStar = {x: starX,
y: starY,
draw: drawStar}
return makeStar;
}
function displayStars(){ //BY XIAOYU
for (i = 0; i < star.length; i++){
star[i].draw();
}
}
function drawCloud(){ //BY XU
noStroke();
fill(185);
push();
translate(this.x2, this.y2);
ellipse(15, 26, 34, 20);
ellipse(45, 30, 22, 25);
ellipse(15, 35, 15, 16);
ellipse(30, 28, 28, 26);
pop();
}
function makeCloud(cloudX, cloudY){ //BY XU
var makeCloud = {x2: cloudX,
y2: cloudY,
speed: -6,
move: moveCloud,
draw: drawCloud}
return makeCloud;
}
function moveCloud(){ //BY XU
this.x2 += this.speed;
if(this.x2 <= -10){
this.x2 += width;
}
}
function displayCloud(){ //BY XU
for (i = 0; i < cloud.length; i++){
cloud[i].move();
cloud[i].draw();
}
}
function drawCrow(){ //BY XIAOYU
strokeWeight(1);
stroke(0);
noFill();
arc(this.x3, this.y3, this.size, this.size/2, PI, TWO_PI);
arc(this.x3 + this.size, this.y3, this.size, this.size/2, PI, TWO_PI);
}
function makeCrow(crowX, crowY){ //BY XIAOYU
var makeCrow = {x3: crowX,
y3: crowY,
speed2: random(3, 10),
size: random(5, 10),
move2: moveCrow,
draw2: drawCrow}
return makeCrow;
}
function moveCrow(){ //BY XIAOYU
this.x3 -= this.speed2;
this.y3 -= this.speed2 / random(5, 10);
}
function displayCrow(){ //BY XIAOYU
for (i = 0; i < crow.length; i++){
crow[i].move2();
crow[i].draw2();
}
}
function addCrow(){ //BY XU
if (random(0,1) < 0.1) {
var crowX = width;
var crowY = random(0, height/2);
crow.push(makeCrow(crowX, crowY));
}
}
function removeCrow() { //BY XU
var keep = [];
for (i = 0; i < crow.length; i++) {
if (crow[i].x3 + crow[i].size > 0) {
keep.push(crow[i]);
}
}
crow = keep;
}
For this final project, Xiaoyu and I collaborated to use p5js to create a CMU version of Reigns (a phone game). The game starts by pressing the mouse, then a series of questions will be presented, and the player uses ‘a’ key and ‘b’ key to make choices. There are four HP counters at the top of the screen, and they will fluctuate based on the choices the player makes. Once any of the four HPs reach 0/100 or 100/100, the game will present the bad end. If the player survives until 48 months (4 years), the game presents the good end.
We used things we learned in the past semester to give the game some particular features, such as the background changes, making sure the characters don’t repeat themselves, etc. We drew the characters ourselves in illustrator, and created the gamestart/end pages with p5js.
Instructions:
Up- w key
Down- s key
Left- a key
Right- d key
WARNING: DO NOT TOUCH THE FIRE
The objective of the game is to avoid the wild fires and reach the plant at the lower right hand corner of the screen. If you reach the plant, the words “You Win” appear along with the button that leads you to the next level. If you touch the fire, the words “You Lose” appear along with a restart button. The game has multiple levels which get harder as you go due to the increase in the number of fires and the heightened speed of the character. The level resets without an increase in difficulty if you touch the fire. The game is meant to bring awareness to forest fires caused by climate change.
Note: click on the screen to start the game
// Ankitha Vasudev and Mari Kubota
// 15-104 Final Project
// ghost position
var dx = 0;
var dy = 0;
var posX;
var posY;
//ghost speed
var speed = 1;
//sapling postion
var xsap = 375;
var ysap = 370;
//fire
var shapes = [];
//level number
var lvl = 1;
var gameOn = true;
var lose = false;
function setup() {
createCanvas(400, 400);
//Makes fires and puts into array
for (var i = 0; i < 15; i++) {
shapes.push(makeShapes());
}
}
function draw() { //written together
background(178, 204, 163);
//calls functions
sapling();
ghost();
pressingKey();
levelNumber();
for (i = 0; i < shapes.length; i++) {
shapes[i].display();
//Game Over route
if (posX >= shapes[i].x & posX <= shapes[i].x+30
&& posY >= shapes[i].y-25 && posY <= shapes[i].y+20) {
lose = true;
speed = 0;
resetButton();
gameOn = false;
}
}
//You Win route
if (posX >= xsap-10 & posY >= ysap) {
noStroke();
fill(0);
textSize(50);
// winsound.play();
text('You Win!', 100, 200);
speed = 0;
nextLvl();
gameOn = false;
}
if (lose==true){
noStroke();
fill(0);
textSize(50);
text('You Lose', 100, 200);
}
}
function pressingKey() { //by Ankitha
//Ghost movements controlled by keys
if(keyIsDown(87)) { //W key
dy -= speed;
}
if(keyIsDown(83)) { //S key
dy += speed;
}
if(keyIsDown(65)){ //A key
dx -= speed;
}
if(keyIsDown(68)){ //D key
dx+=speed;
}
}
function ghost() { //by Mari
//Ghost character shape and constraints
noStroke();
fill(255);
posX = 25+dx;
posY = 17+dy;
if (posX <= 7){
dx += speed;
}
if (posX >= width - 7){
dx -= speed;
}
if (posY <= 9){
dy += speed;
}
if (posY >= height-14){
dy -= speed;
}
ellipse(25+dx,17+dy,17,20);//head
ellipse(25+dx,27+dy,5,10);//feet
ellipse(20+dx,27+dy,5,10);//feet
ellipse(30+dx,27+dy,5,10);//feet
fill(0);
ellipse(20+dx,19+dy,3,3);//left eye
ellipse(30+dx,19+dy,3,3);//right eye
strokeWeight(1);
stroke(0);
line(22+dx,23+dy,28+dx,23+dy);
}
function shapesDisplay() { //by Ankitha
//Make fire shape using vertex
strokeWeight(3);
stroke("red");
fill("orange");
beginShape();
vertex(this.x-5, this.y);
vertex(this.x, this.y+20);
vertex(this.x+20, this.y+20);
vertex(this.x+30, this.y+20);
vertex(this.x+40, this.y+10);
vertex(this.x+30, this.y-20);
vertex(this.x+25, this.y+5);
vertex(this.x+22, this.y-20);
vertex(this.x+13, this.y);
vertex(this.x+12, this.y-20);
vertex(this.x-5, this.y);
endShape();
}
function makeShapes() { //by Ankitha
//randomizes fire position
var sh = {x: random(-30,430),
y: random(50,340),
display: shapesDisplay}
return sh;
}
function resetButton() { //by Mari
//Reset button when game over appears
stroke(0);
strokeWeight(2);
fill("red");
rectMode(CORNER);
rect(115,360,75,30);
fill(0);
textSize(12);
noStroke();
text("RESTART",128,379);
}
function nextLvl() { //by Mari
//next level button when you win
rectMode(CORNER);
strokeWeight(2);
stroke(0);
fill("green");
rect(30,360,75,30);
fill(0);
textSize(10);
noStroke();
text("Next Level",45,378);
}
function sapling() { //by Mari
//sapling (the goal)
fill("green");
strokeWeight(5);
stroke("green")
line(xsap,ysap,xsap,ysap+20); //stem
noStroke();
ellipse(xsap-10,ysap,20,10); //left leaf
ellipse(xsap+10,ysap,20,10); //right leaf
fill("yellow");
ellipse(xsap,ysap+20,10,10);
}
function levelNumber() { //by Mari
noStroke();
fill(0);
textSize(15);
text("Level: " + lvl, 340, 20);
}
function mouseClicked() { //by Ankitha
//mouse clicking on button resets the games
//game over button LOSE
if (mouseX<245 & mouseX>130 && mouseY>360 && mouseY<400 && gameOn==false) {
shapes=[];
gameOn=true;
lvl = 1;
speed=lvl;
makeShapes();
for (var i = 0; i < 15; i++) {
shapes.push(makeShapes());
}
dx=0;
dy=0;
lose = false;
}
//next level button WIN
if (mouseX<80 & mouseX>30 && mouseY>360 && mouseY<400 && gameOn==false) {
gameOn=true;
lvl++;
speed=lvl;
makeShapes();
for (var i = 0; i < 15; i++) {
shapes.push(makeShapes());
}
dx=0;
dy=0;
}
}
]]>