svitoora – Evolution

For best viewing experience, please open in full screen here, and let it run for a while in the background. Check on your simulation occasionally to see how they’re evolving.

You can click to add producers, but this is highly unrecommended as it will alter the carrying capacity of the system. I recommend simply letting the software simulation run in the background.

Evolution

// 
// Supawat Vitoorapakorn
// Svitoora@andrew.cmu.edu
// Section E


// EVOLUTION: An overview of genetic algorithms
// 
// Evoltuion is a software that simulates evolution over time
// of a specie of consumers. The three main conceps are:

// VARIABILITY
// At setup, a diverse pool of genetic material is randomly created.

// HERERDITY
// Every time two producers mate, it's geneteics are combined and passed down.

// SELECTION
// The consumer's speed is determine by its darkness.
// Over time natural selection will probabilistically occur.



// CHARACTERS
// 
// PRODUCERS:
// A certain amount of producers is randomly created every time interval.
// The producers gathers sunlight, grows, and if it exceeds a certain amount
// of calories it splits into two. Producers' calories can be consumed
// by consumers, and if producers' calories is negative it dies.

// CONSUMERS:
// A certain amount of consumers is randomly generated at setup.
// Consumers move around randomly searching for food (prdoucers).
// Each movement of a consumer consumes a certain amount of calories, and
// If a consumer's calorie is below zero, it dies. If a consumer's calorie
// reaches a certain threshold, it will stop searching for food and begin
// searching for a potential mate nearby to reproduce with.
// 
// Reproduction consumes energy, and the the consumer may die shortly after.


var time = 1; // Time variable
var w = 480; // width
var h = 480; // height

var turtles_AI = []; // array of consumers

var INDEX = 0; // ID for each turtle
var debug_mode = false; // true to see calorie transfer.


//------------------------------------------
// Creates Turtle
// Each turtle has fitness level based on its color's darkness
// The darker it is the faster it moves to food.
// Each turtle also has a certain amount of energy (calorie).
// Input: x,y,energy, color
// Output: Object turtle
function makeTurtle(tx, ty, energy = random(25, 300),
	c = color(random(0, 255), random(0, 255), random(0, 255))) {
	var turtle = {
		x: tx,
		y: ty,
		life_index: random(-1, 1), //random seed for perlin bheavior
		life_index2: random(-1, 1), //random seed for perlin bheavior
		x_dir: 1,
		y_dir: 1,
		c: c,
		energy: energy,
		index: INDEX,
		mating: false
	};
	turtle.fitness = fitness(turtle); //0 to 100
	turtle.diameter = map(turtle.energy, 0, 100, 1, 20);
	INDEX += 1;
	return turtle;
}

// Determines fitness of Turtle from 0 to 100%
// The darker the color the faster it is.
// Input: Color
// Out: fitness
function fitness(turtle) {
	R = red(turtle.c);
	G = green(turtle.c);
	B = blue(turtle.c);
	avg = (R + G + B) / 3 // 0-255
	return map(avg, 0, 255, 100, 0); //Darker the fitter
}

// Moves Turtle with noise
function turtle_move_AI() {
	speed = .045;
	for (i in turtles_AI) {
		life = turtles_AI[i].life_index;
		life2 = turtles_AI[i].life_index2;
		x_dir = turtles_AI[i].x_dir;
		y_dir = turtles_AI[i].y_dir;

		// Moves turtle with Perlin Noise
		SIZE = 10; //Radius of displacement
		turtles_AI[i].x += x_dir * (noise(time * life) - .5) * SIZE;
		turtles_AI[i].y += y_dir * (noise(time * life2) - .5) * SIZE;
		turtles_AI[i].energy -= .01; //Caloric Cost of movement


	}
}

// Contains any group of objects within the screen
// If object touch boundary of screen reverse direction
// Input: group
function contain(group) {
	for (i in group) {
		// Min
		if (group[i].x < 0) {
			group[i].x = 0
			group[i].x_dir *= -1;
			group[i].dx *= -1;
		}
		if (group[i].y < 0) {
			group[i].y = 0
			group[i].y_dir *= -1;
			group[i].dy *= -1;
		}
		// Max
		if (group[i].x > w) {
			group[i].x = w
			group[i].x_dir *= -1;
			group[i].dx *= -1;
		}
		if (group[i].y > h) {
			group[i].y = h
			group[i].y_dir *= -1;
			group[i].dy *= -1;
		}
	}
}

// Draws Turtle
function drawTurtle() {
	for (var i = 0; i < turtles_AI.length; i++) {
		d = map(turtles_AI[i].energy, 0, 100, 1, 20);
		fill(turtles_AI[i].c);
		ellipse(turtles_AI[i].x, turtles_AI[i].y, d, d)
		fill(255);
		textSize(10);
		if (debug_mode == true) {
			text(floor(turtles_AI[i].energy), turtles_AI[i].x, turtles_AI[i].y)
		}
	}
}

// Removes dead turtles
// If turtle has less than zero energy left it dies.
function turtle_update() {
	for (var i = 0; i < turtles_AI.length; i++) {
		if (turtles_AI[i].energy > 250) {
			turtles_AI[i].mating = true;
		}
		if (turtles_AI[i].energy < 0) {

			turtles_AI.splice(i, 1);
			return
		}
	}
}


//------------------------------------------
// Hunt Food
// Input: individuall turtle
// Output: individiual turtle's target
function hunt(turtle) {
	x = turtle.x;
	y = turtle.y;
	// If no food return false
	if (Object.keys(PRODUCERS) == 0) {
		return false
	} else {
		// Else target is the closest food
		target = {
			x: null,
			y: null,
			d: null
		};
		// Search all food
		for (j in PRODUCERS) {
			// Distance to food
			d_food = dist(x, y, PRODUCERS[j].x, PRODUCERS[j].y);
			// If target is null or closer than previous
			// Reassign target to closest producers
			if (target.d == null || d_food < target.d) {
				target.x = PRODUCERS[j].x;
				target.y = PRODUCERS[j].y;
				target.d = d_food;
				target.i = j
			}
		}
		return PRODUCERS[target.i];
	}
}

// Make all turtle hunt for Food
function HUNT() {
	for (var i = 0; i < turtles_AI.length; i++) {
		target = hunt(turtles_AI[i]);
		if (target != false & turtles_AI[i].mating == false) {
			moveToward(turtles_AI[i], target, turtles_AI[i].fitness);
			eat(turtles_AI[i], target)
		}
	}
}

// Makes Object X moves towards Object Y with V velocity
function moveToward(X, Y, V) {
	v = map(V, 0, 100, 0, .1);
	X.x = lerp(X.x, Y.x, v / 2);
	X.y = lerp(X.y, Y.y, v / 2);
}


// Eat food if food is inisde circle
// Input: individual food, FOOD array
function eat(turtle, target) {
	d = dist(turtle.x, turtle.y, target.x, target.y)
		// If food is inside turtle
	if (d < (turtle.diameter / 2)) {
		target.energy -= .2;
		turtle.energy += 1;
	}
}


//------------------------------------------
// Mating function of turtles
// Turtle becomes mature at 200 calories and seeks to reproduce
// Input: individual turtle
// Ouput: closest mateable target
function mate(turtle) {
	x = turtle.x;
	y = turtle.y;
	target = {
		x: null,
		y: null,
		d: null
	};
	// Search all potential mate
	mate_count = 0;
	for (var j = 0; j < turtles_AI.length; j++) {
		// If Mate-able and not self
		if (turtles_AI[j].mating == true & turtles_AI[j] != turtle) {
			mate_count += 1;
			d = dist(turtles_AI[j].x, turtles_AI[j].y, turtle.x, turtle.y)
			if (target.d == null || d < target.d) {
				target.x = turtles_AI[j].x;
				target.y = turtles_AI[j].y;
				target.d = d;
				target.i = j
			}
		}
	}
	// If there is no mate return false.
	if (mate_count == 0) {
		return false
	}
	// If there is mate return target.
	else {;
		return turtles_AI[target.i];
	}
}

