katieche – final project

final project

// BORROWED FROM IRA GREENBERG
var radius = 45, rotAngle = -90;
var accelX = 0.0, accelY = 0.0;
var deltaX = 0.0, deltaY = 0.0;
var springing = 0.0009, damping = 0.98;

//corner nodes
var nodes = 5;

//zero fill arrays
var nodeStartX = [];
var nodeStartY = [];
var nodeX = [];
var nodeY = [];
var angle = [];
var frequency = []; // END OF BORROWED CODE

// array for the rings and boxes
var rings = [];
var boxes = [];

// initializing colors for keypressed changes
var r = 250;
var g = 250;
var b = 250;

var frames = [];

//var score = 0;

function preload() { // loading ring popping animation frames
	var f = [];
		f[0] = "https://i.imgur.com/AZJNy6P.png";
		f[1] = "https://i.imgur.com/coAJvFm.png";
		f[2] = "https://i.imgur.com/Y3RE12y.png";
		f[3] = "https://i.imgur.com/9sTJNBz.png";


		for(var i =0; i < 4; i++){
			frames[i] = loadImage(f[i]);
		}
}


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


  // BORROWED FROM IRA GREENBERG: initialize arrays to 0
  for (var i=0; i<nodes; i++){
    nodeStartX[i] = 0;
    nodeStartY[i] = 0;
    nodeY[i] = 0;
    nodeY[i] = 0;
    angle[i] = 0;
  } // END OF BORROWED CODE

  // initializes collection of rings 
  for (var i = 0; i < 3; i++){
    	var ry = random(-300, 0); //rings are falling from above the canvas at random
        rings[i] = makeRing(ry);
        boxes[i] = makeBox(ry);
        }

  // BORROWED FROM IRA GREENBERG: iniitalize frequencies for corner nodes
  for (var i = 0; i < nodes; i++){
    frequency[i] = random(4, 5);
  } // END OF BORROWED CODE

  noStroke();
  frameRate(30);
}

function draw() {
	fill(50);
	rect(0,0,width, height);

	// display rings + boxes
	updateAndDisplayRings();
	ringAdd();

	updateAndDisplayBox();
	boxAdd();

	// for the character
  	drawShape();
  	moveShape();

  	// instructional text
  	text("press 'Q' to eat blue, 'W' to eat pink, 'E' to eat yellow", 180, 450);


}
// CHARACTER
// CODE IS BASED ON IRA GREENBERG'S SOFT BODY EXAMPLE:
// https://p5js.org/examples/simulate-soft-body.html
// HAS BEEN HEAVILY EDITED BY ME
function drawShape() {
  //  calculate node  starting locations
  for (var i=0; i<nodes; i++){
    nodeStartX[i] = mouseX+cos(radians(rotAngle))*radius;
    nodeStartY[i] = mouseY+sin(radians(rotAngle))*radius;
    rotAngle += 360.0/nodes;
  }

  // draw polygon
  curveTightness(0.5); // to create soft body like organic shape
  noStroke();
  fill(r,g,b);
  beginShape();
  // creates a point at each (nodeX, nodeY) for the 5 corners in the array
  for (var i=0; i<nodes; i++){
    curveVertex(nodeX[i], nodeY[i]);
  }
  // to make shape connect all the way around
  for (var i=0; i<nodes-1; i++){
    curveVertex(nodeX[i], nodeY[i]);
  }
  endShape(CLOSE); // END OF BORROWED CODE

  // eyes
  fill(149, 166, 193);
  ellipse(mouseX+30, mouseY, 10, 10);
  ellipse(mouseX-30, mouseY, 10, 10);

  push();
  noFill();
  stroke(149, 166, 193);
  strokeWeight(3);
  ellipse(mouseX+30, mouseY, 15, 15);
  ellipse(mouseX-30, mouseY, 15, 15);
  pop();
}


function moveShape() { // BORROWED FROM IRA GREENBERG
  //move center point
  deltaX = mouseX;
  deltaY = mouseY;

   //create springing effect
  deltaX *= springing;
  deltaY *= springing;
  accelX += deltaX*0.8;
  accelY += deltaY*0.8;

  // slow down springing
  accelX *= damping;
  accelY *= damping;

  //move nodes
  for (var i=0; i<nodes; i++){
    nodeX[i] = nodeStartX[i]+sin(radians(angle[i]))*(accelX);
    nodeY[i] = nodeStartY[i]+sin(radians(angle[i]))*(accelY);
    angle[i] += frequency[i];
  }
} // END OF BORROWED CODE

function keyPressed() { // changes blob's color

    if(keyCode == 81) { // "Q" blue
        r = 202;
        g = 220;
        b = 247;
    }
    if(keyCode == 87) { // "W" pink
        r = 247;
        g = 207;
        b = 207;
    }
    if(keyCode == 69) { // "E" yellow
        r = 255;
        g = 241;
        b = 193;
    }
}

