In this interactive game/animation:
(click on the canvas first)
Press left arrow to add birds, press right arrow to add chimneys. Bird nests and raccoons will show up according to the added elements. The position of mouseY changes the color of the sky. As more birds are added, bird sound gets louder.
//Veronica Wang
//Section B
//yiruiw@andrew.cmu.edu
//Final Project
var Y_AXIS = 1; //gradient sky axis
var c1, c2; //gradient sky colors
var PREFIX = "https://courses.ideate.cmu.edu/15-104/f2018/wp-content/uploads/2018/12/";
//var PREFIX = "";
var birdSound;
var trainSound;
var bV = 0.2;
var houseList = []; //array of houses
var allTrees = []; //trees
var birdList = []; //birds
var raccoonList = []; //raccoons
var flap = true;
var nestCount = 0;
var birdperNest = 3; //for every three birds draw a nest
var trainList = []; //trains
var rachouseCount = 0;
frameCount = 200;
function preload(){
birdSound = loadSound(PREFIX + "birds.wav");
trainSound = loadSound(PREFIX + "train.wav");
trainSound.setVolume(0.5);
}
function setup() {
createCanvas(500, 300);
//sky colors
c1 = color(244, 248, 255); //light blue
c2 = color(94, 164, 186); //dark teal blue
drawBackground();
//draw houses
for(var i = 0; i < 10; i++) {
var newX = random(80, 420);
var newY = random(120, 200);
if (houseList.length < 1){
houseList.push(makeHouse(newX, newY));
}
else{
while (checkifLess(newX, newY, houseList, 30)){
newX = random(80, 420);
newY = random(120, 200);
}
houseList.push(makeHouse(newX, newY));
}
}
//draw trees
for(var i = 0; i < 40; i++) {
allTrees.push(makeTree());
}
sortTree(allTrees);
sortTree(houseList);
//draw creatures
birdList.push(makeBird(width / 2,height / 2, 15));
raccoonList.push(makeRac(width / 2, height / 2, 5));
}
function draw() {
//play sound
birdSound.setVolume(bV);
if (frameCount % 100 == 0){
birdSound.play();
}
//set volume to be proportional to number of birds
bV = (map(birdList.length, 0, 20, 0, 1));
drawBackground();
drawStairs();
//initial number of artificial nests
nestCount = 0;
rachouseCount = 0;
//raccoon house counter
for (var i = 0; i < houseList.length; i++) {
if (houseList[i].rh){
rachouseCount += 1;
}
}
//add raccoons
if (raccoonList.length < rachouseCount * 2){
raccoonList.push(makeRac(random(0, 500), random(150, 220)));
}
//drawing raccoons and removing them from array if out of canvas
for (var i = 0; i < raccoonList.length; i++) {
raccoonList[i].draw();
raccoonList[i].rx += raccoonList[i].vel;
if (racoutofBound(raccoonList[i])){
raccoonList.splice(i, 1);
i -= 1;
}
}
//draw houses
for (var i = 0; i < houseList.length; i++) {
houseList[i].draw();
}
//draw trees
for (var i = 0; i < allTrees.length; i++){
if (i % 3 == 0){
allTrees[i].draw1();
} else if (i % 3 == 1){
allTrees[i].draw2();
} else {
allTrees[i].draw3();
}
if (allTrees[i].nest){
nestCount += 1;
}
}
drawTracks();
//bird wing flap
if (frameCount % 40 == 0){
flap = !(flap);
}
//if bird is out of canvas, take it out of the array
for (var i = 0; i < birdList.length; i++) {
if (birdoutofBound(birdList[i])){
birdList.splice(i, 1);
i -= 1;
}
}
//draw birds
for (var i = 0; i < birdList.length; i++) {
if (flap){
birdList[i].draw2();
} else {
birdList[i].draw();
}
birdList[i].bx += birdList[i].xvel * noise(1);
birdList[i].by += birdList[i].yvel * noise(1);
};
//adding random movement and scale birds
if (frameCount % 100 == 0){
for (var i = 0; i < birdList.length; i++) {
birdList[i].xvel += random(-3, 3) * (map(pow(2, birdList[i].by / 50), 0, pow(2, 300 / 50), 1, 100)) / 35;
birdList[i].yvel += random(-1, 1) * (map(pow(2, birdList[i].by / 50), 0, pow(2, 300 / 50), 1, 100)) / 35;
}
}
//night sky filter
var darkness = map(mouseY, 0, height, 0, 200);
fill(0, 0, 0, darkness);
noStroke();
rect(0, 0, width, height);
//add nest to empty trees
if (birdList.length > 0 & int(birdList.length / birdperNest) < 40){
if (int(birdList.length / birdperNest) > nestCount){
var seltree = int(random(0, allTrees.length));
while (allTrees[seltree].nest){
seltree = int(random(0, allTrees.length));
}
allTrees[seltree].nest = true;
} else if (int(birdList.length / birdperNest) < nestCount){
var seltree = int(random(0, allTrees.length));
if (allTrees[seltree].nest == true){
allTrees[seltree].nest = false;
}
}
}
//draw train
if (frameCount % 400 == 0) {
trainList.push(makeTrain());
trainSound.play();
}
for (var i = 0; i < trainList.length; i++) {
trainList[i].x -= trainList[i].vel;
trainList[i].draw();
};
if (trainList.length > 0){
if (trainList[0].x < -750){
trainList.splice(0, 1);
}
}
}
//if raccoon is out of canvas
function racoutofBound(bird){
if (bird.rx < 0){
return true;
} else if (bird.rx > width){
return true;
} else if (bird.ry < 0){
return true;
} else if (bird.ry > height){
return true;
}
return false;
}
//if bird is out of canvas
function birdoutofBound(bird){
if (bird.bx < 0){
return true;
} else if (bird.bx > width){
return true;
} else if (bird.by < 0){
return true;
} else if (bird.by > height){
return true;
}
return false;
}
//sort tree order by y position
function sortTree(treelist){
n = treelist.length;
for (var i = 0; i < n; i++){
for (var j = 0; j < n - i - 1; j++){
if (treelist[j].y > treelist[j+1].y){
var temp = treelist[j];
treelist[j] = treelist[j+1];
treelist[j+1] = temp;
}
}
}
}
//draw tree outside of houses
function ispointIn(x, y, house){
var x1 = house.x;
var x2 = house.x + house.w;
var y1 = house.y - house.w / 3;
var y2 = house.y + house.h * house.f;
if (x < x1){
return false;
} else if (x > x2){
return false;
} else if (y < y1){
return false;
} else if (y > y2 + 13){
return false;
}
return true;
}
//goes through list to check if point is outside the boundary
function hListcheck(x, y, list, n){
if (list.length < 1){
return false;
}
if (n < list.length - 2){
return (ispointIn(x, y, list[n]) || hListcheck(x, y, list, n + 1));
} else {
return ispointIn(x, y, list[n]);
}
}
//make sure houses are not overlapping
function checkifLess(x, y, list, mindist){
var chck = false;
for (var i = 0; i < list.length; i++) {
var objdist = dist(x, y, list[i].x, list[i].y);
if (objdist < mindist){
chck = true;
}
}
return chck;
}
//house object
function makeHouse(locX, locY){
var num = 75;
var house = { x: locX,
y: locY,
w: map(pow(2, locY / num), 0, pow(2, 300 / num), 1, 100), //proportionally scale house in the distance
f: floor(random() * 2 + 1), //random number of floors from 1-3
rh: false, //raccoon houses
draw: drawHouse
};
return house;
}
function drawHouse(){
noStroke();
fill(173, 110, 110);
var h = this.w / 2;
rect(this.x, this.y, this.w, h * this.f); //body of house
rect(this.x + this.w * 0.7, this.y - this.w / 3,
this.w / 5, this.w / 3); //chimney
triangle(this.x, this.y,
this.x + this.w / 2, this.y - this.w / 3,
this.x + this.w, this.y); //roof
if(this.f == 1){
//gradient
for (var i = 0; i < this.y / 10; i++) {
var op2 = map(i, 0, 50, 0, 200);
stroke(201, 141, 141, op2);
line(this.x, i * this.y * 0.005 + this.y,
this.x + this.w, i * this.y * 0.005 + this.y);
}
drawWindow(this.x + this.w / 5, this.y + this.w / 6,
this.w * 0.3, this.w / 5);
drawShrub(this.x, this.y + h,
this.w * 0.5, this.w * 0.3);
}else{
//gradient
for (var i = 0; i < this.y / 5; i++) {
var op3 = map(i, 0, 50, 0, 200);
stroke(201, 141, 141, op3);
line(this.x, i * this.y * 0.005 + this.y,
this.x + this.w, i * this.y * 0.005 + this.y);
}
drawWindow(this.x + this.w / 5, this.y + this.w / 6,
this.w * 0.3, this.w / 5);
drawWindow(this.x + this.w / 5, this.y + this.w * 0.6,
this.w * 0.3, this.w / 5);
drawShrub(this.x, this.y + this.w,
this.w * 0.4, this.w * 0.3);
}
if(this.rh){
fill(135, 81, 132);
rect(this.x + this.w,
this.y + h / 5, this.w / 6, (h * this.f) * 0.75);
triangle(this.x + this.w, this.y + h * this.f,
this.x + this.w, this.y + (h * this.f) / 2,
this.x + this.w + this.w / 3, this.y + h * this.f);
}
}
function drawWindow(x, y, w, h){
noStroke();
fill(239, 233, 218);
rect(x, y, w, h);
}
function drawShrub(x, y, w, h){
noStroke();
fill(143, 168, 104);
ellipse(x, y, w, h);
ellipse(x + 5, y, w * 0.8, h * 0.5);
ellipse(x + 3, y - 2, w * 0.8, h * 0.8);
}
function makeTree(){
var num1 = 75;
var tempY = random(120, 200);
var tempX = random(0, 450)
var mult = map(pow(2, tempY / num1), 0, pow(2, 300 / num1), 1, 100) / 40;
var tX = tempX + 3 * mult;
var tY = tempY + 30 * mult;
while (hListcheck(tX, tY, houseList, 0)){
tempY = random(120, 200);
tempX = random(0, 450)
tX = tempX + 3 * mult;
tY = tempY + 30 * mult;
}
var tree = { x: tempX,
y: tempY,
w: map(pow(2, tempY / num1), 0, pow(2, 300 / num1), 1, 100), //proportionally scale house in the distance
m: map(pow(2, tempY / num1), 0, pow(2, 300 / num1), 1, 100) / 40,
draw1: tree1draw,
draw2: tree2draw,
draw3: tree3draw,
nest: false
};
return tree;
}
function tree1draw(){
noStroke();
push();
translate(this.x, this.y);
scale(this.m, this.m);
fill(122, 98, 66);
rect(3, 0, 3, 30);
fill(95, 140, 99, 180);
ellipse(0, -5, 30, 35);
ellipse(15, -5, 20, 25);
ellipse(5, 8, 50, 20);
if(this.nest == true){
fill(89, 68, 49);
rect(0, 0, 10, 10);
fill(193, 164, 137);
ellipse(5, 5, 4, 4);
}
pop();
}
function tree2draw(){
noStroke();
push();
translate(this.x, this.y);
scale(this.m, this.m);
fill(122, 98, 66);
rect(5, 0, 3, 30);
fill(95, 120, 96, 200);
triangle(18, 18, -6, 18, 6, -30);
if(this.nest == true){
fill(89, 68, 49);
rect(0, 0, 10, 10);
fill(193, 164, 137);
ellipse(5, 5, 4, 4);
}
pop();
}
function tree3draw(){
noStroke();
push();
var mult = this.w / 40;
translate(this.x, this.y);
scale(mult, mult);
fill(122, 98, 66);
rect(3, 0, 3, 30);
fill(108, 132, 102, 200);
ellipse(4, -17, 20, 20);
ellipse(4, -8, 30, 20);
ellipse(4, 5, 40, 25);
if(this.nest == true){
fill(89, 68, 49);
rect(0, 0, 10, 10);
fill(193, 164, 137);
ellipse(5, 5, 4, 4);
}
pop();
}
function drawBackground(){
//draw background gradient
setGradient(0, 0, width, height * 0.45, c1, c2, Y_AXIS);
//mountain layer 1
noStroke();
fill(75, 137, 138);
beginShape();
curveVertex(0, height);
curveVertex(0, height);
curveVertex(0, 200);
curveVertex(0, 180);
curveVertex(190, 60);
curveVertex(280, 80);
curveVertex(350, 70);
curveVertex(420, 100);
curveVertex(520, 80);
curveVertex(width, height);
curveVertex(width, height);
endShape();
//gradient mask
for (var i = 0; i < 500; i++) {
var op = map(i, 100, 500, 0, 255);
stroke(255, 255, 255, op);
line(0, i * 0.6, width, i * 0.6);
}
//mountain layer 2
noStroke();
strokeWeight(1);
fill(75, 147, 154);
beginShape();
curveVertex(0, height);
curveVertex(0, height);
curveVertex(0, 120);
curveVertex(0, 110);
curveVertex(100, 70);
curveVertex(200, 130);
curveVertex(300, 90);
curveVertex(400, 130);
curveVertex(500, 120);
curveVertex(500, 130);
curveVertex(width, height);
curveVertex(width, height);
endShape();
//gradient mask
for (var i = 0; i < 500; i++) {
var op = map(i, 100, 500, 0, 255);
stroke(255, 183, 80, op);
line(0, i, width, i);
}
}
function drawStairs(){
noStroke();
fill(99, 88, 77);
for (var i = 0; i < 15; i++) {
rect(i * 5 + 30, i * 3 + 190, 8, 1.5);
rect(i * 5 + 40, -i * 2 + 190, 8, 1);
rect(i * 5 + 30, i * 3 + 120, 1 + i * 0.5, 1)
};
rect(35, 190, 2, 45);
rect(28, 190, 2, 45);
rect(28, 210, 8, 2);
rect(110, 162, 1, 35);
rect(116, 162, 1, 35);
rect(110, 170, 6, 1);
strokeWeight(2);
line(1, 1, 100, 100);
for (var j = 0; j < 30; j++) {
fill(117, 107, 98);
rect(j * 2 + 440, j * 3 + 130, 1 + j * 0.7, 1);
};
}
function drawTracks(){
stroke(122, 102, 82);
strokeWeight(2);
line(0, 280, 500, 280);
strokeWeight(1);
line(0, 275, 500, 275);
for (var i = 0; i < 15; i++) {
rect(i * 40, 273, 1, 6);
};
}
function makeTrain(){
var train = { x: 500,
y: 277,
vel: random(5, 15),
draw: drawTrain
};
return train;
}
function drawTrain(){
noStroke();
fill(80);
triangle(this.x, this.y - 5, this.x + 25, this.y - 5, this.x + 25, this.y - 20);
rect(this.x + 12, this.y - 40, 10, 15);
rect(this.x + 20, this.y - 40, 50, 35);
rect(this.x + 22, this.y - 50, 15, 25);
rect(this.x + 55, this.y - 59, 30, 4);
rect(this.x + 70, this.y - 16, 16, 8);
rect(this.x + 80, this.y - 16, 8, 8);
rect(this.x + 88, this.y - 13, 18, 3);
fill(140, 89, 88);
rect(this.x + 16, this.y - 45, 40, 25);
fill(140, 100, 88);
rect(this.x + 60, this.y - 55, 20, 45);
fill(201, 216, 215);
rect(this.x + 64, this.y - 52, 12, 18);
fill(96, 83, 58);
ellipse(this.x + 30, this.y - 5, 15, 15);
ellipse(this.x + 70, this.y - 5, 15, 15);
for (var i = 1; i < 5; i++) {
fill(80);
rect(this.x + 100 * i, this.y - 55, 90, 3);
rect(this.x + 100 * i, this.y - 13, 100, 3);
fill(140, 120, 88);
rect(this.x + 5 + 100 * i, this.y - 52, 80, 45);
fill(140, 130, 98);
rect(this.x + 5 + 100 * i, this.y - 22, 80, 15);
fill(201, 216, 215);
rect(this.x + 12 + 100 * i, this.y - 48, 30, 18);
rect(this.x + 48 + 100 * i, this.y - 48, 30, 18);
fill(96, 83, 58);
ellipse(this.x + 20 + 100 * i, this.y - 5, 15, 15);
ellipse(this.x + 70 + 100 * i, this.y - 5, 15, 15);
};
fill(0);
drawSmoke(this.x, this.y);
}
function drawSmoke(x, y){
fill(255, 255, 255, 100);
ellipse(x + 30, y - 60, 20, 10);
ellipse(x + 50, y - 70, 15, 8);
}
function makeBird(x, y) {
var num = 50;
var bird = {"bx": x,
"by": y,
"bsz": size,
"bsca": (map(pow(2, y / num), 0, pow(2, 300 / num), 1, 100)) / 35,
"xvel": random(-10, 10) * (map(pow(2, y / num), 0, pow(2, 300 / num), 1, 100)) / 35,
"yvel": random(-5, 5) * (map(pow(2, y / num), 0, pow(2, 300 / num), 1, 100)) / 35,
};
bird.draw = birdDraw;
bird.draw2 = birdDraw2;
return bird;
}
function birdDraw(){
noFill();
stroke(0);
strokeWeight(2);
push();
translate(this.bx, this.by);
scale(this.bsca);
ellipseMode(CENTER);
arc(35, 35, 100, 100, PI * 1.25, PI * 1.5);
arc(-35, 35, 100, 100, PI * 1.5, PI * 1.75);
fill(0);
ellipse(0, 0, 5, 5);
pop();
}
function birdDraw2(){
noFill();
stroke(0);
strokeWeight(2);
push();
translate(this.bx, this.by);
scale(this.bsca);
rotate(PI/10);
arc(35, 35, 100, 100, PI * 1.25, PI * 1.5);
fill(0);
ellipse(0, 0, 5, 5);
pop();
push();
translate(this.bx, this.by);
scale(this.bsca);
rotate(-PI / 10);
arc(-35, 35, 100, 100, PI * 1.5, PI * 1.75);
pop();
}
//make raccoon
function makeRac(x, y){
var rac;
var num = 50;
rac = {
"rx": x ,
"ry": y,
"rsz": size,
"bsca": (map(pow(2, y / num), 0, pow(2, 300 / num), 1, 100)) / 400,
"vel": random(-2, 2) * (map(pow(2, y / num), 0, pow(2, 300 / num), 1, 50)) / 35
};
rac.draw = racDraw;
return rac;
}
//draw raccoon
function racDraw(){
var inv = -1;
push();
translate(this.rx, this.ry);
fill(0);
noStroke();
beginShape();
if (this.vel < 0){
scale(this.bsca, this.bsca);
} else{
scale(this.bsca * -1, this.bsca);
}
vertex(0, 0);
vertex(5, -10);
vertex(3, -20);
vertex(7, -30);
vertex(5, -40);
vertex(8, -45);
vertex(20, -40);
vertex(65, -55);
vertex(85, -65);
vertex(150, -60);
vertex(190, -30);
vertex(180, 20);
vertex(190, 50);
vertex(180, 80);
vertex(170, 80);
vertex(170, 75);
vertex(175, 73);
vertex(176, 55);
vertex(140, 25);
vertex(110, 25);
vertex(80, 80);
vertex(70, 80);
vertex(70, 75);
vertex(75, 75);
vertex(80, 50);
vertex(70, 10);
vertex(50, 10);
vertex(30, 5);
vertex(10, 10);
vertex(0, 0);
endShape();
beginShape();
vertex(200, -25);
vertex(192, 10);
vertex(200, 18);
vertex(210, -20);
endShape();
beginShape();
vertex(220, -15);
vertex(230, -8);
vertex(220, 30);
vertex(210, 22);
endShape();
beginShape();
vertex(240, -3);
vertex(250, 5);
vertex(242, 20);
vertex(232, 25);
endShape();
beginShape();
fill(255);
vertex(50, 10);
vertex(30, -15);
vertex(20, -13);
vertex(15, -5);
vertex(15, -5);
vertex(20, 0);
vertex(26, 0);
vertex(35, 6);
endShape();
fill(0);
ellipse(23, -8, 5, 5);
rect(112, 50, 15, 7);
rotate(PI / 4);
rect(60, -40, 10, 40);
rect(120, -80, 10, 30);
pop();
}
//interactions
function keyPressed(){
if(keyCode === RIGHT_ARROW){
if (rachouseCount < houseList.length){
var selhouse = int(random(0, houseList.length));
while (houseList[selhouse].rh == true){
selhouse = int(random(0, houseList.length));
}
houseList[selhouse].rh = true;
}
}
if(keyCode === LEFT_ARROW){
birdList.push(makeBird(random(0, 500), random(0, 200)));
}
}
//Linear gradient code from p5js examples https://p5js.org/examples/color-linear-gradient.html
function setGradient(x, y, w, h, c1, c2, axis) {
noFill();
if (axis == Y_AXIS) { // Top to bottom gradient
for (var i = y; i <= y + h; i++) {
var inter = map(i, y, y + h, 0, 1);
var c = lerpColor(c1, c2, inter);
stroke(c);
line(x, i, x + w, i);
}
}
}
In this project I want to create an interactive animation on cohabitation wiht urban wildlife in cities. As we are displacing natural habitats to make way for urban sprawl, effects of a loss of biodiversity and edge effects from habitat fragmentation are becoming more pronounced. In my studio I am currently working on a thesis project dealing with the environment and cohabitation/negotiation of boundaries between human and more-than-human species, and creating installations for endangered species as well as synanthropic species to share urban space. I am inspired by the works of Joyce Hwang and Sarah Gunawan , and I want to create an animation that documents the vision of such installation projects and their impacts on our environment. Especially the bird landing pads and composting chimneys for raccoons.
In this animation, trains pass by at a certain interval of time, pressing left arrow adds a bird, and for every 3 birds added, an artificial bird nest attached to trees will pop up. (Nests will start disappearing when birds leave the screen). Pressing right arrow adds a compost chimney attached to houses, and every chimney attracts 2 raccoons.
Working on this project I spent a large chunk of my time illustrating the creatures and houses which in hindsight could have been done much easier if I just drew them and upload them to imgur. I also had trouble with overlapping objects and had to create functions that sort the order or the object array and also draws objects outside of the boundary of other objects. I feel like I reviewed everything we learned this semester through this project.
Sound Test