// Makes turtles have sex.
// If mateable turtles touch one another they both lose 100 calorie
// and creates 1 baby. Turtle bcomes mateable at 200 calories.
function sex(turtle, target) {
	d = dist(turtle.x, turtle.y, target.x, target.y)
	if (d < turtle.diameter / 2) {
		turtle.energy -= 100;
		target.energy -= 100;
		// Genetic Averaging and Mutation
		c = lerpColor(turtle.c, target.c, random(.3, .7));
		x = (turtle.x + target.x) / 2;
		y = (turtle.y + target.y) / 2;
		turtles_AI.push(makeTurtle(x, y, 66, c))
	}
}


// Loop through turtles to and make them mate
function MATE() {
	for (var i = 0; i < turtles_AI.length; i++) {
		target = mate(turtles_AI[i]);
		if (target != false & turtles_AI[i].mating == true) {
			moveToward(turtles_AI[i], target, turtles_AI[i].fitness);
			sex(turtles_AI[i], target);
		}
	}
}


//------------------------------------------
// Control

// Adds producers where mouse is clicked
function mouseClicked() {
	// FOOD.push(new makeFood(mouseX, mouseY));
	producer = (new makeProducer(mouseX, mouseY, 30));
	PRODUCERS[time] = producer;
	print(PRODUCERS);
}

// Adds producers where mouse is dragged
function mouseDragged() {
	if (millisecond % 2 == 0) {
		producer = (new makeProducer(mouseX, mouseY, 30));
		PRODUCERS[time] = producer;
	}
}


//------------------------------------------
// Producers
// Make food from sunlight
// Grows overtime and increase cell amount
var PRODUCERS = {};

// Creates prodcuers that grows from light
// Producers are eaten by turtles
// Input: x,y, energy, dx, dy
function makeProducer(x, y, energy = 10, dx = 0, dy = 0) {
	this.x = x;
	this.y = y;
	this.life_index = random(-1, 1); //random seem for perlin beheavior
	this.life_index2 = random(-1, 1); //random seem for perlin beheavior
	this.energy = energy;
	this.c = color(0, 255 / 2, 0, 255 * random(.5, 1));
	this.mitosis = false;

	this.dx = dx;
	this.dy = dy;
}


// Draws producers
function drawProducer() {
	for (key in PRODUCERS) {
		x = PRODUCERS[key].x;
		y = PRODUCERS[key].y;
		c = PRODUCERS[key].c;
		energy = PRODUCERS[key].energy;
		fill(c);
		strokeWeight(1)

		// Perlin noise to size to give it life
		base_vivacity = 5;
		speed = 1
		life = base_vivacity * (sin(time / 5 * speed))

		// Make rectangles rotate randomly
		push();
		rectMode(CENTER);
		translate(x, y);
		rotate((noise(PRODUCERS[key].life_index) * 360));
		rect(0, 0, energy + life, energy + life);
		pop();

		// Debug mode
		if (debug_mode == true) {
			fill(255);
			textAlign(CENTER);
			text(round(PRODUCERS[key].energy), x, y);
		}
	}
}


// Makes producer grow and reproduce if it has enough energy
function growProducer() {
	for (key in PRODUCERS) {
		// Grow Producer
		life_index = PRODUCERS[key].life_index;
		PRODUCERS[key].energy += noise(time * life_index) / 4;
		// Reproduce
		if (PRODUCERS[key].energy > 50) {
			PRODUCERS[key].mitosis = true
		}
	}
}


// Producers preform mitosis by using it's energy to reproduce
function mitosisProducer() {
	for (key in PRODUCERS) {
		if (PRODUCERS[key].mitosis == true) {
			energy = PRODUCERS[key].energy
				// Create 2 new cells
			for (i = 0; i < 2; i++) {
				producer = (new makeProducer(
					PRODUCERS[key].x,
					PRODUCERS[key].y,
					5,
					random(-energy / 4, energy / 4),
					random(-energy / 4, energy / 4)));
				PRODUCERS[time] = producer;
				PRODUCERS[key].energy -= 25;
			}
		}
	}
}

// Basic Physics for producer while splittig
function mitosisPhysic() {
	for (key in PRODUCERS) {
		PRODUCERS[key].x += PRODUCERS[key].dx;
		PRODUCERS[key].y += PRODUCERS[key].dy;
		PRODUCERS[key].dx = lerp(PRODUCERS[key].dx, 0, .1);
		PRODUCERS[key].dy = lerp(PRODUCERS[key].dy, 0, .1);
	}

}


// Kills Producer if its energy is below 0.
function dieProducer() {
	for (key in PRODUCERS) {
		if (PRODUCERS[key].energy < 0) {
			delete PRODUCERS[key]
		}
	}
}

// Give Producer Perlin noise to make it look alive
function lifeProducer() {
	for (key in PRODUCERS) {
		SIZE = .5;
		lifeX = (noise(time * PRODUCERS[key].life_index) - .5) * SIZE;
		lifeY = (noise(time * PRODUCERS[key].life_index2) - .5) * SIZE;
		PRODUCERS[key].x += lifeX;
		PRODUCERS[key].y += lifeY;

	}
}


var producer_timer = 1;
// Adds new producer into the system every producer timer interval
function add_food(interval) {
	producer_timer += 1;
	print(producer_timer)
	if (producer_timer % 500 == 0) {
		for (var i = 0; i < random(0, 6); i++) {
			PRODUCERS[time] = (new makeProducer(random(0, w), random(0, h), 1));
			time += .1;
		}
	}
}


//------------------------------------------
// SETUP
function preload() {
	w = windowWidth;
	h = windowHeight;
}

// Creates petri dish
function setup() {
	createCanvas(w, h);
	background(255);
	num_node = 100;
	// Create a diverse genetic pool of consumers
	for (i = 0; i < num_node; i++) {
		t = makeTurtle(random(0, w), random(0, h));
		turtles_AI.push(t);
	}
	// Initial set of producers
	for (i = 0; i < num_node / 10; i++) {
		PRODUCERS[i] = (new makeProducer(random(0, w), random(0, h)));
	}
}

//------------------------------------------

function draw() {
	background(255, 255, 255, 255 * .33);
	millisecond = floor(millis()) % 2000;
	// Model
	time += .1;
	turtle_move_AI();
	contain(turtles_AI);
	contain(PRODUCERS);
	HUNT();
	MATE()
	turtle_update();

	// Producers 
	growProducer();
	dieProducer();
	mitosisProducer();
	mitosisPhysic();
	lifeProducer();
	add_food(1000);

	// Draw
	noStroke();
	drawTurtle();
	drawProducer();
}

Producer splits into two after growing big enough:

Producers splitting.

Consumers mate after it exceeds a certain caloric threshold and combines their genes with some amount of mutation:

Consumers mating.

At first, the simulation begins with a lot genetic diversity:

The simulation begins with a lot gentic diversity.

But over time due to natural selection for faster (darker) consumers, the genetic diversity is significantly decreased:

Over time natural selection reduces genetic diversity.

Interestingly, membrane-like structures also start to emerge from how food is being consumed. Here, the producers form a membrane around a dense cluster of producers that is rapidly splitting.

Membrane-like structures emerged although it wasn’t explicity programmed.

These membrane-like and clusters macrostructures were not explicitly programmed, they emerge probabilistically out of the specification and rules of this simulation. Here, a wall of producer is protecting a rapidly splitting colony of producers.

Wall of producers protecting a rapidly growing colony.

