Lingfan Jiang & Kai Zhang – Final Project

sketch

//Lingfan Jiang/ Kai Zhang
//Section B
//lingfanj@andrew.cmu.edu / kaiz1@andrew.cmu.edu
//final project

//bird variables
var g = 0.5;
var v = 0;
//upforce
var Fup = 20;
var birdx = 120;
var birdy = 240;
//pipe
var pipes = [];
var currentPipeY;
var gap = 170;
//counter
var successC = 0;
//lean degrees
var gameOn = true;
//terrain
var terrainDetail = 0.002;
var terrainSpeed = -0.001;
//clouds
var clouds = [];
var cloudsx = [];
var cloudsy = [];
var cloudsSpeed = -0.5;
var cloudsSize = [];

var gameOn = true;

var totalTime;
//board content
var level = ["Bronze", "Silver", "Gold", "Platinum", "Diamond", "Master", "王者!"]
var newcolor = ["green", "pink", "gold", "orchid", "tan", "grey", "white"]
var numbercount;


function setup() {
    createCanvas(640,480);
    frameRate(80);
    pipes.push(makePipes()); 
}


function draw() {


    background(170, 237, 239);

    //create cloud objects
    updateAndDisplayclouds();

    terrain();

    updateAndDisplayPipes();

    drawBird(birdx, birdy);

    controlBird();

    //create pipe each 80 frames
    if (frameCount % 90 == 0 & gameOn) {
        pipes.push(makePipes());
    }

    if (pipes.length != 0) {
        //remove the already gone pipes
        if (pipes[0].x < -50) {
            pipes.shift();
        }

        //hit test
        if (pipes[0].x < birdx + 18 & pipes[0].x + 30 > birdx - 18) {
            if (birdy - 15 < pipes[0].gapY || birdy + 15 > pipes[0].gapY + gap) {
                //hit indicator
                fill("red");
                ellipse(birdx + 5, birdy, 30, 30);
                //make the pipes stop
                for (var i = 0; i < pipes.length; i++) {
                    pipes[i].speed = 0;
                }
                gameOn = false;

                //game over content
                textAlign(CENTER);
                textSize(50);
                fill("orange");
                text("GAME OVER", width / 2, height / 2);
                fill("orange");
                push();
                rectMode(CENTER);
                textSize(18);
                rect(320, 300, 100, 50);
                fill(255);
                noStroke();
                text("RESTART", 321, 307);
                pop();

                //add score
                } else if (pipes[0].x == birdx - 2) {
                successC ++;
            }
        }
    }


    drawBoard();
}

////////////////////////////////
function controlBird(){
    v += g;
    birdy += v;

    //make the bird stop when it hits the bottom or the top
    if (birdy > height & gameOn) {
        birdy = height;
        v = 0;
    }
    else{
        birdy = birdy;
    }

    if (birdy < -30) {
        birdy = -30;
        v = 0;
    }
}

function updateAndDisplayclouds(){
    for (var n = 0; n < 8; n ++) {
        cloudsx.push(-150, random(width) + 150);
        cloudsy.push(random(40, 100));
        cloudsSize.push(random(80, 140));

        clouds[n] = makeClouds(cloudsx[n], cloudsy[n], cloudsSize[n]);
        clouds[n].cdraw();
        cloudsx[n] += cloudsSpeed;
        if (cloudsx[n] < -100) {
            cloudsx[n] = random (width + 100, width + 150);
        }
    }
}

function drawBird(x, y) {
    push();
    translate(x, y);
    angleMode(DEGREES);
    rotate(1.5 * v);
    fill("yellow");
    ellipse(0, 0, 36, 30);
    fill("white");
    ellipse(0 + 9, 0 - 5, 15, 15);
    ellipse(0 - 15, 0, 17, 8);
    fill("black");
    ellipse(0 + 13, 0 - 6, 5, 5);
    fill("orange");
    ellipse(0 + 10, 0 + 8, 20, 8);
    ellipse(0 + 10, 0 + 2, 20, 8);
    pop();
}

