Joanne Lee – Term Project

Term Project

// Joanne Lee
// Section C
// joannele@andrew.cmu.edu
// Term Project

// Visual Quadrant Map:
// Q1  Q2  Q3
// Q4  Q5  Q6
// Q7  Q8  Q9

// logistics
var game = "inactive"
var fCount = 150;
var playBoardW = 270;

// playing board variables
var boardWeight = 10;
var playBoxW = 270;
var effectCounter = 6;

// white, orange, gray box variables
var boxW = playBoardW / 4;
var boxX = [165.625, 250, 334.375];
var boxY = [240.625, 325, 409.375];
var grayBoxes = [];
var newOrange = false;
var whiteX = 0;
var whiteY = 2;
var orangeX = 2;
var orangeY = 0;

// score keeper
var score = 0;
var bestScore = 0;

// sound variables
var coin;
var boing;

function preload() {
    coin = loadSound("https://courses.ideate.cmu.edu/15-104/f2018/wp-content/uploads/2018/12/coin.wav");
    coin.setVolume(0.5);
    boing = loadSound("https://courses.ideate.cmu.edu/15-104/f2018/wp-content/uploads/2018/12/boing.wav");
    boing.setVolume(0.5);
}

function setup() {
    createCanvas(500,650);
    frameRate(100);
}

function draw() {
    background(255,211,222);

    // "growth" effect seen when white + gray boxes collide
    if (effectCounter < 2) { // staggered growth effect
        playBoxW = 280;
    }
    if (effectCounter > 2 & effectCounter < 4) { // staggered growth effect
        playBoxW = 290;
    }
    if (effectCounter > 4 & effectCounter < 6) { // staggered growth effect
        playBoxW = 280;
    }
    if (effectCounter > 6) { // staggered growth effect
        playBoxW = 270;
    }
    effectCounter += 1;

    drawPlayingBoard();
    drawPlayerBox();
    drawGoalBox();
    displayScore();

    if (game == "inactive") {
        displayStarterText();
    }
    if (game == "active") {
        drawGrayBox();
        // control speed that new boxes are generated
        if (frameCount % fCount == 0 & frameCount != 0) {
            generateGrayBox();
        }
        updateGrayBox();
        checkCollision();
    }
}

function displayScore() {
    fill(255,80); // display current score
    textSize(200);
    textStyle(BOLD);
    textFont('Helvetica');
    if (score < 10) {
        text(score, width - 120, height - 10);
    }
    else if (score < 100) { // shift over one spot for 2 digit score
        text(score, width - 225, height - 10);
    }
    else if (score < 999) {
        text(score, width - 330, height - 10 );
    }
    if (score >= bestScore) { // record the best score
        bestScore = score
    }

    fill(255); // display the best score
    textSize(20);
    textStyle(BOLD);
    textFont('Helvetica');
    text("High Score  " + bestScore, 110, height-160);
}

function displayStarterText() {
    fill(40,99);
    textSize(20);
    textStyle(BOLD);
    textFont('Helvetica');
    text("Press enter to begin", 155, height/2);
}

function drawPlayingBoard() { // light pink w/ thick white border
    fill(255,211,222);
    stroke(255); 
    strokeWeight(boardWeight);
    rectMode(CENTER);
    rect(width / 2, height / 2, playBoxW, playBoxW);
}

function drawPlayerBox() { // white box, starts in Q7
    strokeWeight(0);
    fill(255);
    rect(boxX[whiteX], boxY[whiteY], boxW, boxW);
}

function drawGoalBox() { // orange box, starts in Q3
    fill(255,170,107);
    rect(boxX[orangeX], boxY[orangeY], boxW*0.7, boxW*0.7);
}

function randomOrangeXY() { // generate random position for orange box
    var randX = int(random(0,3));
    var randY = int(random(0,3));

    // make sure orange x & y are in same line or column as previous position
    while (randX == whiteX || randY == whiteY) {
        randX = int(random(0,3));
        randY = int(random(0,3));
    }
    orangeX = randX;
    orangeY = randY;
}