Group behavior also emerged out of the algorithm. Here, the producers began to form almost cell like clusters where food is rapidly consumed and reproduction distance is significantly decreased thereby creating a cell-like unit.

Individual cell-like clusters of rapidly reproducing and consuming begin to emerge.
Here, one cell-like cluster separately moves towards another group of producers.
Last colony of producers consumed.

As the system reaches its carrying capacity, the producer begins to starve and die.

No more food left.
Consumers begin to starve and die.
As food emerge, the cycle of population repeats.

And as this cycle of extreme population swing for both the producers and consumers restarts, the genetic diversity of population doesn’t. Over time, despite natural mutation rate, the consumer population becomes abosolutely homogenous without any genetic diversity. In real life, this means that the population is extremely susceptible to a disease that can complete wipe its whole population out. 

No more genetic diversity is left.

Conclusion:

  1. Features can be created implicitly through explicitly programming beheaviors. Alhtough certain feaures of this simulation wasn’t explicity programmed, it emerge through the algorithms of the character’s beheavior.
  2. A system of genetic evolution will never reach its ultimate form. Although the black ones were the fastest, they needed to reproduced with inferior consumers thereby creating sub par offpsrings. Over time, this means that humans as a geneticically evolving organsims will never reach our ultimate form, if such form exist.
  3. What happens when there is no genetic diversity? If life on earth continued to evolve due to natural selection and converge towards an ideal form and homogenity what would happen?

 

SaveSave

SaveSave

SaveSave

rmanagad-finalproject-sectione

sketch

//Robert Managad
//Section-E
//rmanagad@andrew.cmu.edu
//Final Project -- Trigonometric Loading Icons

//This code loops on the basis of the trigonometric functions cosine, sine, and tangent.
//It can be modified to produce three distinct animations, depending on the trig function used,
// and on the axis be affected.


var spins = [];
var drips = [];
var warps = [];
var pluss = [];

function setup() {
	createCanvas(420, 420);

	//Spin Loop, center
	push();
	for (var i = 0; i < 12; i++){
		var spinposition = createVector(i * 20, i);
		var spinradius = i * 1; // Radius increases by 200% for every iteration of i, up to 12.
		spins[i] = new Spin(spinposition, spinradius); //holds circle function in the array
	}  
	pop();

	//Drip loop, top center
	for (var i = 0; i < 8; i++){
		var dripposition = createVector(i*18, i);
		var dripradius = i * 2;
		drips[i] = new Drip(dripposition, dripradius);
	}

	//Warp loop, left center
	for (var i = 0; i < 12; i++){
		var warpposition = createVector(i*20, i);
		var warpradius = i * 1.5;
		warps[i] = new Warp(warpposition, warpradius);
	}

	//Plus loop, left center
	for (var i = 0; i < 12; i++){
		var plusposition = createVector(i*20, i);
		var plusradius = i * 1.5;
		pluss[i] = new Plus(plusposition, plusradius);
	}
}

function draw() {
	background(0);

	//drip draw
	for(var i = drips.length - 1; i >= 0; i--){
		var drip = drips[i];
		drip.update();
		drip.display();
	}  

	//rectangle mask
	fill(0);
	noStroke();
	rect(140, 130, 140, 300);

	//warp draw
	for(var i = warps.length - 1; i >= 0; i--){
		var warp = warps[i];
		warp.update();
		warp.display();
	}  

	//spin draw
	for(var i = spins.length - 1; i >= 0; i--){
		var spin = spins[i];
		spin.update();
		spin.display();
	}  

	//plus draw
	for(var i = pluss.length - 1; i >= 0; i--){
		var plus = pluss[i];
		plus.update();
		plus.display();
	}  
}

//--------------------

//elements of each circle are compounded into the Spin function.
function Spin(spinposition, spinradius){
  this.position = spinposition;
  this.radius = spinradius;
  this.direction = 1;
  this.time = 0; //controls the placement of the starting anchor, time to jump towards.
  //updates position every time draw is called.
  this.update = function(){
	this.time += this.direction * this.radius * 0.007; //holds value of velocity. Higher radius value = higher speed.
	this.position.y = (200 + 90 * cos(this.time))/2;
	this.position.x = (200 + 90 * sin(this.time))/2;
	//trig function directs the animation (slowing down in middle/accelerating the circles as they approach the edges)
  }

  //holds visual presentation
  this.display = function(){
  	noFill();
    stroke(255);
    strokeWeight(1.5);
    ellipse(this.position.x - 20, this.position.y - 20, this.radius, this.radius);  
  }
}

//---------------------

function Drip(dripposition, dripradius){
  this.position = dripposition;
  this.radius = dripradius;
  this.direction = 1;
  this.time = 0; //controls the placement of the starting anchor, time to jump towards.
  //updates position every time draw is called.
  this.update = function(){
	this.time += this.direction * this.radius * 0.004; //holds value of velocity. Higher radius value = higher speed.
	this.position.y = (200 + 90 * tan(this.time))/3;
	//trig function directs the animation (slowing down in middle/accelerating the circles as they approach the edges)
  }

  //holds visual presentation
  this.display = function(){
  	noFill();
    stroke(255);
    strokeWeight(1.5);
    ellipse(this.position.x + 275, this.position.y - 10, this.radius, this.radius);  
  }
}

//------------------------

function Warp(warpposition, warpradius){
  this.position = warpposition;
  this.radius = warpradius;
  this.direction = 1;
  this.time = 0; //controls the placement of the starting anchor, time to jump towards.
  //updates position every time draw is called.
  this.update = function(){
	this.time += this.direction * this.radius * 0.002; //holds value of velocity. Higher radius value = higher speed.
	this.position.y = 200 + 30 * cos(this.time);
	this.position.x = 200 + 30 * tan(this.time);
	//trig function directs the animation (slowing down in middle/accelerating the circles as they approach the edges)
  }

  //holds visual presentation
  this.display = function(){
  	noFill();
    stroke(255);
    strokeWeight(1.5);
    ellipse(this.position.x , this.position.y + 160, this.radius, this.radius);  
  }
}

//----------------------------

function Plus(plusposition, plusradius){
  this.position = plusposition;
  this.radius = plusradius;
  this.direction = 1;
  this.time = 0; //controls the placement of the starting anchor, time to jump towards.
  //updates position every time draw is called.
  this.update = function(){
	this.time += this.direction * this.radius * 0.002; //holds value of velocity. Higher radius value = higher speed.
	this.position.y = 200 + 40 * 1/cos(this.time)/3;
	this.position.x = 200 + 40 * 1/sin(this.time)/3;
	//trig function directs the animation (slowing down in middle/accelerating the circles as they approach the edges)
  }

  //holds visual presentation
  this.display = function(){
  	noFill();
    stroke(255);
    strokeWeight(1.5);
    ellipse(this.position.x , this.position.y, this.radius, this.radius);  
  }
}

If gifs below are frozen: right-click and open the image in a new tab.

Helix loading icon
Warp loading icon
Drip loading icon
Plus loading icon
Spin loading icon
Chain Loading Icon

 

My final project culminated in a series of six loading icons. I used trigonometric functions to calculate x and y positions — because of their cyclical behaviours, sin, cosine, and tangent worked well for looping animations. Individual animation gifs can be seen above. Process work can be seen below

From a design standpoint, I developed these loading icons with several human factors in mind — from longevity and human attention span, to interactivity and engagement. Repetition in form, symmetry in composition and shape, and cyclical rhythm all contribute to keeping people from becoming disengaged with the content. As a component living in a transitionary space, the loading icon serves to bridge one space to the next.

 

Loading icon sketches on Illustrator
initial sketches

dayoungl & kyungak – Final project