function drawBoard(){
    noFill();
    strokeWeight(2);
    stroke(0);
    rect(10, 10, 140, 46);
    noStroke();
    fill(0);
    textSize(18);
    textAlign(CENTER);
    text("Score: " + successC, 80, 30);
    text("Level: " + level[numbercount], 80, 48);
    strokeWeight(1);
    stroke(0);
}


function keyPressed() {
    if (key === " " & gameOn) {
        v -= Fup;
        //set up a maximum speed for the bird
        if (v < -10) {
        v = -10;
        }
    }
}


function mouseClicked() {
    //reset the game when the game is over
    if (mouseX > 270 & mouseX < 370 && mouseY > 275 && mouseY < 325 && gameOn == false) {
        pipes = [];
        gameOn = true;
        successC = 0;
    }
}


function makePipes() {
    var pipe = {
        x: 640,
        gapY: random(50, height - gap - 40),
        pwidth: 50,
        speed: 2,
        move: pipeMove,
        draw: pipeDraw,
        col: "green"
    }
    return pipe;
}


function pipeDraw() {
    //change the color and level when the score gets higher
    numbercount = floor(successC / 5);
    if (numbercount > 6) {
        numbercount = 6;
    }
    fill(newcolor[numbercount]);
    push();
    translate(this.x, 0);
    rect(0, -10, this.pwidth, this.gapY);
    rect(-3, this.gapY - 30, this.pwidth + 6, 30);
    rect(0, this.gapY + gap, this.pwidth, height - this.gapY - gap + 10);
    rect(-3, this.gapY + gap, this.pwidth + 6, 30);

    strokeWeight(5);
    stroke(255);
    line(10, 10, 10, this.gapY - 40);
    line(10, 470, 10, this.gapY + gap + 40);

    line(7, this.gapY - 10, 7, this.gapY - 20);
    line(7, gap + this.gapY + 10, 7, gap + this.gapY + 20);
    pop();
}


function pipeMove(){
    this.x -= this.speed;
}


function updateAndDisplayPipes() {
    for (var i = 0; i < pipes.length; i++) {
        pipes[i].move();
        pipes[i].draw();
    }
}


function terrain() {
    push();
    stroke(135, 210, 167);

    for (var q = 0; q < width; q++) {
        var t = (q * terrainDetail) - (millis() * terrainSpeed / 10);
        var y = map(noise(t), 0, 1 , height / 2 - 40, height - 50);
        line(q, y, q, height);
    }
    pop();

}

function makeClouds(x, y, size) {
    var cloud = {"cx": x, "cy": y, "csize": size, "cdraw": cloudDraw};
    return cloud;
}

function cloudDraw() {
    push();
    noStroke();
    fill(255);
    ellipse(this.cx, this.cy, this.csize, this.csize * 0.7);
    pop();
}

Introduction:

For our group final project, we decided to recreate the once viral game on mobile platforms – Flappy Birds, back in 2013. I(Kai) still recall how I would spend hours playing the game and tried to master it. Unfortunately, the game was removed from App Store because of it being “too addictive”. Up till now, I still have the game on my phone, despite it was too old for my current operating system. In order to experience a very unique game again, we tried to take a shot to develop the game.

 

Development Process:

First of all, Lingfan started to build the basic framework of the game, by creating an object “the bird” that will rise as we press the space button using basic physics, along with an array of tubes as objects that show up as time lapsed from right end of the canvas. Then I’ve taken over the file. I first used primitive geometries to create the tubes and the bird so they resemble the original look of the game. Also I’ve created the background and clouds that moved along with the camera panned, of course, of a much slower speed. As a next step, I started working on the “collision” moments when bird hit the tube. The logic is when the x position of tube passes through the location of the bird, the program determines if the bird is in between the gap of the tubes. If not, the tube turned grey, and an indicator would show up and informed the player the bird has hit the tube. The game was pretty much playable at this point, but we still need a reward/failure system, otherwise the game would run forever, even if the bird hits the tube. Lingfan then took over the file again and created it. He set a Boolean that determines if the game is over or not, which changes its value from true to false as it hits the tube. And then a button was created to restart the game. Every time the bird hits the tube, it would fall and we’ll see the “GAME OVER” appearing at the center of the screen. After the restart button is clicked, the code wipes the existing tubes array and brings back the bird so we can replay the game. As a final step, we worked together to clean up the code and further polish the game, including adding the score counter, a game rank indicator that tells you how good you are playing this game, the change of tube colors as you promote to the next level, the 3-dimensional look of the tube, etc. As this point, the game was pretty much finished.

 

Reflections:

One of the surprises about the game we’ve created is how it felt so close to the original game as we are playing. And during the development of the game, we’ve taken advantage of most skills that we acquired during the semester, so we got to practice pretty much everything again. Please go ahead and give it a try, and we hope you will enjoy the game as much as we do.

 

Game instruction:

Simply tap the space key so the bird rises to avoid the tube and earn 1 point each time the bird flies past a gap. You will get to the next level of the rank every time you successfully passed 5 tubes. (We tried to make it a little easier and more rewarding for you, as the original game was pretty hard to beat.)

Lingfan Jiang – Looking Outwards 12

For this week’s looking outwards, I am going to write about two projects that I found inspiring for my final project.

“Inside” is one of my favorite games. It is a puzzle-platformer adventure game developed and published by Playdead in 2016. The player controls a boy in a dystopic world, solving environmental puzzles and avoiding death.

Image result for inside

The second project that I found interesting is called “Minicade” by Chloe Varelidi who is an indie game designer/ developer. (year unknown) It is a mobile web-app that makes it super easy way to create games with your friends while learning to code along the way. Each person can add a link to one or more games to a custom playlist and instantly play them as one massive game. Here are three examples. 

Comparing the two games I showed above, the most impressive aspect for “Inside” is that the game does not have any written clues that tell you how to play this game, but since the controls of the game are very easy, players are still able to enjoy the game. As for the “Minicade”, different from other Arcade Mode games, the players are able to customize the games themselves which let people learn while playing games.

For the shortcomings of “inside”, since there are no guidance systems in the game, players tend to depend on other people’s successful strategies if they are stuck in the game. It would potentially spoil the gaming experience. As for Minicade, since the number of games is still limited. Players might be tired of the games really easily.

Lingfan Jiang—Project 12—Proposal

For the final project, Kai Zhang and I are planning to build a web game that resembles something similar to the game that was popular in 2014 on mobile platforms -Flappy Bird. The only thing that the player needs to do is to tap the screen so that the bird flaps the wings and ascend to avoid falling or obstacles.

Image result for flappy bird

The idea of the game would be similar, but the style of the graphics would definitely be more original. Also,  additional to the original game, we are thinking about adding more operations into the game. For example, the bird is able to spit out bullets to destroy the cloest obstacle, but the bullets would be limited to a certain number.

Since we decided to do a collaborative project, one person might focus on the generative background and the scoring system. The other one might focus on the general movement of the bird, but more detailed ways of collaboration might be mentioned more in the next post.

Lingfan Jiang – Project 11 – Composition

click to draw

// Lingfan Jiang
// Section B
// lingfanj@andrew.cmu.edu
// Project-11

var nPoints = 10;
var x = [];
var y = [];


function setup(){
    createCanvas(480, 480);
    background(0);
    //generate random points around mouse
    for (var i = 0; i < nPoints; i++) {
        x.push(random(mouseX - 50, mouseX + 50));
        y.push(random(mouseY - 50, mouseY + 50));

    };

}