function checkBoard() {
    // if white box position = orange box, you need a new orange box
    if (whiteX == orangeX & whiteY == orangeY) {
        coin.play();
        newOrange = true; // indicate need for new orange box
        score += 1; // increment score

        // increase speed at which new boxes spawn, cap at 90 frame count
        if (score % 10 == 0 & score != 0 && fCount > 90) {
            fCount -= 20;
        }   
    }

    // if board needs new orange box, randomly generate
    if (newOrange == true) {
        randomOrangeXY();
        newOrange = false;
    }
}

function generateGrayBox() { // will create a box with random attributes
    if (int(random(0,2)) == 0) { // create a box going left or right
        if (int(random(0,2)) == 0) { // create box going right
            createBox(-23.625, boxY[int(random(0,3))], 1, 0);
        }
        else { // create box going left
            createBox(523.625, boxY[int(random(0,3))], -1, 0);
        }
    }
    else { // create a box going up or down
        if (int(random(0,2)) == 0) { // create box going down
            createBox(boxX[int(random(0,3))], -23.625, 0, 1);
        }
        else { // create box going up
            createBox(boxX[int(random(0,3))], 673.625, 0, -1);
        }
    }
}

function createBox(tx, ty, dx, dy) { // store as an object in the array
    var grayBox =   { x: tx, y: ty,
                      dx: dx, dy: dy,
                      speed: 2.5, state: 'active'
                    }
    grayBoxes.push(grayBox);
}

function updateGrayBox() { // moves the box along
    for (var a = 0; a < grayBoxes.length; a ++) {
        grayBoxes[a].x += grayBoxes[a].dx * grayBoxes[a].speed;
        grayBoxes[a].y += grayBoxes[a].dy * grayBoxes[a].speed;
    }
}

function drawGrayBox() {
    for (var b = 0; b < grayBoxes.length; b ++) {
        fill(107,105,107);
        rect(grayBoxes[b].x, grayBoxes[b].y, boxW*0.7, boxW*0.7);
    }
}

function checkGrayBox() {
    for (var c = 0; c < grayBoxes.length; c ++) {
        // delete box from array once it is off the screen
        if (grayBoxes[c].x < -24 || (grayBoxes[c].x > 524) || 
            (grayBoxes[c].y < -24) || (grayBoxes[c].y > 674)) {
            count += 1;
            grayBoxes.splice(c,1);
        }
    }
}

function checkCollision() {
    for (var d = 0; d < grayBoxes.length; d ++) {
        if ((grayBoxes[d].x + boxW * 0.35) > (boxX[whiteX] - boxW * 0.5) &
            (grayBoxes[d].x - boxW * 0.35) < (boxX[whiteX] + boxW * 0.5) &&
            (grayBoxes[d].y - boxW * 0.35) < (boxY[whiteY] + boxW * 0.5) &&
            (grayBoxes[d].y + boxW * 0.35) > (boxY[whiteY] - boxW * 0.5)) {
            gameReset();
            break;
        }
    }
}

function gameReset() {
    boing.play();
    score = 0;
    grayBoxes = [];
    fCount = 150;
    effectCounter = 0;
    game = "inactive";
}

function keyPressed() {
    if (game == "active") {
        if (keyCode == UP_ARROW & whiteY > 0) {
            whiteY -= 1;
        }
        if (keyCode == DOWN_ARROW & whiteY < 2) {
            whiteY += 1;
        }
        if (keyCode == LEFT_ARROW & whiteX > 0) {
            whiteX -= 1;
        }
        if (keyCode == RIGHT_ARROW & whiteX < 2) {
            whiteX += 1;
        }
    }
    if (game == "inactive" & keyCode == ENTER) {
        game = "active"
        generateGrayBox();
    }

    // CHECK BOARD: to see if white box "ate" orange box
    checkBoard();
}