Please refer to the gif file for how the game works.The objective of our game, “TOM” is to survive from the bombs by navigating its to stay away from bombs. A little bonus of the game would be eating hearts to achieve higher points. One can navigate the character using the arrow keys. The scoreboard on the top right corner keeps track of the hearts the character eats. Each time character consumes a heart, it will be logged as +1 on the scoreboard; whenever the character comes into contact with the puffs, it will result in -1 point from the scoreboard. When the character comes into contact with the bomb, the game will end with a “gameover” message. When heart and bomb collides with each other, the heart will turn into a little fire icon indicating that the heart is gone.

Download the PLAY-ME version to play the game on your device.

PLAY-ME
GRADE-ME

data-width=”800″ data-height=”800″TOM-FINAL

//Kyunga Ko
//15104B
//kyungak@andrew.cmu.edu
//Capstone Project:Tom and Toms (COMPLETE VERSION)

var bomb;
var puff;
var heart;
var explosion;
var tomcharacter; 
var eating;
var gameover;
var scoreboard;
var score = 0;

function preload() {

  //Preloading gameover and scoreboard image
  gameover = loadImage("https://i.imgur.com/VlLC4xC.png");
  scoreboard = loadImage("https://i.imgur.com/8ke3Z26.png");

}

function setup() {
  createCanvas(800,800);  

  //Grouping variables
  bomb = new Group();
  puff = new Group();
  heart = new Group();
  explosion = new Group();
  tomcharacter = new Group();
  eating = new Group();

  //Create bomb at random locations on canvas
  for(var i=0; i<15; i++) {
    createBomb(random(0,width), random(0,height));
  }

  //Single character "Tom" is created
  for(var i=0; i<1; i++) {
    createTom(100, 100);
  }

}

function draw() {
  background(230);
  //Reduce the size of the heart by the rate of the frameCount
  if(frameCount%60==0 & heart.length<5) {
    //Create hearts at random locations on the canvas
    createHeart(random(0,width), random(0,height)); 
  }
  
  //Recycle puff on four sides = randomly displaced
  if(frameCount%60==0 & puff.length<10) {
    var canvasside = floor(random(0,4));
    
    if(canvasside == 0) //left
      createPuff(0, random(height));
    if(canvasside == 1) //right
      createPuff(width, random(height));
    if(canvasside == 2) //top
      createPuff(random(width), 0);
    if(canvasside == 3) //bottom
      createPuff(random(width), height);
      
    }
  
  //(BOMB) Bomb orientation in general
  for(var i = 0; i<bomb.length; i++) {
  
    var b = bomb[i];
    b.noisePosition += 0.1;
    b.rotation += (noise(b.noisePosition)-0.5)*10;
    b.setSpeed(2, b.rotation);
    randomrelocation(b);

  }
  
  //(PUFF) When puff collides with bomb and heart
  for(var i = 0; i<puff.length; i++) {
      var p = puff[i]; 
      randomrelocation(p);

      for(var j = 0; j<bomb.length; j++) {
        var b = bomb[j]; 
        //Distance btw puff and bomb
        var dis = p5.Vector.dist(p.position, b.position); 
        
        //Puff and bomb does not attract
        if(dis < 70) {   
          var angle = degrees(atan2(b.position.y-p.position.y, 
            b.position.x-p.position.x));
          //repel
          var attraction = -30 / dis;
          p.addSpeed(attraction, angle);

        }
      }

      for(var z = 0; z<heart.length; z++) {
        var h = heart[z]; 
        //Distance btw heart and puff
        var dis2 = p5.Vector.dist(p.position, h.position); 
        
        //Puff and heart attract
        if(dis2 < 30) {   
          var angle2 = degrees(atan2(h.position.y-p.position.y, 
            h.position.x-p.position.x));
          var attraction2 = 100 / dis2;
          p.addSpeed(attraction2, angle2);

        }

      }
  }

  //(HEART) When heart collides with bomb and puff
  for(var i = 0; i<heart.length; i++) {

      var h = heart[i]; //save in a temp variable
      h.scale = map(h.life, 300, 0, 1, 0);
      h.overlap(bomb, bombeatsheart);
      h.overlap(puff, puffeatsheart); 

  }

  //(TOM) When Tom collides with bomb and heart
  for(var i = 0; i<tomcharacter.length; i++) {

    var t = tomcharacter[i]; //save in a temp variable
    t.overlap(bomb, bombmeetstom); 
    t.overlap(heart, heartmeetstom);
    t.overlap(puff, puffmeetstom);
   
  }

  //Scoreboard
  image(scoreboard, 580, 15); //Scoreboard image
  textSize(30);
  fill(0); 
  text(score,670,90); //Displays the score

  //Draws all sprites
  drawSprites();

}

//Makes Tom move up, down, left, right using the arrow keys
function keyPressed(){
  var tab = 20;
  var clickCount = 0;

  for (var i = 0; i<tomcharacter.length; i++ ){
    var t = tomcharacter[i];

    if (keyIsPressed === true){
      clickCount ++; //clickcount increases with movement
    }

    if (keyIsDown(LEFT_ARROW)){
      t.position.x -= tab; //left
    }

    if (keyIsDown(RIGHT_ARROW)){
      t.position.x += tab; //right
    }

    if (keyIsDown(UP_ARROW)){
      t.position.y -= tab; //up

    } else if (keyIsDown(DOWN_ARROW)){
      t.position.y += tab; //down
      }
    
  }
  
}

//The object dissapears outside the canvas and is randomly located again
function randomrelocation(w) {
   //wrap around the screen
    if (w.position.x > width)
      w.position.x = 0;
    if (w.position.x < 0)
      w.position.x = width;
    if (w.position.y > height)
      w.position.y = 0;
    if (w.position.y < 0)
      w.position.y = height;

}

//When puff eats the heart, they multiply x2 
function puffeatsheart(puff, heart) {
  puff.remove();
  createPuff(heart.position.x, heart.position.y);

}

//When Tom meets puff, score decreases by one
function puffmeetstom(puff, tomcharacter) {
  tomcharacter.remove();
  score--;

}

//Bomb eats/gets rid of the heart + explosion sign
function bombeatsheart(bomb, heart) {
  bomb.remove();
  createExplosion(heart.position.x, heart.position.y);
  createExplosion(heart.position.x, heart.position.y);
  createExplosion(heart.position.x, heart.position.y);
  createExplosion(heart.position.x, heart.position.y);
  createExplosion(heart.position.x, heart.position.y);
  createExplosion(heart.position.x, heart.position.y);
  createExplosion(heart.position.x, heart.position.y);
  

}

//Tom eats heart and +1 sign comes up
function heartmeetstom(heart, tomcharacter) {
  tomcharacter.remove();
  aftereatingheart(heart.position.x, heart.position.y);
  score++;
  
}

//When bomb meets Tom, Tom dies and game over sign comes up
function bombmeetstom(bomb, Tom) {
  bomb.remove();
  noLoop();
  push();
  scale(0.7);
  image(gameover, 175, 400);
  pop();

}

//Bomb is created
function createBomb(x, y) {

  var b = createSprite(x, y);
  b.addAnimation("bomb", "https://i.imgur.com/N4m1kty.png");
  b.setSpeed(2, random(0,360));
  b.noisePosition = random(0, 1000);
  b.maxSpeed = 2;
  bomb.add(b);

}

//When bomb eats heart, explosion is created to indicate that a heart was ate
function createExplosion(x, y) {

  var e = createSprite(x, y);
  e.addAnimation("bomb", "https://i.imgur.com/wzVAcbK.png");
  e.setSpeed(2, random(0,360));
  e.noisePosition = random(0, 1000);
  e.maxSpeed = 2;
  explosion.add(e);

}