// RINGS
function updateAndDisplayRings(){
    // Update the rings positions, and display them.
    for (var i = 0; i < rings.length; i++){
        rings[i].move();
        rings[i].display();
    }
}


function ringAdd() {
    // With a very tiny probability, add a new ring to the end.
    var newringLikelihood = 0.02; 
    if (random(0,1) < newringLikelihood) {
        rings.push(makeRing(0));
        rings.push(makeRing(0));

    }
}

// moving the rings
function ringMove() {
    this.y += this.speed;

}
    
// drawing the rings
function ringDisplay() {
	noFill();
	stroke(210);
	strokeWeight(2);

	if (dist(mouseX, mouseY, this.x, this.y) <= 50) { // if the mouse is 50 points close to the ring, it pops
		this.isPopped = true;
		this.timePopped = frameCount; // record the framecount at which it pops
	}


	if (this.isPopped == false) { // if the state of the ring is not popped
		ellipse(this.x, this.y, this.diam, this.diam);
	}

	else {
		this.popped(); // if this.isPopped == true, then calls ringPop which displays pop animation

	}


}

function ringPop() { // animating rings popping
	var num = frameCount-this.timePopped;
	if (num >= frames.length) {
		// if num goes past the number of animation frames, the pop dissapears
	}
	
	else {
    image(frames[num], this.x, this.y, 50, 50);
	}
}


// making the rings
function makeRing(cy) {
    var ring = {y: cy,
    			x: random(0,600),
    			diam: random(15,25),
 				speed: random(2.0, 3.0),
                move: ringMove,
                display: ringDisplay,
                isPopped: false,
                popped: ringPop,
                timePopped: -10
            }
    return ring;
}

// SQUARES
function updateAndDisplayBox(){
    // Update the clouds' positions, and display them.
    for (var i = 0; i < boxes.length; i++){
        boxes[i].bmove();
        boxes[i].bdisplay();
    }
}


function boxAdd() {
    // With a very tiny probability, add a new ring to the end.
    var newboxLikelihood = 0.02; 
    if (random(0,1) < newboxLikelihood) {
        boxes.push(makeBox(0));
    }
}

// moving the rings
function boxMove() {
    this.by += this.bspeed;
    this.py += this.pspeed;
    this.yy += this.yspeed;

}
    
// drawing the rings
function boxDisplay() {

	// blue box
	if (dist(mouseX, mouseY, this.bx, this.by) <= 50 & r == 202 && g == 220 && b == 247) { 
	// if the mouse is 50 points close to the box and the blob is blue
		this.bisEaten= true;
	}


	if (this.bisEaten == false) { // if the state of the box is not eaten
		noStroke();
		fill(202, 220, 247);
		rect(this.bx, this.by, this.bs, this.bs, 5);
	}

	else {
	}

	// pink box
	if (dist(mouseX, mouseY, this.px, this.py) <= 50 & r == 247 && g == 207 && b == 207) { 
	// if the mouse is 50 points close to the box and the blob is pink
		this.pisEaten = true;
	}


	if (this.pisEaten == false) { // if the state of the box is not eaten
		noStroke();
		fill(247, 205, 202);
		rect(this.px, this.py, this.bs, this.bs, 5);
	}

	else {
		// box disapears when state is true
	}

	// yellow box
	if (dist(mouseX, mouseY, this.yx, this.yy) <= 50 & r == 255 && g == 241 && b == 193) { 
	// if the mouse is 50 points close to the box and the blob is pink
		this.yisEaten = true;
	}

	if (this.yisEaten == false) { // if the state of the box is not eaten
		noStroke();
		fill(255, 241, 193);
		rect(this.yx, this.yy, this.bs, this.bs, 5);
	}

	else {
		// box disapears when state is true
	}

}

// making the rings
function makeBox(ty) {
    var box = {	// blue box coord
    			by: ty,
    			bx: random(0,600),
    			// pink box coord
    			py: random(0,200),
    			px: random(0,600),
    			// yellow box coord
    			yy: random(0,200),
    			yx: random(0,600),

    			bs: random(20,25),
 				bspeed: random(1.0, 2.0),
 				pspeed: random(1.0, 2.0),
 				yspeed: random(1.0, 2.0),
                bmove: boxMove,
                bdisplay: boxDisplay,

                bisEaten: false, // blue false
                pisEaten: false, // pink false
                yisEaten: false, // yellow false
            }
    return box;
}

For my final project, I wanted to create a game with a fun ambiguous character. In a way, the game is like a fight between an organic shape and geometric shapes. It’s slightly different from my proposal but I like the way it turned out. It uses a modified version of the generative landscape functions, making the terrain (falling objects) fall downwards instead of sideways. It was difficult to modify the code so that the shapes would fall in a way that was relatively even, and not in lines or waves. The character is done using a heavily modified version of the soft body code found on the p5.js examples.

The objective of the game is to clear the screen! The rings pop whenever the character touches them, but the character’s own color must match up with the color of the squares to eat them. To change the character’s color, the player must select Q, W or E on their keyboard.

Leave a Reply