The objective of the game is quite simple: collect the orange box by using your arrow keys to move the white box while avoiding the incoming gray boxes!

As the game progresses, you’ll find that the gray boxes will appear quicker, creating more obstacles to dodge. The game was based off of a game I saw a while ago, but was unable to find again on the internet — so the idea is not original, however, the interface is!

Although seemingly simple, there were a lot of technical obstacles I had to overcome with the main one being having to make sure that too many gray boxes did not appear at once. I ran into some bugs along the way, but I believe that my current code is clean and efficient!

Something I also tried to focus on was simple, but meaningful UI. Attention to detail was put in to small things such as a visual (and audio) cue when you mess up or the placement of the score. Hopefully this is a soothing yet addictive game that will get people through finals week! It was fun to create something of my own.

Joanne Lee – Looking Outward 12

For my final project, I plan to create a virtual Rube Goldberg machine geared towards children ages 5-9. Because my project is not necessarily “art”, I took inspiration from a media sample and contest design. One project that I found relevant and interesting was a media sample of a simple Rube Goldberg machine being explained on Sesame Street.

Sesame Street explaining how a simple Rube Goldberg machine works.

I was unable to find the year of this media sample however they call it the “What Happens Next Machine”. I found it to be useful to see how the machine was shown and expressed in very simple terms and helped me to better understand the type of terminology and level of difficulty I will implement in my actual experiment. Something that this media sample lacks is interactivity due to it being shown via television. I believe learning is done best through interaction and trial & error!

At the Children’s Museum of Pittsburgh, children are able to tinker with the Rube Goldberg exhibit to complete everyday tasks.

Another project I found was the Rube Goldberg exhibit at Children’s Museum of Pittsburgh. At this exhibit, children are able to activate chain reactions in order to complete every day tasks. I think this is a great exhibit because as a child, I was very fascinated by Rube Goldberg machines and still am! It is important to foster creativity in children and by using these inefficient machines to complete every day tasks, it helps to keep the creative juices flowing! I don’t think they overlooked anything large, but I would like to increase accessibility to these interactive exhibits by creating my own virtually. I hope my project turns out well.

Joanne Lee – Final Project Proposal

I want to create an educational Rube Goldberg game geared towards children ages 5-9. I think that Rube Goldberg games are very helpful in learning cause and effect. I would like to use my virtual Rube Goldberg machine to complete ‘green’ tasks such as watering a plant, turning off the lights, recycling, etc. The purpose of the game is to teach children to understand cause and effect better and also understand the impact we can make when we go green!

In terms of functionality, I’ll have a virtual Rube Goldberg machine with certain parts that are broken. Through trial and error, they will have to pass three stages — each stage dedicated to a green task. I’m still juggling between what tasks I want to do. Once they pass each level, they’ll be shown a blurb about the environmental impact that the stage’s task makes on the environment. Below is a rough mock-up of what a level could look like.

In the actual one, I will include images to replace some aspects. I just used rectangles and circles to roughly represent a stage.

Joanne Lee – Looking Outward 11

Alan Walker’s 2015 hit single, ‘Faded’.

When I realized this week’s theme was computational music, I immediately thought of Alan Walker specifically. He is a music producer and he creates his music (EDM type of music) using a computer program that allows him to combine synthetic instruments and sounds. He is also able to edit the sound waves in the program to tweak it any which way he wants and add effects such as echo or reverb to name a few. The song that I chose specifically is ‘Faded’ which is one of his most popular songs. When I first came across this song, it really piqued my interest in computer music and I actually felt motivated to learn about computer music.

Walker obviously begins with an artistic vision and then he tinkers with the program to make sure every beat and sound is placed exactly where he wants it to be. Provided below is a video of one of this studio sessions and he has other studio sessions on his channel. I am thankful to live in a generation where computer music allows someone to create music with all sorts of synthetic instruments on their own from the comforts of their own home / studio!

 