//After Tom eats heart, +1 sign is created
function aftereatingheart(x, y) {

  var a = createSprite(x,y);
  a.addAnimation("eat", "https://i.imgur.com/b9C1Xyl.png");
  a.setSpeed(2, random(0,360));
  a.noisePosition = random(0, 1000);
  a.maxSpeed = 2;
  eating.add(a);

}

//Puff is created
function createPuff(x, y) {

  var p = createSprite(x, y);
  p.addAnimation("puff", "https://i.imgur.com/cs8Mkcr.png");
  p.setSpeed(-2, random(0,360));
  p.maxSpeed = 1;
  puff.add(p);

}

//Heart is created
function createHeart(x, y) {
  
  var h = createSprite(x, y);
  h.addAnimation("heart", "https://i.imgur.com/u2uRAYl.png");
  h.life = 300; 
  heart.add(h);

}

//Tom is created
function createTom(x, y) {

  var t = createSprite(x, y);
  t.addAnimation("tomcharacter", "https://i.imgur.com/Q8FnPtP.png", 
    "https://i.imgur.com/QzOR227.png");
  tomcharacter.add(t);

}

















eeryan + yoonyouk_Final Project

sketch

// Erin Ryan and Yoon Young Kim
// eeryan@andrew.cmu.edu
// yoonyouk@andrew.cmu.edu
// Section C and Section E
//FINAL PROJECT - water animation


//HOME PAGE VARIABLES
//STARTING CODE WITH HOME PAGE
var animation = 0;
//text position variables
var textStart1 = 0;
var textStart2 = 140;
var textStart3 = 280;
var textStart4 = 420;
var textSpace = 140;

//RAINFALL ANIMATION VARIABLES
var numClicks = 0; 
var fall = false; //starting the animation off
var water = [];
var xr; //x position of water droplets

//COHESION ANIMATION VARIABLES
var move = false;
var drop1; //first water object 
var drop2; //second water object 
var opacity = 100; //variable to deal with changing opacity of fills and strokes of different water objects

//RIPPLE ANIMATION VARIABLES
var rx;
var ry;
var diam;

//SPLASH ANIMATION VARIABLES
var SPLASH = [];
var droplets = [];
var dropX = -50; //x coordinate of the water drop
var dropY = 0; // y coordinate of the water drop
var splashpoint = 150;
var numClick = 0;
var explodeTime;
var timeElapsed;
var splashed = false;

function preload(){
    var splashImgs = [];
    //img links of the splash droplets
    splashImgs[0] = "https://i.imgur.com/Aau3BBu.png";
    splashImgs[1] = "https://i.imgur.com/C156jX4.png";
    splashImgs[2] = "https://i.imgur.com/nnR9v06.png";

    //cycling images by pushing into SPLASH array
    for(var i = 0; i<3; i++){
        SPLASH.push(loadImage(splashImgs[i]));
    }
}

function setup() {
  createCanvas(400, 300);
  //object declaration for cohesion animation
  drop1 = makeDrop(120, height / 2, 100);
  drop2 = makeDrop(width - 120, height / 2,100);
  //pushes raindrop objects into the array water
  for(var i = 0; i < 300; i++){
    xr = random(0, width); 
    water.push(makeRain(xr, 20, 5, 0)); 
  }
}

function draw() {
  //default page
  if(animation === 0){ 
    background(173, 212, 255);
    textSize(20);
    fill(255, 255, 255);
    textFont("Courier New")
    text("let's play with water!", width / 2 - 130, 100);
    menuText(200);
  }
  //ripple animation
  if(animation == 1){ 
    background(238, 252, 255);
    noStroke();
    fill(80, 130, 200);
    textFont("courier", 16);
    textAlign(CENTER);
    text("click to touch the water...", width / 2, 30); // creates text guiding user to press a key
    ripple(rx, ry);
    if(diam < 550){
      diam += 1; //circle expands
      opacity -= 0.4; //circle becomes less opaque as it expands
    }
    menuText(275);
  }
  //splash animation
  else if(animation == 2){
    background(238, 252, 255);
    noStroke();
    fill(80, 130, 200);
    textFont("courier", 16);
    textAlign(CENTER);
    text("click to splash water droplets", width / 2, 30); // creates text guiding user to press a key
    fill(26, 133, 192);
    if(dropY > 0 & dropY < splashpoint){
        droplet(dropX, dropY);
        dropY++;
    }
    if(dropY == splashpoint){
        splashed = true;
        if(explodeTime == null){
          explodeTime = frameCount;  
        }
    }
    if(splashed == true){
        timeElapsed = frameCount - explodeTime;
    for(var i = 0; i < 12; i++){
        if(timeElapsed == i){
            imageMode(CENTER);
            image(SPLASH[floor(i / 4)], dropX, dropY);
            }
        }
    }
    menuText(275);
  }
  //rain code animation
  else if(animation == 3){ 
    background(238, 252, 255);
    fill(80, 130, 200);
    textFont("courier", 16);
    textAlign(CENTER);
    text("click once for to make it rain...", width / 2, 30); // creates text guiding user to press a key
    text("click again for clear skies", width / 2, 55);
    if(fall){ 
      for(var i = 0; i < water.length; i++){
        water[i].render();
        water[i].fall();
      }
    }
    menuText(275);
  }
  //cohesion animation
  else if(animation == 4){ 
    background(238, 252, 255);
    textAlign(CENTER);
    noStroke();
    fill(80, 180, 200);
    textFont("courier", 16);
    text("click to see cohesion...", width / 2, 30);
    drop1.render();
    drop2.render();
    if(dist(drop1.x, drop1.y, drop2.x, drop2.y) > 0 & move){// if the two objects are not on top of each other and move == true
      drop1.x++;                                            // move the two drops towards each other and lessen the opacity of their strokes
      drop2.x--;
      opacity -= 1.3;
    }
    menuText(275);
  }
}
    
function keyTyped(){ // assigns a different value to the variable animation based on the key pressed
    if (key == 'r'){ 
      animation = 1;
    }
    if (key == 's'){ 
      animation = 2;
    } 
    if (key == 'u'){ 
      animation = 3;
    }                                             
    if (key == 'c'){ 
      animation = 4;
    } 
    
}

function menuText(posY){ //makes menu scroll with instructions
    noStroke();
    fill(80, 130, 200);
    textSize(10);
    text("press R for ripples", textStart1, posY);
    text("press S for splash", textStart2, posY);
    text("press U for rain", textStart3, posY);
    text("press C for cohesion", textStart4, posY);
    textStart1++;
    textStart2++;
    textStart3++;
    textStart4++;
    if(textStart1 > width){
        textStart1 = -140;
    }    
    if(textStart2 > width){
        textStart2 = -140;
    }
    if(textStart3 > width){
        textStart3 = -140;
    }
    if(textStart4 > width){
        textStart4 = -140;
    }
}

function mousePressed(){
  //variable reset for ripple code
  diam = 0;
  opacity = 100;
  rx = mouseX;
  ry = mouseY;
  if(animation == 2){
    numClick++;
    dropX = mouseX;
    dropY = 1;
    splashpoint = mouseY;
  }

  //rain
  if(animation == 3){
    numClicks++;
    if(numClicks%2 == 1){
      fall = true;
    }
    else{
      fall = false;
    }
  }

  //cohesion
  if(animation == 4){
    move = true;
  }
}

function ripple(px, py){ //draws ripples
  noFill();
  strokeWeight(2.5);
  stroke(0, 0, 255, opacity);
  ellipse(px, py, diam, diam);
  strokeWeight(2);
  ellipse(px, py, diam / 2, diam / 2);
  strokeWeight(1.5);
  ellipse(px, py, diam / 4, diam / 4);
}

