mmiller5-Final Project

Instructions: Press any key to continue when text is finished scrolling.  Click on choice boxes to select choice.

sketch

//choice variables
var choiceNow = false;
var choiceSoon = false;
var lastChoice = 1;
var buttonChoice = [0, 0, 0, 0, 0];
var choice1 = ["Go Forward", "Feel Around", "Stay Still"];
var choice2 = ["Go Back", "Kick", "Fall Down"];
var choice3 = ["Scarlet", "Cerulean", "Emerald", "None"];
var doorUnlocked = false;

//text variables
var textAll = []; //array containing all text objects
var textList = [
    //0-2
    "You awake to find yourself in a dark room.",
    "You can't see anything, probably because it is dark.",
    "What are you going to do?",
    //3-5
    "You decide to move forward, not that it really matters what direction you move in because it all looks the same.",
    "This area of the room smells like apples; it makes you nauseous.",
    "Now what are you going to do?",
    //6-9
    "You decide that this area isn't as good as the last one, so you walk backwards to whence you came.",
    "As you moonwalk to your origin, the apple smell dissipates, easing your uneasiness.",
    "Now back in your starting area, you feel a calm serenity wash over your eyebrows.",
    "What are you going to do?",
    //10-13
    "You decide to wave your arms frantically, almost instantly thwacking a wall to your left.",
    "After massaging your injured hand, you run your hand across the wall and notice some buttons.",
    "There are 3 buttons, and you are stupidly confident that you know their colors even though it is pitch black.",
    "Which button do you wish to press?",
    //14-15
    "You hear a satisfying 'Click' as you press the Scarlet button.",
    "Which button do you wish to press?",
    //16-17
    "The Cerulean button gives you much resistance, but you eventually manage to become victorious and press the button.",
    "Which button do you wish to press?",
    //18-19
    "You feel an exhilerating rush of energy enter your body through your fingertip as you press the Emerald button.",
    "Which button do you wish to press?",
    //20-22
    "You decide that the buttons aren't worthy of your tender carresses and go back.",
    "You are back where you were before, it's just as dark as before.",
    "What are you going to do?",
    //23-29
    "You decide that this is probably all a dream and that the proper course of action is to do nothing.",
    "After doing nothing for so long, you become drowsy, eventually falling asleep on the floor.",
    "zzzzzzzzz",
    "...",
    "You awake to find yourself in a dark room.",
    "You can't see anything, probably because it is dark.",
    "What are you going to do?",
    //30-34
    "You decide to vent your frustrations by violently thrusting your foot forward, only for it to meet a metal object.",
    "The metal object wins, leaving your foot in a sorry state.",
    "You examine the metal object more closely and discover that it's a door, but it's locked.",
    "You recover from your defeat and contemplate your course of action.",
    "What are you going to do?",
    //35-40
    "You decide to do some pushups, but your arms are too weak and you fall to the floor.",
    "When your nose crunches against the cold ground, you feel some engravings scratch against you.",
    "Rubbing your fingers across the floor, you feel some words.",
    "'Blue', 'Green', 'Blue', 'Red', 'Green'.",
    "Wondering what these words mean, you stand back up and wipe the blood from your upper lip.",
    "What are you going to do?",
    //41-43
    "You decide to cautiously extend your foot outwards, but there is nothing there to challenge its movement.",
    "You then advance through the empty space, no metal object impeding your progress.",
    "Did you escape? Or is this just the beginning?"];
var textNum = 0;
var currentText = 0;
var count = 0;
var letterGap = 10;
var timeGap = 3; //timing between letter placements
var textTop; //top of text box
var textLimit; //length of displayed text