Alan Walker in his studio session for ‘Faded’, showing snippets of the thought process behind his hit song.

Joanne Lee – Project 11

Project-11

// Joanne Lee
// Section C
// joannele@andrew.cmu.edu
// Project-11

// global variables
var maze;
var character = [];
var brightnessThreshold = 10; // low threshold bc working with black background

// color variables
var redR = 255;
var redG = 96;
var redB = 95;

var pinkR = 255;
var pinkG = 184;
var pinkB = 255;

var cyanR = 2;
var cyanG = 255;
var cyanB = 255;

var orangeR = 255;
var orangeG = 184;
var orangeB = 82;

// array with color r,g,b values
var randomCol = [redR, redG, redB, pinkR, pinkG, pinkB, cyanR, cyanG, cyanB, orangeR, orangeG, orangeB]

function preload() {
    maze = loadImage("https://i.imgur.com/hGU36Hn.png") // load maze image
}

function setup() {
    createCanvas(480, 323);
    image(maze, 0, 0, 480, 323);
    maze.loadPixels();

    for (var a = 0; a < 6; a ++) { // create 6 hexagonal character in the maze
        if (a < 3) { // positions for character startion left on maze
            character[a] = makeCharacter(112, 57 + (95 * a));
        }
        else if (a >= 3) { // positions for character starting right on maze
            character[a] = makeCharacter(360, 57 + (95 * (a % 3)));
        }
        setColor(a); // randomly set colors
        character[a].setWeight(6);
        character[a].penDown(); // begin drawing
        character[a].forward(6);
        // note that it doesn't stop drawing
    }
}

function setColor(a) { // pick a random color from red, pink, cyan, orange
    var randNum = random(0,4);
    var index = floor(randNum) * 3; // take floor to make sure they're whole #'s
    character[a].setColor(color(randomCol[index], randomCol[index + 1], randomCol[index + 2]));
}

function draw() {
    for (var x = 0; x < 6; x ++) {
        chrMove(x);
        chrReset(x);
    }
}

// getPixel: fast access to pixel at location x, y from image 
function getPixel(image, x, y) {
    var i = 4 * (y * image.width + x);
    return color(image.pixels[i], image.pixels[i + 1], image.pixels[i + 2]);
}

function chrMove(x) { // update the object's position
    var theColorAtPxPy = getPixel(maze, character[x].x, character[x].y); // get color of px, py position
        
    // Fetch the brightness at PxPy, and save it.
    var theBrightnessOfTheColorAtPxPy = brightness(color(theColorAtPxPy));

    // If the brightness at PxPy is greater than the brightness threshold, move it down.
    if (theBrightnessOfTheColorAtPxPy < brightnessThreshold) {
        character[x].forward(1);
    }

    // If the brightness at PxPy is lower than darkness threshold, move up until it isn't.
    while (theBrightnessOfTheColorAtPxPy > brightnessThreshold & this.py != 0) {
        character[x].back(2);
        if (random(0,1) < 0.5) {
            character[x].right(90);
        }
        else {
            character[x].left(90);
        }
        theColorAtPxPy = getPixel(maze, character[x].x, character[x].y);
        theBrightnessOfTheColorAtPxPy = brightness(color(theColorAtPxPy));
        }
}

function chrReset(x) { // reset the object to the top of the screen
    if (character[x].x > width) { // bring back to left of screen if it moves off the right
        character[x].x = 0;
    }
    else if (character[x].x < 0) { // bring back to right of screen if it moves off the left
        character[x].x = width;
        character[x].back(150);
        character.right(90);
    }
}

// turtle graphic commands

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 makeCharacter(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, move: chrMove, // update the character's position
                  reset: chrReset, // reset the character to maze entrance
                  }
    return turtle;
}