function droplet(x, y){
  fill(26, 133, 192);
  //using the map function in order to expand the drop as it moves down
  var xmap = map(dropY, 0, height, 5, 15);
  var ymap = map(dropY, 0, height, 10, 30);
  noStroke();
  beginShape();
  curveVertex(x, y);
  curveVertex(x, y);
  curveVertex(x - xmap, y + ymap);
  curveVertex(x, y + ymap + xmap);
  curveVertex(x + xmap, y + ymap);
  curveVertex(x, y);
  curveVertex(x, y);
  endShape();    
}

//object implementation for cohesion animation
function drawDrop(){ //draws water drop for cohesion animation
  stroke(80, 130, 200, opacity);
  strokeWeight(3);
  fill(230, 242, 255);
  ellipse(this.x, this.y, this.w, this.w);
  noStroke();
}

function makeDrop(posX, posY, diam){//make water drop object for cohestion animation
  var drop = {x:posX, y:posY, w:diam, render:drawDrop};
  return drop;
}

//object implementation for rain animation
function makeRain(px, py, diam, velocity){ //make rain object
  var raindrop = {x:px, y:py, d:diam, v:velocity, render: drawRain, fall:rainFall};
  return raindrop;
}

//draws raindrop
function drawRain(){
  noStroke();
  fill(0, 10, 150, 60);
  ellipse(this.x, this.y, this.d, this.d);
  triangle(this.x - (this.d)/2, this.y, this.x, this.y - 5, this.x + (this.d)/2, this.y);
}

function rainFall(){
  this.v = random(0,8); //randomizess velocity variable
  this.y += this.v; //adds velocity to current y value of each rain drop objects so they'll fal at different speeds
  if(this.y >= height){ //resets rain drops to the top once it hits the bottom of the page
    this.y = 0;
  }
}

For this project, I collaborated with Erin Ryan from Lab section C to make a series of four water-based animations using different animation, interactive, and object oriented techniques. We coded the four interactive animations and the home screen separately, then used a series of conditionals to allow the user to toggle between different animations. We tried to establish cohesive visual language through use of color and simple shapes.

 

Initial sketches of water animations

mecha-alchan-final-project

Use the number keys in order to progress in the story!

sketch

var playerChoices = [];
var currentScene = 0;
var sceneList = [];

var leggyFish = [];
var leggyFishLoad = [];

var cat = [];
var catLoad = [];

var catFamily = [];
var catFamilyLoad = [];

var possums = [];
var possumsLoad = [];

var backgrounds = [];
var backgroundsLoad = [];

var endingImages = [];
var endingImagesLoad = [];

var items = [];
var itemsLoad = [];

function preload(){
    //preloads images for background, characters, items, etc.
    //imgur file link https://imgur.com/a/I9BTC

    //backgrounds
    //home
    backgrounds[0] = "https://i.imgur.com/fdhG0uX.png";
    //store
    backgrounds[1] = "https://i.imgur.com/XLXhiQ1.png";
    //street
    backgrounds[2] = "https://i.imgur.com/C9gVejq.png";
    //possum ending
    backgrounds[3] = "https://i.imgur.com/hoVazkD.png";
    //lose ending
    backgrounds[4] = "https://i.imgur.com/Dyp8s1Z.png";

    //characters
    //cat neutral
    cat[0] = "https://i.imgur.com/3edcYR9.png";
    //cat sad
    cat[1] = "https://i.imgur.com/u1bIhz2.png";
    //cat happy
    cat[2] = "https://i.imgur.com/YFuQ8tH.png";

    //cat family neutral
    catFamily[0] = "https://i.imgur.com/iWE061b.png";
    //cat family angry
    catFamily[1] = "https://i.imgur.com/dwXS352.png";
    //cat family happy
    catFamily[2] = "https://i.imgur.com/gJDwiOw.png";
    //cat full family
    catFamily[3] = "https://i.imgur.com/qJ2XmCm.png";

    //leggy fish neutral
    leggyFish[0] = "https://i.imgur.com/afO8XhG.png";
    //leggy fish sad
    leggyFish[1] = "https://i.imgur.com/h90qSVH.png";
    //leggy fish happy
    leggyFish[2] = "https://i.imgur.com/kSy0YDp.png";

    //possums neutral
    possums[0] = "https://i.imgur.com/BV6DYd7.png";
    //possums mad
    possums[1] = "https://i.imgur.com/2EGHdrk.png";

    //water ending
    endingImages[0] = "https://i.imgur.com/QwObsf3.png";
    //milk ending
    endingImages[1] = "https://i.imgur.com/NO4hAnK.png";
    //bad possum ending
    endingImages[2] = "https://i.imgur.com/smECEKi.png";
    //good possum ending
    endingImages[3] = "https://i.imgur.com/EX8XQjv.png";
    //bad cat ending
    endingImages[4] = "https://i.imgur.com/mjmR2QE.png";
    //good cat ending
    endingImages[5] = "https://i.imgur.com/Xj8lU3S.png";
    //leggy fish ending
    endingImages[6] = "https://i.imgur.com/ni1ZpWr.png";

    //knock off oreos image
    items[0] = "https://i.imgur.com/0RvrPHJ.png";
    //fish
    items[1] = "https://i.imgur.com/cIw1WkF.png";
    //grocery store PANTS
    items[2] = "https://i.imgur.com/bRV6gGk.png";
    //milk
    items[3] = "https://i.imgur.com/Psx2phG.png";

    // load all images and add to appropriate arrays
    for(var b = 0; b < backgrounds.length; b++){
        backgroundsLoad[b] = loadImage(backgrounds[b]);
    }
    for(var c = 0; c < cat.length; c++){
        catLoad[c] = loadImage(cat[c]);
    }
    for(var cF = 0; cF < catFamily.length; cF++){
        catFamilyLoad[cF] = loadImage(catFamily[cF]);
    }
    for(var l = 0; l < leggyFish.length; l++){
        leggyFishLoad[l] = loadImage(leggyFish[l]);
    }

    for(var p = 0; p < possums.length; p++){
        possumsLoad[p] = loadImage(possums[p]);
    }

    for(var e = 0; e < endingImages.length; e++){
        endingImagesLoad[e] = loadImage(endingImages[e]);
    }

    for(var i = 0; i < items.length; i++){
        itemsLoad[i] = loadImage(items[i]);
    }
}