function draw(){
    strokeJoin(MITER);
    strokeCap(PROJECT);

    //generate new random points around mouse
    if (mouseIsPressed) {
        for (var i = 0; i < nPoints; i++) {
            x.push(random(mouseX - 20, mouseX + 20));
            y.push(random(mouseY - 20, mouseY + 20));
        }; 
        //get rid of the old ones in the array
        x.splice(0, nPoints);
        y.splice(0, nPoints);

        for (var i = 0; i < nPoints; i++) {
            var ttl = makeTurtle(x[i], y[i]);
            //create the sine curve in the middle
            var targetY = 240 + 90 * sin(radians(mouseX))
                ttl.setWeight(4);
                ttl.setColor(color(random(255),random(255),random(255)), 50);
                ttl.penDown();
                //connect the points towards the center sin curve
                ttl.goto(mouseX,targetY);
                ttl.penUp();
        };
    };
}



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;
}

For this project, I am interested in creating a brush myself. Therefore, I generated random points at the position of the mouse and then connected them with the center sin curve. It could look like a curtain sometime.

Lingfan Jiang – Looking Outwards 11

This week, I am interested in the project called “looks like music” done by a Japanese creator Yuri Suzuki in 2013. It consists of a miniature robot which detects and follows a circuit – a black line traced in marker pen – interspersed with colored reference points that the device translates in sound. The main reason I admire it is that it is an easy but super fun project that everyone can be involved in. For the general public, people normally are not able to be part of the artwork, so sometimes they do not understand them. Also, the algorithms behind it should be pretty easy too. The robot has a sensor inside which is able to differentiate between black and white and use “0” and “1” to represent them. As for the color, every color would have a specific sound. Similar to the “pixel.get” command in p5js, the robot is able to get different colors from the paper.

As for the creator’s sensibilities, being a sound artist, designer and electronic musician himself, Yuri Suzuki really incorporated his professions into the project. It is a simple graphic artwork that combined with sound and electrics. His intention is “to raise public awareness of the way in which sound and music are produced”, and I think he did it really well in this project.

Lingfan Jiang – Looking Outwards 10

The project I chose for this week is “Filtered Transparencies 2.0” done by Filipa Valente in 2015.

Filipa is a female creator of dynamic light architectures. As for this project specifically, it is an interactive installation that uses layered light, space, and sound to create an immersive experience. It uses projected imagery and a maze of transparent screens to blur physical spatial boundaries and transports its users into an augmented hologram-like environment. The reason I admire her work is that to me, the most important aspect of architecture is the human experience. Instead of using solid walls to create spatial conditions, she used light to create a completely different world. Hologram is being used a lot in fictional movies, but not many architects are trying to combine those technologies into architecture. Therefore, I think she did a really good job in that area.

Filipa Valente is a Portuguese architect/ environmental designer based in LA. She completed her BSc and Masters in Architecture at the Bartlett School of Architecture in London and the Masters in Media Art and Architecture MEDIASCAPES at SciArc in LA. She has held design and project management positions at different prestigious architectural practices around the world such as Zaha Hadid Architects.

 

Lingfan Jiang-Project-10-Landscape

sketch

// Lingfan Jiang
// Section B
// lingfanj@andrew.cmu.edu
// Project-10

var people = [];


function setup() {
    createCanvas(480, 300); 
    
    // create an initial collection of people
    for (var i = 0; i < 10; i++){
        var rx = random(width);
        people[i] = makePeople(rx);
    }
}