I created an endless self-navigating maze inspired by Pac-Man! The colors of the lines are randomly generated from the red, cyan, pink, orange colors of the ghosts from the games. I wish I had the ability and time to add more randomness in the maze’s twists and turns as well as more smooth line as opposed to hitting the wall then moving back a step. However, I am very happy with my final product as is. I’ve included pictures of different iterations below.

The start of one iteration. You can clearly see the different paths here since it is early on in its stages.
Another iteration in a more advanced stage of completion. Lines go back the same path it took sometimes due to the slight randomness in its turns.

Joanne Lee – Looking Outward 10

SUPERHYPERCUBE (2016, Heather Kelley).

The project I chose to discuss is called SUPERHYPERCUBE. It is a “VR first person puzzler” that was originally developed for PlayStationVR. In order to play this game, you must figure out how to rotate the cubes on your screen to fit the approaching wall. With each successful completion, you get more and more cubes, making it more difficult to see the shape of the wall behind you. At that point you must quickly peek around the cubes to see the shape of the wall and figure out the correct rotations.

This fascinating game was created by Heather Kelley who is a veteran game designer, digital artist, and media curator among many things. She has served as the co-chair of the IGDA’s Women in Game Development Special Interest Group for 7 years and is clearly very passionate and good at what she does. She is the co-founder of Kokoromi (an experimental game collective) and has created many creative games under her belt. I admired this game in particular because I really love puzzle games and the idea of a VR version was very interesting to me. I also admire how involved she has been in advocating women in game development and it has inspired me to try to create a small, simple game as a side project during the remainder of this semester!

Joanne Lee – Project 09

Project 09

// Joanne Lee
// Section C
// joannele@andrew.cmu.edu
// Project-09

var christine;

function preload() {
    var myImageURL = "https://i.imgur.com/beoY7rv.jpg"; // load image
    christine = loadImage(myImageURL);
}

function setup() {
    createCanvas(480, 480);
    background(0);
    christine.loadPixels();
    frameRate(50000000);
}

function draw() {
    var px = random(width);
    var py = random(height);
    var ix = constrain(floor(px), 0, width-1);
    var iy = constrain(floor(py), 0, height-1);
    var rx = px + random(0,6); // randomize line x-value
    var ry = py + random(0,4); // randomize line y-value
    var rs = random(1,4); // randomize stroke weight
    var theColorAtLocationXY = christine.get(ix, iy);

    strokeWeight(rs);
    stroke(theColorAtLocationXY);
    line(px,py,rx,ry);
}

I chose a photo of my gorgeous current roommate (although the style of the photo made it hard to really capture her eyes!). I knew I wanted to do lines in order to emulate “strokes” and to further emulate this, I tried to vary the stroke weights as well as direction and lengths of the line. I didn’t want to make the line extend too far in order to ensure that facial features could moderately be made out.

I very much admire impressionist type paintings and so I tried to emulate thicker brush strokes. I made the frame rate ‘50000000’ because I preferred to see the photo render quickly. Below are photos further in to rendering and near completion.

Midway through full rendering
Almost fully done rendering
Original Photo of Christine Seo

Joanne Lee – Looking Outwards 09

Nunu & Willump League of Legends Champion Spotlight

In Robert Oh’s first Looking Outward, he reviewed the visual game update of a longtime League of Legends (multiplayer online game) champion. He mentioned that, “Riot is aiming towards making their boring characters more interesting by adding new skills that make them feel more fun to play.” As an avid player of this game, I wholeheartedly agree with this statement. Although I only started playing League of Legends 2 years ago, I find that something that keeps me engaged in the game is the consistent changes they make to their champions, gameplay, items, and abilities. This is a very great strategy implemented by Riot Games to keep the game feeling fresh despite the base mechanics of the game remaining the same.