function createScenes() {
    // create all scenes & choices and add them to an array
    // format: main dialogue, choice 1 text, choice 1 next scene #,
    //         choice 2 text, choice 2 next scene #,
    //         choice 3 text (or "none"), choice 3 next scene,
    //         background image, character image
    // should set up a spreadsheet to keep track of scene nums

    //scenes at home
    sceneList.push(makeScene("you wake up", "time for cereal", 1,
    "none", 0, "none", 0, backgroundsLoad[0], backgroundsLoad[0]));
    sceneList.push(makeScene("oh no! you're out of milk", "go to store", 4,
    "water's basically the same thing", 2, "none", 0, backgroundsLoad[0], backgroundsLoad[0]));

    //ENDING 1
    sceneList.push(makeScene("this cereal is reacting strangely to water...","BOOM!!!",3,"none",0,"none",0,backgroundsLoad[0], backgroundsLoad[0]));
    sceneList.push(makeScene("your cereal spontaneously combusted", "none", 0, "none", 0, "none", 0, backgroundsLoad[0],backgroundsLoad[0]));

    //store scenes
    sceneList.push(makeScene("the sun is shining as you walk down the street", "head to the store", 5, "none", 0, "none", 0, backgroundsLoad[2], backgroundsLoad[2]))
    sceneList.push(makeScene("you're here for milk, but you're tempted to pick something else up", "resist the temptation", 6,
    "wander the aisles", 9, "none", 0, backgroundsLoad[1], backgroundsLoad[1]));
    sceneList.push(makeScene("you're on a mission", "take your milk home", 7, "actually, wait...", 9, "none", 0, backgroundsLoad[1], backgroundsLoad[1]));

    //ENDING 2
    sceneList.push(makeScene("you eat your cereal", "wow, you're so boring", 8, "none", 0, "none", 0, backgroundsLoad[0], backgroundsLoad[0]));
    sceneList.push(makeScene("technically you win, but you are a loser", "none", 0, "none", 0, "none", 0, backgroundsLoad[0], backgroundsLoad[0]));

    sceneList.push(makeScene("you head to the...", "junk food aisle", 10, "seafood section", 11, "dairy section", 6, backgroundsLoad[1], backgroundsLoad[1]));
    sceneList.push(makeScene("you do love your sweets", "let's get some roreos", 13, "aren't you on a diet", 9, "wait what's that", 12, backgroundsLoad[1], backgroundsLoad[1]));
    sceneList.push(makeScene("the fish are so fresh", "why not?", 25, "you don't even like seafood", 9, "none", 0, backgroundsLoad[1], backgroundsLoad[1]));
    sceneList.push(makeScene("are those........ pants", "grocery store pants oh heck yeah", 42, "you don't trust no grocery store pants", 9, "none", 0, backgroundsLoad[1], backgroundsLoad[1]));
    sceneList.push(makeScene("you have just enough money", "time to go home", 14, "none", 0, "none", 0, backgroundsLoad[1], backgroundsLoad[1]));

    //possum scenes
    sceneList.push(makeScene("you stroll down the street", "what's that?", 15, "none", 0, "none", 0, backgroundsLoad[2], backgroundsLoad[2]));
    sceneList.push(makeScene("something emerges from a trah can", "say hi to the... possums?", 16, "none", 0, "none", 0, backgroundsLoad[2], possumsLoad[0]));
    sceneList.push(makeScene("the possums eye your purchase", "excuse YOU, these are mine", 17, "offer a cookie", 20, "none", 0, backgroundsLoad[2], possumsLoad[0]));
    sceneList.push(makeScene("the possums watch disapprovingly as you walk down the street", "whatever, time for cookies", 18, "none", 0, "none", 0, backgroundsLoad[2], possumsLoad[1]));

    //ENDING 3
    sceneList.push(makeScene("every bite of cookie tastes like regret", "the possums have cursed you", 19, "none", 0, "none", 0, backgroundsLoad[0], backgroundsLoad[0]));
    sceneList.push(makeScene("death is coming for you", "none", 0, "none", 0, "none", 0, backgroundsLoad[0], backgroundsLoad[0]));

    sceneList.push(makeScene("the possum grabs the entire package out of your hands and vanishes", "oh well", 21, "none", 0, "none", 0, backgroundsLoad[2], possumsLoad[0]));
    sceneList.push(makeScene("you unlock the door with no cookies or milk", "you hear strange rustling sounds", 22, "none", 0, "none", 0, backgroundsLoad[2], backgroundsLoad[2]));
    sceneList.push(makeScene("there's an army of possums in your house", "welcome your new family", 23, "none", 0, "none", 0, backgroundsLoad[0], backgroundsLoad[3]));

    //ENDING 4
    sceneList.push(makeScene("they push forward a gallon of milk", "you did the right thing", 24, "none", 0, "none", 0, backgroundsLoad[0], backgroundsLoad[3]));
    sceneList.push(makeScene("the possums are in the house", "none", 0, "none", 0, "none", 0, backgroundsLoad[0], backgroundsLoad[3]));

    //cat scenes
    sceneList.push(makeScene("you have just enough money", "time to go home", 26, "none", 0, "none", 0, backgroundsLoad[1], backgroundsLoad[1]));
    sceneList.push(makeScene("you stroll down the street", "something scurries by", 27, "none", 0, "none", 0, backgroundsLoad[2], backgroundsLoad[2]));
    sceneList.push(makeScene("a scrawny cat begs for help", "you coldly walk on", 28, "aw, he looks hungry", 34, "none", 0, backgroundsLoad[2], catLoad[1]));
    sceneList.push(makeScene("you are heartless, but not fishless", "continue on your way", 29, "none", 0, "none", 0, backgroundsLoad[2], backgroundsLoad[2]));
    sceneList.push(makeScene("as you round the corner, you hear more meows", "it's a family of cats", 30, "none", 0, "none", 0, backgroundsLoad[2], catFamilyLoad[0]));
    sceneList.push(makeScene("you stare at each other", "suddenly, one speaks", 31, "none", 0, "none", 0, backgroundsLoad[2], catFamilyLoad[0]));
    sceneList.push(makeScene("where's father? he was supposed to bring us food", "uh oh...", 32, "none", 0, "none", 0, backgroundsLoad[2], catFamilyLoad[0]));

    //ENDING 5
    sceneList.push(makeScene("you remember the cat from before", "should have given him the fish", 33, "prepare to be eaten", 33, "none", 0, backgroundsLoad[2], catFamilyLoad[1]));
    sceneList.push(makeScene("you become cat food", "none", 0, "none", 0, "none", 0, backgroundsLoad[2], catFamilyLoad[1]));

    sceneList.push(makeScene("the cat politely takes the fish", "farewell sir", 35, "none", 0, "none", 0, backgroundsLoad[2], catLoad[0]));
    sceneList.push(makeScene("as you round the corner, you hear more meows", "it's a family of cats", 36, "none", 0, "none", 0, backgroundsLoad[2], catFamilyLoad[0]));
    sceneList.push(makeScene("you smile at them", "the cat from before appears!", 37, "none", 0, "none", 0, backgroundsLoad[2], catFamilyLoad[3]));
    sceneList.push(makeScene("he gives the fish to his family", "you swear you hear them say ''thanks'' to you", 38, "none", 0, "none", 0, backgroundsLoad[2], catFamilyLoad[3]));
    sceneList.push(makeScene("that was nice", "time to go home, finally", 39, "none", 0, "none", 0, backgroundsLoad[2], backgroundsLoad[2]));
    sceneList.push(makeScene("when you get home, there's a package at your door", "there's a paw print on it", 40, "none", 0, "none", 0, backgroundsLoad[0], backgroundsLoad[0]));

    //ENDING 6
    sceneList.push(makeScene("it's... a gallon of milk", "you tearfully thank the cats", 41, "none", 0, "none", 0, backgroundsLoad[0], backgroundsLoad[0]));
    sceneList.push(makeScene("you have been blessed by cats", "none", 0, "none", 0, "none", 0, backgroundsLoad[0], backgroundsLoad[0]));

    //leggy fish
    sceneList.push(makeScene("you have just enough money", "time to go home", 43, "none", 0, "none", 0, backgroundsLoad[1], backgroundsLoad[1]));
    sceneList.push(makeScene("you stroll down the street", "there's something odd over there", 44, "none", 0, "none", 0, backgroundsLoad[2], backgroundsLoad[2]));
    sceneList.push(makeScene("a fish head peaks at you from behind a bush", "why does this fish has legs", 45, "none", 0, "none", 0, backgroundsLoad[2], leggyFishLoad[0]));
    sceneList.push(makeScene("you should really get to know your neighbors", "...some other time", 46, "he looks sad and cold", 47, "none", 0, backgroundsLoad[2], leggyFishLoad[1]));
    sceneList.push(makeScene("what are you, heartless", "those pants won't fit you anyways", 47, "none", 0, "none", 0, backgroundsLoad[2], leggyFishLoad[1]));
    sceneList.push(makeScene("you look at your grocery store pants", "give the fish pants", 48, "give the fish pants", 48, "GIVE THE FISH PANTS", 48, backgroundsLoad[2], leggyFishLoad[1]));
    sceneList.push(makeScene("the fish hurriedly puts them on", "leggy fish looks quite dapper in those pants", 49, "none", 0, "none", 0, backgroundsLoad[2], leggyFishLoad[2]));
    sceneList.push(makeScene("you wave good bye", "leggy fish signals you to wait", 50, "none", 0, "none", 0, backgroundsLoad[2], leggyFishLoad[2]));
    sceneList.push(makeScene("the fish pulls a box from behind the bush", "thank you?", 51, "none", 0, "none", 0, backgroundsLoad[2], leggyFishLoad[2]));
    sceneList.push(makeScene("you head home with the box", "let's see what's inside", 52, "none", 0, "none", 0, backgroundsLoad[0], backgroundsLoad[0]));

    //ENDING 7
    sceneList.push(makeScene("it's a gallon of milk", "wow, you have such nice neighbors", 53, "none", 0, "none", 0, backgroundsLoad[0], backgroundsLoad[0]));
    sceneList.push(makeScene("this is the true ending", "none", 0, "none", 0, "none", 0, backgroundsLoad[0], backgroundsLoad[0]));
}