function setup() {
    createCanvas(480, 360);
    textSize(20);
    textTop = 2 * height / 3;
    textAlign(CENTER);
    textAssign();
}
function draw() {
    currentText = textNum;
    background(0);
    present(1, 2, choice1, 3, 2, 10, 4, 23, 9);
    present(2, 5, choice2, 6, 3, 30, 10, 35, 11);
    present(3, 9, choice1, 3, 2, 10, 4, 23, 9);
    present4();
    present5();
    present6();
    present7();
    present(8, 22, choice1, 3, 2, 10, 4, 23, 9);
    present(9, 29, choice1, 3, 2, 10, 4, 23, 9);
    if (lastChoice == 10) {
	if (doorUnlocked) {
	    lastChoice = 12;
	    textNum = 41
	} else {
	    present(10, 34, choice2, 6, 3, 30, 10, 35, 11);
	}
    }
    present(11, 40, choice2, 6, 3, 30, 10, 35, 11);
    present(12);
    unlockDoor();
}
//put each text line into an object
function textAssign() {
    for (var i = 0; i < textList.length; i ++) {
	var newText = {words: textList[i],
		       scroll: textScroll,
		       next: nextText}
	textAll.push(newText);
    }
}
//access the line of text and display it in a scroll style
function textScroll() {
    fill(255);
    strokeWeight(0);
    stroke(255);
    textSize(2 * letterGap);
    var xPos = 0;
    var yPos = 0;
    textLimit = min((count / timeGap), this.words.length);
    for (var i = 0; i < textLimit; i ++) {
	xPos += letterGap;
	//if overflow, start a new line
	if (25 + letterGap + xPos > width - 25 - letterGap) {
	    xPos = letterGap;
	    yPos += letterGap * 2;
	}
	text(this.words.charAt(i), 25 + letterGap + xPos,
	     textTop + letterGap * 2 + yPos);
    }
    count = min(count + 1, this.words.length * timeGap);
    if (count == this.words.length * timeGap & choiceSoon == true) {
	choiceNow = true;
    } else {
	choiceNow = false;
    }
}
//perform text functions
function textStep(t) {
    textAll[t].scroll();
    textAll[t].next();
    textBox();
}
//displays the text box
function textBox() {
    noFill();
    strokeWeight(3);
    stroke(255);
    rectMode(CORNERS);
    rect(25, textTop, width - 25, height - 25);
}
//make it so when you press a key, next text comes in
function nextText() {
    if (count == this.words.length * timeGap
	& keyIsPressed == true
	&& textNum != textList.length - 1
	&& choiceSoon == false) {
	textNum += 1
	count = 0;
    }
}
//display the choice options in multiple boxes
function choiceDisplay(choice) {
    for (var i = 0; i < choice.length; i ++) {
	var boxSide = (width - 25 * (choice.length + 1)) / choice.length;
	noFill();
	strokeWeight(2);
	stroke(255);
	rectMode(CORNER);
	rect(25 * (i + 1) + (boxSide * i),
	     height / 3 - boxSide / 2, boxSide, boxSide);
	fill(255);
	strokeWeight(1);
	text(choice[i], 25 * (i + 1) + (boxSide * i) + boxSide / 2, height / 3);
    }
}
//allow player to make a choice depending on click location
function choiceAction(choice) {
    var boxSide = (width - 25 * (choice.length + 1)) / choice.length;
    if (mouseIsPressed) {
	if (mouseY > height / 3 - boxSide / 2 &
	    mouseY < height / 3 + boxSide / 2) {
	    for (var i = 0; i < choice.length; i ++) {
		if (mouseX > 25 * (i + 1) + (boxSide * i) &
		    mouseX < 25 * (i + 1) + (boxSide * i) + boxSide) {
		    return i;
		}
	    }
	}
    }
}
//button panel
function present4() {
    if (lastChoice == 4) {
	textStep(currentText);
	if (currentText == 13) {
	    choiceSoon = true;
	    if (choiceNow == true) {
		choiceDisplay(choice3);
		var choice = choiceAction(choice3);
		if (choice == 0) {
		    textNum = 14;
		    lastChoice = 5;
		    count = 0;
		    buttonChoice.shift();
		    buttonChoice.push(1);
		} else if (choice == 1) {
		    textNum = 16;
		    lastChoice = 6;
		    count = 0;
		    buttonChoice.shift();
		    buttonChoice.push(2);
		} else if (choice == 2) {
		    textNum = 18;
		    lastChoice = 7;
		    count = 0;
		    buttonChoice.shift();
		    buttonChoice.push(3);
		} else if (choice == 3) {
		    textNum = 20;
		    lastChoice = 8;
		    count = 0;
		}
	    }
	} else {
	    choiceSoon = false;
	}
    }
}
//scarlet button press
function present5() {
    if (lastChoice == 5) {
	textStep(currentText);
	if (currentText == 15) {
	    choiceSoon = true;
	    if (choiceNow == true) {
		choiceDisplay(choice3);
		var choice = choiceAction(choice3);
		if (choice == 0) {
		    textNum = 14;
		    lastChoice = 5;
		    count = 0;
		    buttonChoice.shift();
		    buttonChoice.push(1);
		} else if (choice == 1) {
		    textNum = 16;
		    lastChoice = 6;
		    count = 0;
		    buttonChoice.shift();
		    buttonChoice.push(2);
		} else if (choice == 2) {
		    textNum = 18;
		    lastChoice = 7;
		    count = 0;
		    buttonChoice.shift();
		    buttonChoice.push(3);
		} else if (choice == 3) {
		    textNum = 20;
		    lastChoice = 8;
		    count = 0;
		}
	    }
	} else {
	    choiceSoon = false;
	}
    }
}
//cerulean button press
function present6() {
    if (lastChoice == 6) {
	textStep(currentText);
	if (currentText == 17) {
	    choiceSoon = true;
	    if (choiceNow == true) {
		choiceDisplay(choice3);
		var choice = choiceAction(choice3);
		if (choice == 0) {
		    textNum = 14;
		    lastChoice = 5;
		    count = 0;
		    buttonChoice.shift();
		    buttonChoice.push(1);
		} else if (choice == 1) {
		    textNum = 16;
		    lastChoice = 6;
		    count = 0;
		    buttonChoice.shift();
		    buttonChoice.push(2);
		} else if (choice == 2) {
		    textNum = 18;
		    lastChoice = 7;
		    count = 0;
		    buttonChoice.shift();
		    buttonChoice.push(3);
		} else if (choice == 3) {
		    textNum = 20;
		    lastChoice = 8;
		    count = 0;
		}
	    }
	} else {
	    choiceSoon = false;
	}
    }
}
//emerald button press
function present7() {
    if (lastChoice == 7) {
	textStep(currentText);
	if (currentText == 19) {
	    choiceSoon = true;
	    if (choiceNow == true) {
		choiceDisplay(choice3);
		var choice = choiceAction(choice3);
		if (choice == 0) {
		    textNum = 14;
		    lastChoice = 5;
		    count = 0;
		    buttonChoice.shift();
		    buttonChoice.push(1);
		} else if (choice == 1) {
		    textNum = 16;
		    lastChoice = 6;
		    count = 0;
		    buttonChoice.shift();
		    buttonChoice.push(2);
		} else if (choice == 2) {
		    textNum = 18;
		    lastChoice = 7;
		    count = 0;
		    buttonChoice.shift();
		    buttonChoice.push(3);
		} else if (choice == 3) {
		    textNum = 20;
		    lastChoice = 8;
		    count = 0;
		}
	    }
	} else {
	    choiceSoon = false;
	}
    }
}