One aspect I would like to clarify on is when Robert states that every couple months, Riot chooses to to update old characters to make them feel fresh and new. The original Nunu was released far back on February 21, 2009. The updated Nunu was released on August 29, 2018. It took about 9 years for them to update this champion. Many other champions are also waiting on a visual game update. My hypothesis is that they want to make sure that their updates will be radical and refreshing and not just minor changes. In between, they will often release brand new champions, further delaying the updates of previous champions.

As a player of this champion, the visual game update proved to be very exciting and worth the wait. I hope Riot will continue to re-envision their previous art and champion styles in order to keep the game a fresh and exciting game to play!

Joanne Lee – Looking Outward 07

Flight Visualizations over 24 Periods, Institution: NATS (2014)

NATS is an air traffic control services provider and in this particular video, they visualized the air traffic coming and going to European airspace over the course of 24 hours on a summer day in 2014. Despite the fact that there is not much information available on it, I chose this work because after I graduated high school, my parents moved to Germany for 3 years. During those 3 years, I had the privilege of making many trips to, from, and within Europe. Throughout the countless delayed flights, time spent on the runway, and in the air, I became increasingly aware of the sheer number of flights that there are in a day and wondered how air traffic was managed and controlled.

It is particularly fascinating and meaningful to me to see this visualization. In terms of creating this visualization, I image that NATS has access to all of the flight routes of any given day and they carefully compiled these routes onto a 3D model of the Earth. I really like that they used a dark background for the Earth and used an eye-catching neon color while also making it slightly opaque — so that we can see places where many of the routes overlap.

(* Note: This assignment was submitting using one grace day.)

Joanne Lee – Project 07

Project-07

// Joanne Lee
// Section C
// joannele@andrew.cmu.edu
// Project-07

var nPoints = 100;
var colorR = 167;
var colorG = 168;
var colorB = 212;
var div = 0;
var w = 0;
var h = 0;

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

function draw() {
  background (240);
  push();
  translate(0,0);
    for (var c = 0; c < 6; c ++) {
      h = c * 96; // shifts height of flower
      for (var d = 0; d < 7; d ++) {
        w = d * 80; // shift width of flower
        drawEpitrochoidCurve(colorR, colorG, colorB, div, w, h);
      }
    }
    if (mouseY > 0){
        div = (mouseY / 15.0) + 5; // flower size scales to mouseY position
    }
  pop();
}

function drawEpitrochoidCurve(colorR, colorG, colorB, div, w, h) {
    // Epicycloid:
    // http://mathworld.wolfram.com/Epicycloid.html    
  var x;
  var y;
    
  var a = 80.0;
  var b = a / 2.0;
    
  fill(colorR, colorG, colorB);
  beginShape();
  for (var i = 0; i < 100; i++) {
    var t = map(i, 0, nPoints, 0, TWO_PI);
    x = a * (6 * cos(t) - cos(6 * t)) / div + w;
    y = a * (6 * sin(t) - sin(6 * t)) / div + h;
    vertex(x, y);
  }
  endShape(CLOSE);
    
}

function mouseClicked() { // petal color changes upon mouseClick
  var colNum = int(random(1,4));
    if (colNum == 1) {
      colorR = 167;
      colorG = 168;
      colorB = 212;
    }
    else if (colNum == 2) {
      colorR = 207;
      colorG = 61;
      colorB = 160;
    }
    else if (colNum == 3) {
      colorR = 90;
      colorG = 76;
      colorB = 181;
    }
}

Image result for flower color

For this week’s project, I first thought of flower petals when I was looking at the possible curves. I looked up flower petals and liked the colors and shapes of these, so I used ranuncloid curves to create them. Ranuncloid curves are essentially epicycloids with 5 cusps. For interactivity, I thought it would be interesting to make the flower “grow” using the mouseY position as well as randomly alternate between the flower colors depicted above with mouse clicks. I purposely had the flowers overlap with each other at its largest size because I thought that it formed an interesting pattern.