function draw() {
    background(240); 

    updateAndDisplaypeople();
    removepeopleThatHaveSlippedOutOfView();
    addNewpeopleWithSomeRandomProbability(); 

    //belt
    fill(180);
    rect(-5, 190, 500, 110);
    push();
    translate(-100, 0);
    for (var i = 0; i < 20; i++) {
        stroke(100);
        line((i + 3) * 50, 190, i * 50, 300);
    };
    pop();
    stroke("pink");
    line(0, 190, 480, 190);
    line(0, 297, 480, 297);

    //draw sushi bottom
    noStroke();
    fill(255);
    beginShape();
    curveVertex(180, 250);
    curveVertex(250, 240);
    curveVertex(300, 260);
    curveVertex(250, 270);
    endShape(CLOSE);

    //draw sushi top
    fill(255, 160, 160);
    beginShape();
    curveVertex(150, 240);
    curveVertex(170, 235);
    curveVertex(240, 220);
    curveVertex(300, 230);
    curveVertex(330, 260);
    curveVertex(240, 250);
    curveVertex(150, 260);
    endShape(CLOSE);

    //draw lines above sushi
    stroke(255, 201, 201);
    strokeWeight(7);
    line(200, 230, 220, 246);
    line(160, 240, 175, 252);
    line(240, 222, 265, 245);
    line(280, 225, 315, 256);

}


function updateAndDisplaypeople(){
    // Update the people's positions, and display them.
    for (var i = 0; i < people.length; i++){
        people[i].move();
        people[i].display();
    }
}


function removepeopleThatHaveSlippedOutOfView(){
    // If a people has dropped off the left edge,
    // remove it from the array.  This is quite tricky, but
    // we've seen something like this before with particles.
    // The easy part is scanning the array to find people
    // to remove. The tricky part is if we remove them
    // immediately, we'll alter the array, and our plan to
    // step through each item in the array might not work.
    //     Our solution is to just copy all the people
    // we want to keep into a new array.
    var peopleToKeep = [];
    for (var i = 0; i < people.length; i++){
        if (people[i].x + people[i].breadth > 0) {
            peopleToKeep.push(people[i]);
        }
    }
    people = peopleToKeep; // remember the surviving people
}


function addNewpeopleWithSomeRandomProbability() {
    // With a very tiny probability, add a new people to the end.
    var newPeopleLikelihood = 0.007; 
    if (random(0,1) < newPeopleLikelihood) {
        people.push(makePeople(width));
    }
}


// method to update position of people every frame
function peopleMove() {
    this.x += this.speed;
}
    

// draw the people and some windows
function peopleDisplay() {
    fill(255); 
    stroke(0); 
    strokeWeight(5);
    push();
    translate(this.x, height - 100);
    //people outline
    fill(255, 235, 222);
    ellipse(0, 0, this.breadth, this.breadth);
    //eyes
    fill(255);
    strokeWeight(3);
    ellipse(-30, -50, 30, 30);
    ellipse(30, -50, 30, 30);
    fill(0);
    ellipse(-30, -40, 5, 5);
    ellipse(30, -40, 5, 5);
    pop();
}


function makePeople(birthLocationX) {
    var ppl = {x: birthLocationX,
                breadth: random(150, 300),
                speed: -1.0,
                move: peopleMove,
                display: peopleDisplay}
    return ppl;
}

When I first saw this assignment, it reminded me of the conveyor belt sushi right away. However, instead of letting the sushi move in front of a static human, I decided to let the sushi stay. For the generative background, I wanted to learn it since the abstract clock project. It took me some time to figure out how the generative code works, but it is definitely easier than I expected. As a result, it turns out pretty well. If I have more time to work on it, maybe I would do another layer of a background behind the “people”. Overall, it’s a super fun project to do.

sketch

 

Lingfan Jiang – Looking Outwards 09

I looked through Kai Zhang’s posts. One of them talked about the making of Movie CGI Effects in Doctor Strange in 2016. The video attached is very interesting to look at. Here’s the link for that.

It is surprising that the process of CGI making is not all about 3D generated objects. A lot of the times it is a combination of 2D and 3D layers that make up the full imagery.

It is also amazing to see how much software is capable now. Being an architecture student myself, it is still really difficult for me to imagine rendering one frame of the movie. Although the process is a lot different from each other, the final product is still fascinating to look at.