//door open exit
function present12() {
    if (lastChoice == 12) {
	choiceSoon = false;
	textStep(currentText);
    }
}

//general present function, isn't used for button presses
function present(lastC, choiceLine, choiceNum, newTextNum0, newLastC0, newTextNum1,
		 newLastC1, newTextNum2, newLastC2) {
    if (lastChoice == lastC) {
	textStep(currentText);
	if (currentText == choiceLine) {
	    choiceSoon = true;
	    if (choiceNow == true) {
		choiceDisplay(choiceNum);
		var choice = choiceAction(choiceNum);
		if (choice == 0) {
		    textNum = newTextNum0;
		    lastChoice = newLastC0;
		    count = 0;
		} else if (choice == 1) {
		    textNum = newTextNum1;
		    lastChoice = newLastC1;
		    count = 0;
		} else if (choice == 2) {
		    textNum = newTextNum2;
		    lastChoice = newLastC2;
		    count = 0;
		} else if (choice == 3) {
		    textNum = newTextNum3;
		    lastChoice = newLastC3;
		    count = 0;
		}
	    }
	} else {
	    choiceSoon = false;
	}
    }
}

function unlockDoor() {
    if (buttonChoice[0] == 2 &
	buttonChoice[1] == 3 &&
	buttonChoice[2] == 2 &&
	buttonChoice[3] == 1 &&
	buttonChoice[4] == 3) {
	doorUnlocked = true;
    }
}

This project was pretty fun to work on, but I’m actually a bit disappointed in it.  It’s a text-based adventure with a simple non-puzzle that’s really more of a proof of concept.

mmiller5-Project-12

For my Final Project, I’d like to create a multiplayer asymmetric cooperation visual novel-ish game.  Breaking that down, I want to make a 2-player game (where each player uses a different computer) that gives each player a different experience and requires them to work together to advance, presented mainly via text with some possible image interaction.

Requirements: 1- 2 player functionality;2- Different events for each player;3-  Text presentation;4- Player inputs