function setup() {
    createCanvas(480, 360);
    createScenes();
}

function draw() {
    // render the current scene
    sceneList[currentScene].draw();
}

function keyPressed() {
    var sceneToChange;
    // check which key is pressed
    // & switch to the correct scene depending on the current scene
    if(key == 1) {
        sceneToChange = sceneList[currentScene].next1;
        currentScene = sceneToChange;
    } else if(key == 2 & sceneList[currentScene].opt2 != "none") {
        sceneToChange = sceneList[currentScene].next2;
        currentScene = sceneToChange;
    }
    // make sure this is a 3rd option
    else if(key == 3 & sceneList[currentScene].opt3 != "none") {
        sceneToChange = sceneList[currentScene].next3;
        currentScene = sceneToChange;
    }
}

function drawScene() {
    // draw background image
    image(this.bg,0,0,480,360);

    // draw character or item
    image(this.character,0,0,480,360);

    // text underlay
    fill(250, 250, 250, 200);
    noStroke();
    rect(0, (2/3)*height, width,(1/3)*height);

    // draw ending text
    if(currentScene == 3 || currentScene == 8 || currentScene == 19 || currentScene == 24 || currentScene == 33 || currentScene == 41 || currentScene == 53){
        textAlign(CENTER);
        image(backgroundsLoad[4],0,0);
        fill(255);
        text("press '1' to play again ", width/2, 290);
    }
    else{
        fill(0);
    }

    //adds items
    if(currentScene == 12){
        image(itemsLoad[2],0,0);
    }
    if(currentScene == 10){
        image(itemsLoad[0],0,0);
    }
    if(currentScene == 11){
        image(itemsLoad[1], 0, 0);
    }
    if(currentScene == 6 || currentScene == 40 || currentScene == 52 || currentScene == 23){
        image(itemsLoad[3],0,0);
    }

    //adds ending images
    if(currentScene == 3){
        image(endingImagesLoad[0],0,0);
    }
    if(currentScene == 8){
        image(endingImagesLoad[1],0,0);
    }
    if(currentScene == 19){
        image(endingImagesLoad[2],0,0);
    }
    if(currentScene == 24){
        image(endingImagesLoad[3],0,0);
    }
    if(currentScene == 33){
        image(endingImagesLoad[4],0,0)
    }
    if(currentScene == 41){
        image(endingImagesLoad[5],0,0);
    }
    if(currentScene == 53){
        image(endingImagesLoad[6],0,0);
    }
    // draw main dialogue
    textAlign(CENTER);
    textStyle(NORMAL);
    textFont("Courier", 12);
    text(this.dialogue, width/2, 260);

    // draw options
    textAlign(LEFT);
    textStyle(ITALIC);
    // check if each option exists before drawing it
    if(this.opt1 != "none"){
        text("1. " + this.opt1, width/2-150, 290);
    }
    if(this.opt2 != "none") {
        text("2. " + this.opt2, width/2-150, 310);
    }
    if(this.opt3 != "none") {
        text("3. " + this.opt3, width/2-150, 330);
    }
}

// create a scene object
function makeScene(words, one, n1, two, n2, three, n3, b, c) {
    var scene = {dialogue: words,
        opt1: one,
        next1: n1,
        opt2: two,
        next2: n2,
        opt3: three,
        next3: n3,
        bg: b,
        character: c,
        draw: drawScene}
        return scene;
    }

For this project, I collaborated with Allissa Chan in order to create a choice-based game revolving around a trip to the grocery store. We used objects in order to implement all of the scenes, and created our graphics using Adobe Photoshop. There are seven different routes/endings within the game (including our definition of a “true” ending).




jwchou-Project-Proposal v2

Note: This is my new proposal.

My new proposal is to create an interactive game is controlled by the arrow keys on the keyboard.

I want to create an airplane landing game, sort of inspired by the game/app Flight Control which used to be quite popular. I wanted to do this to continue my series of airplane-themed projects.

I’m imagining that the game will progress either through different levels, or just naturally increase in hardness (ie the planes fly faster, are harder to control, etc). I might try to make it with more than one plane in the frame at a time, but if I can’t execute that, I might just stick with one plane at a time to keep things simple.

The styling will probably be simple and contemporary, and I might explore sound feedback as well, if possible.

I plan to work on this by myself.

 

dayoungl Looking Outwards 12

 

For the final project, we took our concept from “Pac Man” and an online game called “agar.io”. Because everyone knows how Pac Man goes, I will discuss more on agar.io. My first encounter with agar.io was when I was back in high school. Everyone in my grade used to play this game and how this game works is that you simply log-in to the team server and you can play with your friends that way. This game was developed by  a 19 ear old Matheus Valadares. The concept of the game is to “bulk up” by eating other cells in the game; more specifically, The objective of Agar.io is to grow a cell on a petri dish by swallowing both randomly generated pellets (agar), which slightly increases a cell’s mass, and smaller cells, without being swallowed by larger cells. This game was coded using C++ and javascript.

Here is a link to the game.

 

jwchou-Project-12-Proposal

I want to create a project that makes politics more visible.

Often, stories break out about President Trump and his policies, but how they may affect people is often unclear. This can be seen in his Muslim Ban, his tweets, or his executive orders, or his diplomatic actions.

I want to create an interactive quiz/poll that shows how Trump might be doing something that directly affects you. For example… qualifiers could be

-being transgender

-being undocumented

-having a specific country of origin

-etc

In addition to being informative to a specific audience, I also want the project to be informative to a more general audience so they can find out how Trump is affecting others.

I’m imagining this project to be a sort of interactive javascript application, where users can click and interact with the different information being presented. I’m a bit hesitant about pursuing this project, because I’m not sure the text formatting tools in P5.Js are powerful enough in order to design a good interface.

Here is an EXTREMELY rough diagram of what it could look like (I promise I have better skills than this). Imagine each box to be a different qualifier, and which one clicks on the box, more information comes up about Trump and his actions.

jwchou-LookingOutwards-12

I think I want to base my project around something political, because I think art/design can be very political.

Here are two simple projects that help make politics most digestable and visible, and I think that is in the vein of what I want to do.

Trump Tracker by Viren Mohindra


This simple website tracks Trump’s promises and highlights them using a color code, depending on if he broke or kept his promise.

Tracking Trump’s Agenda, Step by Step
This is yet another web-based infographic by the New York Times. It shows a chronological timeline of the president’s actions and how they affect people of different populations. Each piece in the timeline links to an article about the specific event.

 

Both of these are simple web-apps with very primitive interactions. I appreciate how direct they are and how they make following politics more visual and more accessible. However, I think in order for them to be truly effective, they need to be more personalized to the person accessing them. I plan to move in this direction for my project.