I think in the original post, Kai really explained the process really well. For me, on the other hand, I am interested in what it means to have those amazing effects. First of all, being a Marvel movie, Doctor Strange is a movie with great fighting scenes and fancy looks. However, with those effects, actors had to act based on their imagination and acting experience. As for the movies themselves, we like to watch these kinds of visual effects. In order to not let people feel bored, more and more money is invested to create better effects. As a result, the characters’ emotions become less important in those movies. They are definitely nice movies to watch, but normally we do not learn from them anymore.  Image result for doctor strange cgi

Here is the original post.

Kai Zhang-Looking Outwards-05

 

 

Lingfan Jiang – Project 09 – Portrait

sketch

// Lingfan Jiang
// Section B
// lingfanj@andrew.cmu.edu
// Project-09

var x = [50, 61, 83, 69, 71, 50, 29, 31, 17, 39];
var y = [18, 37, 43, 60, 82, 73, 82, 60, 43, 37];
var underlyingImage;

function preload() {
    var myImageURL = "https://i.imgur.com/U0XImSh.jpg";
    underlyingImage = loadImage(myImageURL);
}

function setup() {
    createCanvas(480, 480);
    frameRate(10);
    //translate the origin a little to make the canvas filled
    translate(-20, -20);
    background(187, 230, 235);
    underlyingImage.loadPixels();
}

function drawStar(sx,sy){
    push()
    translate(sx - 20, sy - 20)
    var nPoints = x.length;

    //draw stars based on the two arrays stated at the beginning
    //also use random function to let the stars have slightly different shapes
    beginShape();
    for (var i = 0; i < nPoints; i++) {
        var ssx = x[i] + random(-3, 3);
        var ssy = y[i] + random(-3, 3);
        scale(0.85)
        vertex(ssx,ssy);
    }
    endShape(CLOSE);
    pop()
}


function draw() {
    //create random points width the size of (500, 500) 
    //to make sure the stars can reach the edge
    var px = random(width + 20);
    var py = random(height + 20);
    var ix = constrain(floor(px), 0, width + 20);
    var iy = constrain(floor(py), 0, height + 20);
    //get color from the underlying image
    var theColorAtLocationXY = underlyingImage.get(ix, iy);

    noStroke();
    fill(theColorAtLocationXY);
    //draw stars based on px, py
    drawStar(px, py);
}



This is a very interesting project which reminds me of the first portrait project we did for the course. It is amazing how much I learned along the way.

For this project specifically, I decided to use the star shape we learned how to do earlier as my pixel. The result turns out very good in the end.

Lingfan Jiang – Looking Outwards 08

This week, I am especially interested in a project in INSTINT 2014 presented by Kate Hollenbach.  The company that the speaker is in is called Oblong. The intention of the software they are designing is to break out from a single screen and use human gestures to create more opportunities.

For decades, our normal interface with computers has been restricted to the possibilities offered by a single screen attached to a keyboard, a mouse, and limited touchscreen function. Although the machines have gotten much smarter, the human-machine interface still remains confined to interactions between one person and one machine. Therefore, in order to make the human-machine interfaces more advanced, Oblong created a 6-degree-of-freedom device that enables pointing at any screen in a Mezzanine room.

To me, I think this is definitely a direction we should aim for. Some people might think this needs a long time to develop and may only exist in fictional movies. However, to give a more common example, I think Apple is also trying very hard in this direction.  The earlier product iPhone X has already started to use gestures to achieve a more intuitive user interface. Therefore, I really admire the spirit of digging into this direction. Overall, I like the idea of encouraging a group of people working together and how their product could help them work more effectively. A lot of the times, conveying new ideas to other people is always the hardest.

For the strategies they present, they used their products step by step which shows one person working at first and then it shows how multiple people can also work together which helps her audience to understand the range of conditions their products are able to accomplish. I think I can definitely learn from it.