To accomplish the 2 player functionality, since I won’t be able to create some type of pipeline that actually allows each players’ game to interact with one another, the game will, at the start, ask the player to be either Player 1 or Player 2, with different events being displayed throughout the game depending on which player they chose.  The events of each player will then work as a sort of manual for solving the puzzles/tasks of the other player’s events, and vice versa.  To make sure each player is paced at about the same speed through the game, I could implement text that auto continues to compensate for different reading speeds, or I could implement “stop” screens that tell players to not continue until both players are ready.

For the text presentation, minimally, I could just have the text appear on screen and then change either after the player clicks or after a set amount of time.  What I would prefer to do, however, is have the text “write itself out” one letter at a time and accompany that with a little writing sound effect, but I don’t know how difficult that would be to implement.

For player inputs, I’m currently thinking of dialogue options, where multiple choices would be presented on screen and the player would need to choose the correct one or incur a penalty.  I’m thinking of also accompanying this with a visible timer-bar that shortens as time passes, giving the player limited time to communicate with their partner to find the right answer.  Depending on how this goes, I might implement more ways to interact with the system.


Example of what it might kinda look like

I know this might sound like a lot, and maybe it is, but I’m pretty confident that I can do this, or at least a proof-of-concept that’s a few minutes. (also sorry that this is more than 200 words; I’m also using this just to get my ideas written down)

mmiller5-Looking Outwards-12

This week, I’ll be looking at two games that focus on multiplayer asymmetric cooperation: We Were Here — released by Total Mayhem Games in 2017–, and Keep Talking and Nobody Explodes — released by Steel Crate Games in 2015.  The reason as to why I’m looking at these projects is because I want to make a multiplayer asymmetric cooperation game for my final project.  What these games have in common is that there are essentially two roles that different players take on before the game starts, and the role of one player is to help the other player complete tasks by providing them with information.

In We Were Here, each player is a character in different sections of an arctic castle, and one player travels from room to room encountering new puzzles that they need the help of the other player, who travels from room to room looking for clues, to solve.

Example of a clue one player needs to relay to the other

In Keep Talking and Nobody Explodes, one player is tasked with disarming a bomb while the other player is given a bomb-defusal manual that has the information needed to disarm it.

Example of a bomb with different modules that must be defused

In both projects, one player is generally in danger while the other gives information to save that player; however, I think there may be a missed opportunity where both players simultaneously fulfill both roles, one where they must solve tasks as well as one where they must provide info to their partner.

mmiller5-Looking Outwards-11


Computer generated chorale based off Bach’s compositions

This week, I’ll be looking at a Bach Style Chorale created by the program EMI (or Emmy, I dunno) and David Cope.  EMI, or Experiments in Musical Intelligence, is a program made by David Cope in 1981 that composes music in the styles of various composers, essentially making pieces that sound as though they were written by those composers.  Abstractly, the program works in three steps: deconstruction–analyzing pieces of the selected works–, signatures–identifying common aspects of works–, and compatibility– recombining these parts into new pieces.  I find this to be very inspiring because its goal is to computationally analyze musical structure and then produce something from that, potentially creating limitless numbers of works that follow existing styles.  This method could be utilized by composers to analyze their own works to aid their composing process, allowing man and machine to work together to make works of art.

mmiller5-Project-11

sketch

var turts = [];
var turtCount = 20; //set how many turtles you want

function setup() {
    createCanvas(400, 400);
    background(255);
    //populate turts array, make them different colors and weights
    for (var i = 0; i < turtCount; i ++) {
	turts[i] = makeTurtle(random(width), random(height));
	turts[i].setColor(map(i, 0, turtCount, 0, 255));
	turts[i].setWeight(i + 1)
    }
}

function draw() {
    //make first turtle follow mouse
    turts[0].turnToward(mouseX, mouseY, turts[0].angleTo(mouseX, mouseY));
    turts[0].forward(lerp(0, turts[0].distanceTo(mouseX, mouseY), .02));
    //make rest of turtles follow previous turtle
    for (var i = 1; i < turts.length; i ++) {
	turts[i].turnToward(turts[i - 1].x, turts[i - 1].y,
			    turts[i].angleTo(turts[i - 1].x, turts[i - 1].y));
	turts[i].forward(lerp(0, turts[i].distanceTo(turts[i - 1].x,
						     turts[i - 1].y), .02));
    }
}


// Turtles!
function turtleLeft(d){this.angle-=d;}function turtleRight(d){this.angle+=d;}
function turtleForward(p){var rad=radians(this.angle);var newx=this.x+cos(rad)*p;
var newy=this.y+sin(rad)*p;this.goto(newx,newy);}function turtleBack(p){
this.forward(-p);}function turtlePenDown(){this.penIsDown=true;}
function turtlePenUp(){this.penIsDown = false;}function turtleGoTo(x,y){
if(this.penIsDown){stroke(this.color);strokeWeight(this.weight);
line(this.x,this.y,x,y);}this.x = x;this.y = y;}function turtleDistTo(x,y){
return sqrt(sq(this.x-x)+sq(this.y-y));}function turtleAngleTo(x,y){
var absAngle=degrees(atan2(y-this.y,x-this.x));
var angle=((absAngle-this.angle)+360)%360.0;return angle;}
function turtleTurnToward(x,y,d){var angle = this.angleTo(x,y);if(angle< 180){
this.angle+=d;}else{this.angle-=d;}}function turtleSetColor(c){this.color=c;}
function turtleSetWeight(w){this.weight=w;}function turtleFace(angle){
this.angle = angle;}function makeTurtle(tx,ty){var turtle={x:tx,y:ty,
angle:0.0,penIsDown:true,color:color(128),weight:1,left:turtleLeft,
right:turtleRight,forward:turtleForward, back:turtleBack,penDown:turtlePenDown,
penUp:turtlePenUp,goto:turtleGoTo, angleto:turtleAngleTo,
turnToward:turtleTurnToward,distanceTo:turtleDistTo, angleTo:turtleAngleTo,
setColor:turtleSetColor, setWeight:turtleSetWeight,face:turtleFace};
return turtle;}

I was thinking about animal imprinting and thought it would be neat to have some turtles imprinted on each other.  One turtle follows the mouse, the next turtle follows that turtle, and so on, and since each of the turtles is a different color and size you get some cool line effects on the canvas.

mmiller5-Project-10

Don’t (or maybe do) get hit by the barrels!  Press any key to jump.

sketch

var clouds = [];
var barrels = [];
var ground = 320;
var character;
var spaceship = [];
var drop = [];
var gravity = .5;
var count = 0; //keeps track of time since last barrel was made
var ouch = 0; //opacity for Ouch! text

function setup() {
    createCanvas(480, 480);
    frameRate(48);
    strokeWeight(0);
    character = makeCharacter();
    textAlign(CENTER);
    initialClouds();
}

function draw() {
    makeCharacter()
    background(0, 100, 200);
    //cloud stuff
    stepClouds();
    removeClouds();
    addClouds();
    //ground
    midground();
    //spaceship barrel drop stuff
    stepDrop();
    removeDrop();
    //more ground
    closerMidground();
    fill(50, 35, 35);
    rect(0, ground, width, height - ground);
    //character stuff
    stepCharacter();
     //barrel stuff
    stepBarrels();
    removeBarrels();
    addBarrels();
    //spaceship stuff
    stepSpaceship();
    removeSpaceship();
    addSpaceship();
    //extra stuff
    hit();
    count += 1;
}

//cloudy-related stuff
function makeCloud() {
    var cloud = {x: width + 50,
		 y: 80 + random(-50, 50),
		 scale: random(.8, 1.5),
		 tufts: floor(random(1, 7)),//chooses how many parts in cloud
		 tuftX: [random(-15, 15), random(-15, 15), random(-15, 15),
			 random(-15, 15), random(-15, 15), random(-15, 15),
			 random(-15, 15)], //parts in cloud have random location  
		 tuftY: [random(-15, 15), random(-15, 15), random(-15, 15),
			 random(-15, 15), random(-15, 15), random(-15, 15),
			 random(-15, 15)],
		 speed: -.2,
		 move: cloudMove,
		 appearance: cloudAppearance}
    return cloud;
}

function initialClouds() { //make clouds when script starts
    for (var i = 0; i < random(5, 12); i ++) {
	clouds.push(makeCloud());
	clouds[i].x = random(width);
    }
}

function cloudMove() {
    this.x += this.speed * this.scale;
}

function cloudAppearance() {
    fill(240, 100);
    strokeWeight(0);
    for(var i = 0; i < this.tufts; i ++) {
	rect(this.x + this.tuftX[i], this.y + this.tuftY[i],
	     40 * this.scale, 20 * this.scale, 5 * this.scale);
    }
}

function stepClouds() { //updates the cloud features to redraw them
    for (var i = 0; i < clouds.length; i ++) {
	clouds[i].move();
	clouds[i].appearance();
    }
}

function addClouds() { //spawns new clouds randomly
    if (clouds.length <= 15 & random(0, 1) < .01) {
	clouds.push(makeCloud());
    }
}

function removeClouds() { //gets rid of clouds when off screen
    var cloudsToKeep = [];
    for (var i = 0; i < clouds.length; i ++) {
	if (clouds[i].x + (40 * clouds[i].scale) + 15 > 0) {
	    cloudsToKeep.push(clouds[i]);
	}
    }
    clouds = cloudsToKeep;
}

//character stuff
function makeCharacter() {
    var character = {x: 50,
		     y: ground - 10,
		     yf: 0,
		     width: 10,
		     height: 10,
		     color: color(0),
		     jump: false,
		     move: characterJump,
		     appearance: characterAppearance,
		     collision: collision}
    return character;
}

function characterJump() { //makes character move up and down when a key is pressed
    if (this.jump == true) {
	this.yf += gravity;
    }
    this.y += this.yf;
    this.y = min(this.y, ground - this.height);
    if (this.y == ground - this.height) {
	this.jump = false;
    }
    if (keyIsPressed == true & this.jump == false) {
	this.jump = true;
	this.yf -= 10;
    }
}

function characterAppearance() {
    fill(this.color);
    strokeWeight(1);
    stroke(0, 150);
    rect(this.x, this.y, this.width, this.height);
}

function stepCharacter() {
    character.move();
    character.appearance();
    character.collision();
}

function collision() { //when character hits a barrel, change color and say Ouch!
    for (var i = 0; i < barrels.length; i ++) {
	if (dist(this.x, this.y,
		 barrels[i].x, barrels[i].y) <= barrels[i].radius ||
	    dist(this.x + this.width, this.y,
		 barrels[i].x, barrels[i].y) <= barrels[i].radius ||
	    dist(this.x, this.y + this.height,
		 barrels[i].x, barrels[i].y) <= barrels[i].radius ||
	    dist(this.x + this.width, this.y + this.height,
		 barrels[i].x, barrels[i].y) <= barrels[i].radius) {
	    this.color = barrels[i].color;
	    barrelHit(i);
	    
	}	    
    }
}

//barrel stuff
function makeBarrel() {
    var barrel = {x: width + 50,
		  y: ground - random(50, 150),
		  ySpeed: 0,
		  xSpeed: -3,
		  radius: 10,
		  color: color(random(0, 150), random(0, 150), random(0, 150)),
		  move: barrelMove,
		  appearance: barrelAppearance}
    return barrel;
}

function barrelMove() {
    this.x += this.xSpeed;
    this.y += this.ySpeed;
    this.y = min(ground - 10, this.y);
    this.ySpeed += gravity;
    if (this.y == ground - 10) {
	this.ySpeed = -this.ySpeed * .82;
    }
}

function barrelAppearance() {
    fill(this.color);
    strokeWeight(1);
    stroke(0, 150);
    ellipse(this.x, this.y, this.radius * 2, this.radius * 2);
}

function stepBarrels() {
    for (var i = 0; i < barrels.length; i ++) {
	barrels[i].move();
	barrels[i].appearance();
    }
}

function addBarrels() {
    if (random(0, 1) < .01 & count >= 48/*prevents from being too close*/) { 
	barrels.push(makeBarrel());
	count = 0;
    }
}

function removeBarrels() {
    var barrelsToKeep = [];
    for (var i = 0; i < barrels.length; i ++) {
	if (barrels[i].x + 10 > 0) {
	    barrelsToKeep.push(barrels[i]);
	}
    }
    barrels = barrelsToKeep;
}

function barrelHit(index) { //deletes hit barrels and says Ouch!
    var barrelsToKeep = [];
    for (var i = 0; i < barrels.length; i ++) {
	if (i != index) {
	    barrelsToKeep.push(barrels[i]);
	}
    }
    barrels = barrelsToKeep;
    ouch = 255; //sets opacity for Ouch! to be visible
}

function hit() { //displays Ouch!
    fill(255, 0, 0, ouch);
    strokeWeight(0);
    textSize(100);
    text("Ouch!", 60, 100, width - 100, 200);
    ouch -= 5;
}

//landscape elements
function midground() { //makes ground randomly according to noise
    var terrainSpeed = 0.00005;
    var terrainDetail = 0.005;
    fill(50);
    strokeWeight(0);
    beginShape();
    vertex(0, height);
    for (var x = 0; x < width; x++) {
        var t = (x * terrainDetail) + (millis() * terrainSpeed);
        var y = map(noise(t), 0, 1, height / 4, ground);
        vertex(x, y); 
    }
    vertex(width, height);
    endShape();
}

function closerMidground() { //makes ground randomly according to noise
    var terrainSpeed = 0.0007;
    var terrainDetail = 0.05;
    fill(50, 80, 50);
    strokeWeight(0);
    beginShape();
    vertex(0, height);
    for (var x = 0; x < width; x++) {
        var t = (x * terrainDetail) + (100000 + millis() * terrainSpeed);
        var y = map(noise(t), 0, 1, height / 2, ground);
        vertex(x, y); 
    }
    vertex(width, height);
    endShape();
}

//spaceship
function makeSpaceship() {
    var spaceship = {x: -50,
		     y: random(50, 150),
		     speed: 0,
		     speedBoost: random(0, 500),
		     move: spaceshipMove,
		     appearance: spaceshipAppearance}
    return spaceship;
}

function spaceshipMove() {
    //movement speeds up and slows down according to noise
    this.x += this.speed + .5 * (noise(millis() * .001 + this.speedBoost));
}

function spaceshipAppearance() {
    strokeWeight(1);
    fill(255, 200);
    ellipse(this.x + 25, this.y - 5, 15, 15);
    fill(150);
    ellipse(this.x + 25, this.y, 50, 10);
}

function stepSpaceship() {
    for (var i = 0; i < spaceship.length; i ++) {
	spaceship[i].move();
	spaceship[i].appearance();
	addDrop(i); //spawns in barrel drops from ship
    }
}

function addSpaceship() {
    if (random(0, 1) < .0009 - (spaceship.length * .0003)) {
	spaceship.push(makeSpaceship());
    }
}

function removeSpaceship() {
    var spaceshipToKeep = [];
    for (var i = 0; i < spaceship.length; i ++) {
	if (spaceship[i].x - 25 < width) {
	    spaceshipToKeep.push(spaceship[i]);
	}
    }
    spaceship = spaceshipToKeep;
}

//spaceship barrel drop
function makeDrop(i) {
    var drop = {x: spaceship[i].x + 27, //spawns at spaceship location
		y: spaceship[i].y,
		ySpeed: 0,
		color: color(random(0, 160), random(0, 160), random(0, 160)),
		radius: 3,
		move: dropMove,
		appearance: dropAppearance}
    return drop;
}

function dropMove() {
    this.y += this.ySpeed;
    this.ySpeed += gravity / 5;
}

function dropAppearance() {
    fill(this.color);
    strokeWeight(.5);
    stroke(0, 150);
    ellipse(this.x, this.y, this.radius * 2, this.radius * 2);
}

function stepDrop() {
    for (var i = 0; i < drop.length; i ++) {
	drop[i].move();
	drop[i].appearance();
    }
}

function addDrop(i) {
    if (random(0, 1) < .01) {
	drop.push(makeDrop(i));
    }
}

function removeDrop() {
    var dropToKeep = [];
    for (var i = 0; i < drop.length; i ++) {
	if (drop[i].y < ground) {
	    dropToKeep.push(drop[i]);
	}
    }
    drop = dropToKeep;
}

When thinking of generative landscapes, I started thinking about side-scrolling games, and so I decided to combine the two!  Most of the objects are pretty basic — stuff gets introduced and moves across the screen — so I decided to add some complexity to each of the objects, giving them things like different numbers of components, bouncing, and spawning new objects.  The part of this project I personally love the most is that when the character gets hit by a barrel, they become the same color as that barrel!  I just think that’s so neat (and it’s also kinda sad, because clearly getting hit by the barrel hurts, but it’s so cool to watch!).

mmiller5-Looking Outwards-10

Video Introducing Puppet Parade

This week, I’ll be looking outward to Puppet Parade, an interactive exhibit produced in part by Emily Gobeille in 2011.  Emily is an artist and designer who focuses on building interactive exhibits that encourage play and she’s a co-founder of Design I/O, a creative studio specializing in the creation of interactive installations.

In Puppet Parade, visitors control one of two giant projected puppets by moving their arm as if it were the puppet.  By moving the puppet, the puppeteer can eat different colored blocks to change the colors of their puppet or make funny poses.  The puppeteer’s arm is tracked using an XBox Kinect, but custom software was developed to distinguish the different parts of the arm.


Custom software tracks different parts of the arm and correlates it to the puppet

What I find inspiring about this project is how the actions of one or two “players” are able to create an entire area of interactivity, expanding the space and amount of people that can partake in an experience.

mmiller5-Looking Outwards-09

This week, I’ll be looking at the post Hamza Qureshi made on A Text of Random Meanings by architect Rami Hammour from 2015.  What I find to be more interesting about this work isn’t the process used to make the random lines, but rather the implications of using randomness to simulate something that usually is not random — writing.  I can think of two ways to view this seeming juxtaposition:  that by having a random assortment of lines look like text, it highlights that writing is not random and should follow some goal, or that something that seems random, such as the order of letters in a language one can’t read, may in fact possess a deeper meaning and purpose that we’re just not able to understand

mmiller5-Project-09

sketch

var img
var step = [0, 0, 0, 0];
var i = [0, 0, 0, 0]; //y coordinates of strokes
var j = [0, 0, 0, 0]; //x coordinates of strokes
var vertShift = [];
var horzShift = [];
var check1 = true;
var check2 = [false, false, false, false];
var mx; //x value of the point of convergence
var my; //y value of the point of convergence

function preload() {
    img = loadImage("https://i.imgur.com/9nlcGmA.jpg?1");
}

function setup() {
    createCanvas(img.width, img.height);
    strokeCap(SQUARE);
    frameRate(30);
}

function draw() {
    paintLine();
}

function paintLine() {
    var startx = [0, width, 0, width];
    var starty = [0, 0, height, height];

    //make 4 different stroke paths
    for(var count = 0; count < 4; count ++){
	//checks if all the strokes have reached the point of convergence
	if(check1) {
	    //finds trajectory for stroke to align it with point of convergence
	    mx = mouseX;
	    my = mouseY;
	    step[count] = 0;
	    vertShift[count] = 10 * (my - starty[count]) /
		dist(mx, my, startx[count], starty[count]);
	    horzShift[count] = 10 * (mx - startx[count]) /
		dist(mx, my, startx[count], starty[count]);
	}
	//if stroke hasn't made it to the point of convergence
	if (dist(mx, my,
		 j[count] + startx[count], i[count] + starty[count]) > 10) {
	    //get colors from image
	    var pt1 = img.get(constrain(j[count] + startx[count], 0,
					width - 1),
			      constrain(i[count] + starty[count], 0,
					height - 1));
	    var pt2 = img.get(constrain(j[count] + horzShift[count]
					+ startx[count], 0, width - 1),
			      constrain(i[count] - vertShift[count]
					+ starty[count], 0, height - 1));
	    var col = color((pt1[0] + pt2[0]) / 2,
			    (pt1[1] + pt2[1]) / 2,
			    (pt1[2] + pt2[2]) / 2,
			    200);
	    //draw the stroke
	    stroke(col);
	    strokeWeight(10);
	    line(startx[count] + j[count], starty[count] + i[count],
		 startx[count] + j[count] + horzShift[count],
		 starty[count] + i[count] + vertShift[count]);
	    //advance step and recalculate new positions of strokes
	    step[count] += 1;
	    j[count] = step[count] * horzShift[count];
	    i[count] = step[count] * vertShift[count];
	    print(j);
	    check2[count] = false;
	} else { // if stroke reached the end, set check so it can restart later
	    check2[count] = true;
	}
    }
    //if all strokes are done, allow new point of convergence to be made
    if (check2[0] & check2[1] && check2[2] && check2[3]) {
	check1 = true;
    } else {
	check1 = false;
    }
}

Oh boy, this was a pain.  There were many other ways I went about this, with lines forming randomly, serial rows of lines, and lines targeting the mouse from only 1 position.  Eventually though I got here, and I’m just glad it works.


Kinda filled out version

mmiller5-Looking Outwards-08

Theo Watson and Emily Gobeille talk about their projects at the 2012 Eyeo Festival

On today’s edition of “Looking Outwards with Michael Miller,” we will be exploring the life and habits of artist, programmer, and designer Theo Watson.  Born in London, Theo studied Design and Technology at the Parsons School of Design, and has since founded Design I/O and YesYesNo, among doing other cool things.  Theo works on interactive experiences that encourage play, so kinda like games, but generally on a larger scale such as creating digital environments in physical spaces that give open-ended opportunities for people to interact with the space, such as he did with Night Bright and Funky Forest.  One of his focuses in development is rapid prototype creation — making proofs of concept that allow people he’s working with to visualize what it is he wants to do — and I think this is an admirable strategy for project work because it turns abstract concepts into concrete creations which can then be more readily shared and analyzed.  In presenting, he uses a bunch of images and videos of the creation process of his projects, and I feel that this is effective in engaging the audience because it gives them a window of insight into how he works.

(Note, most of this post can also be applied to Emily Gobeille as she also worked on these projects, but I just did more specific research on Theo.  I’m just trying to be clear that Theo didn’t work alone on all of these)