Yiran Xuan – Final Project – Dueling Dragons

sketch

/*
Yiran Xuan
Section A
yxuan@andrew.cmu.edu
Final Project
*/

var redframes = []; // array for red dragon sprites
var blueframes = []; //array for blue dragon sprites

var beat; //sound variables
var impact;
var fire;
var growl;
var charge;
/*
status key:
0 = default
1 = attack
2 = shield
3 = reload
4 = impact
5 = shielded impact
6 = death (not real status)
*/

//---------------------------------------
function preload(){ //load all sprites into respective arrays
    //each sprite's index in array should match the status it represents
    //ex) the red dragon's default sprite should have index of zero
    /*
    redframes.push(loadImage("./DragonDefault.png"));  
    blueframes.push(loadImage("./DragonDefault2.png"));

    redframes.push(loadImage("./DragonAttack.png"));  
    blueframes.push(loadImage("./DragonAttack2.png"));

    redframes.push(loadImage("./DragonShield.png"));  
    blueframes.push(loadImage("./DragonShield2.png"));

    redframes.push(loadImage("./DragonReload.png"));  
    blueframes.push(loadImage("./DragonReload2.png"));

    redframes.push(loadImage("./DragonImpact.png"));  
    blueframes.push(loadImage("./DragonImpact2.png"));

    redframes.push(loadImage("./DragonShieldedImpact.png"));  
    blueframes.push(loadImage("./DragonShieldedImpact2.png"));

    redframes.push(loadImage("./DragonDeath.png"));  
    blueframes.push(loadImage("./DragonDeath2.png"));  

    beat = loadSound("rockyoubeat.wav"); //import sounds
    impact = loadSound("Strong_Punch.wav");
    fire = loadSound("Flame.wav");
    growl = loadSound("Growling Lion.wav");
    charge = loadSound("lasercharge.wav");
    */
    
    //default
    redframes.push(loadImage("https://courses.ideate.cmu.edu/15-104/f2018/wp-content/uploads/2018/12/DragonDefault.png"));  
    blueframes.push(loadImage("https://courses.ideate.cmu.edu/15-104/f2018/wp-content/uploads/2018/12/DragonDefault2.png")); 
    //attack
    redframes.push(loadImage("https://courses.ideate.cmu.edu/15-104/f2018/wp-content/uploads/2018/12/DragonAttack.png"));  
    blueframes.push(loadImage("https://courses.ideate.cmu.edu/15-104/f2018/wp-content/uploads/2018/12/DragonAttack2.png"));
    //shield
    redframes.push(loadImage("https://courses.ideate.cmu.edu/15-104/f2018/wp-content/uploads/2018/12/DragonShield.png"));  
    blueframes.push(loadImage("https://courses.ideate.cmu.edu/15-104/f2018/wp-content/uploads/2018/12/DragonShield2.png"));
    //reload
    redframes.push(loadImage("https://courses.ideate.cmu.edu/15-104/f2018/wp-content/uploads/2018/12/DragonReload.png"));  
    blueframes.push(loadImage("https://courses.ideate.cmu.edu/15-104/f2018/wp-content/uploads/2018/12/DragonReload2.png"));
    //impact
    redframes.push(loadImage("https://courses.ideate.cmu.edu/15-104/f2018/wp-content/uploads/2018/12/DragonImpact.png"));  
    blueframes.push(loadImage("https://courses.ideate.cmu.edu/15-104/f2018/wp-content/uploads/2018/12/DragonImpact2.png"));
    //shielded impact
    redframes.push(loadImage("https://courses.ideate.cmu.edu/15-104/f2018/wp-content/uploads/2018/12/DragonShieldedImpact.png"));  
    blueframes.push(loadImage("https://courses.ideate.cmu.edu/15-104/f2018/wp-content/uploads/2018/12/DragonShieldedImpact2.png"));
    //death
    redframes.push(loadImage("https://courses.ideate.cmu.edu/15-104/f2018/wp-content/uploads/2018/12/DragonDeath.png"));  
    blueframes.push(loadImage("https://courses.ideate.cmu.edu/15-104/f2018/wp-content/uploads/2018/12/DragonDeath2.png"));  

    beat = loadSound("https://courses.ideate.cmu.edu/15-104/f2018/wp-content/uploads/2018/12/rockyoubeat.wav"); //import sounds
    impact = loadSound("https://courses.ideate.cmu.edu/15-104/f2018/wp-content/uploads/2018/12/Strong_Punch.wav");
    fire = loadSound("https://courses.ideate.cmu.edu/15-104/f2018/wp-content/uploads/2018/12/Flame.wav");
    growl = loadSound("https://courses.ideate.cmu.edu/15-104/f2018/wp-content/uploads/2018/12/Growling-Lion.wav");
    charge = loadSound("https://courses.ideate.cmu.edu/15-104/f2018/wp-content/uploads/2018/12/lasercharge.wav");
    

}

//--------------------------------------- Game Process functions

var RedDragon;
var BlueDragon;

var secondsbeforereload = 3;
var deadframecounter = 0; //after the endgame, counts number of frames before the game restarts

var cycle = 24; //number of frames in a cycle
var secondspercycle = 1; //how long one cycle
var framerate = cycle/secondspercycle;
var framecounter = 0; //counts frames to control the cycle


var actionwindow = cycle*2/3; //window for player input

function setup() {
    createCanvas(600, 400);
    imageMode(CENTER);
    
    textStyle(BOLD);
    textAlign(CENTER);
    frameRate(framerate);

    RedDragon = createDragon(3, 0, 1, redframes); //start with one health and no ammo
    BlueDragon = createDragon(3, 0, -1, blueframes);    

}

var redturn = true; //becomes false once a player has made move, ensures each player only makes one action per round
var blueturn = true;

function draw() {
    background('white');
    
    if(RedDragon.death || BlueDragon.death){ //if one dragon had died, just display default renders
        GameEnd();
    }

    else{
        if(framecounter < actionwindow){ //Stage 1: allowing player action input, displaying default renders
            fill('green');
            RedDragon.DefaultRender();
            BlueDragon.DefaultRender();
        }

        else if (framecounter == actionwindow){ //Stage 2: processing player actions
            fill('red');
            RedDragon.Action();
            BlueDragon.Action();
            RedDragon.opponentaction = BlueDragon.currentaction;
            BlueDragon.opponentaction = RedDragon.currentaction;
        }
        else if (framecounter < cycle*5/6){ //Stage 3: display player actions
            fill('red');
            RedDragon.ActionRender();
            BlueDragon.ActionRender();
        }
        else if (framecounter == cycle*5/6){ //Stage 4: process opponent actions and end result
            fill('red');
            RedDragon.Result();
            BlueDragon.Result();
        }
        else{ //Stage 5: display result of interaction
            fill('red');
            RedDragon.ResultRender();
            BlueDragon.ResultRender();
        }

        framecounter++; //framecounter is here music stops after end of the game
    }

    textSize(30);
    fill('green');
    text("Health: " + RedDragon.health, RedDragon.x, RedDragon.y - 75); //display current healths
    text("Health: " + BlueDragon.health, BlueDragon.x, BlueDragon.y - 75); 


    textSize(15);
    fill('red');
    text("J to attack, K to shield, L to reload", RedDragon.x, RedDragon.y - 125); //display current healths
    fill('blue');
    text("A to attack, S to shield, D to reload", BlueDragon.x, BlueDragon.y - 125);


    if (framecounter == cycle){ //when cycle is complete
        framecounter = 0;
        redturn = true; //allows players action again
        blueturn = true; 
        RedDragon.Reset(); //and reset round variables (currentmove, opponentmove, status) 
        BlueDragon.Reset();
        beat.play();//beat starts playing here
    }
}


//--------------------------------------- Event functions

function GameEnd(){
    
    RedDragon.DefaultRender(); //display the dragons; DefaultRender contains death render as well
    BlueDragon.DefaultRender();

    var winner = "Nobody"; //default is draw 
    if(RedDragon.health > BlueDragon.health){ //comparing health to determine victor
        winner = "Red Dragon";
    }
    if(RedDragon.health < BlueDragon.health){ 
        winner = "Blue Dragon";
    }

    fill('black');
    textSize(50);
    text(winner + " Wins!", width/2, 75); //writing victory text

    deadframecounter++; 
    if(deadframecounter == framerate*secondsbeforereload){ //reloads entire page after 3 seconds to restart game
        location.reload();
        }

}


function keyPressed(){
    if(key == 'a' || key == 's' || key == 'd'){ //is the pressed key a valid blue action?
        if(framecounter < actionwindow & blueturn){ //is it within the action window, and has blue made a move already?
            switch(key){
                case 'a':
                    BlueDragon.currentaction = 1; //A to fire
                    break;
                case 's':
                    BlueDragon.currentaction = 2; //S to shield
                    break;
                case 'd':
                    BlueDragon.currentaction = 3; //D to reload
                    break;
            }
            blueturn = false; //blue has spent its move
        }
    }

    if(key == 'j' || key == 'k' || key == 'l'){ //is the pressed key a valid red action?
        if(framecounter < actionwindow & redturn){ //is it within the action window, and has blue made a move already?
            switch(key){
                case 'j':
                    RedDragon.currentaction = 1; //A to fire
                    break;
                case 'k':
                    RedDragon.currentaction = 2; //S to shield
                    break;
                case 'l':
                    RedDragon.currentaction = 3; //D to reload
                    break;
            }
            redturn = false; //red has spent its move
        }
    }
}

//--------------------------------------- Dragon creation functions

function createDragon(startinghealth, startingammo, drdirection, drsprites) {
    var dragon = {
        
        health: startinghealth,
        death: 0,
        ammo: startingammo,
        shield: false,
        status: 0, 
        currentaction: 0, //action key: 0 = nonaction, 1 = shoot, 2 = shield, 3 = reload
        opponentaction: 0,

        direction: drdirection,
        sprites: drsprites,
        x: width/2 + drdirection*150, //where to render the sprite
        y: 225,

        Action: dragonAction, //processing gameplay
        Result: dragonResult,
        Reset: dragonReset,
        GameOver: isAlive,

        ActionRender: dragonActionRender,
        ResultRender: dragonResultRender,
        DefaultRender: dragonDefaultRender };
    return dragon;
}

/*
status key:
0 = default
1 = attack
2 = shield
3 = reload
4 = impact
5 = shielded impact
6 = death (not real status)
*/

function dragonAction(){ //function to process dragon's action
    switch(this.currentaction){ //processing currentaction
        case 0: //if no action
            break;

        case 1: //if attacking
            if(this.ammo == 0){ 
                this.currentaction = 0; //if attempted to attack without ammo, will change to no action
            }
            else{
                this.ammo--;
                fire.play();
            }
            break;

        case 2: //if shielding
            this.shield = true;
            break;

        case 3: //if reloading
            this.ammo++; //can reload indefinitely; reloads even if attacked
            charge.play();
            break;
    }

    this.ActionRender();
}


function dragonResult(){//function to process result of both dragon's actions
    this.status = this.currentaction; //by default, status is the same as action

    if(this.opponentaction == 1){ //unless attacked by other dragon
        if(this.shield){
            this.status = 5; //shield is up, attack is blocked
            impact.play();
        }
        else{
            this.status = 4;
            this.health--; //otherwise, attack hits, takes damage
            impact.play();
        }
    }
    this.GameOver();
    this.ResultRender();
}

function isAlive(){ //checks if dragon is alive or dead
    if (this.health < 1){
        this.death = 1;
        growl.play();
    }
    else this.death = 0;
}


function dragonActionRender(){ //function to display the dragon
    image(this.sprites[this.currentaction], this.x, this.y, 300, 225);
}

function dragonResultRender(){ //function to display the dragon
    image(this.sprites[this.status], this.x, this.y, 300, 225);
}

function dragonDefaultRender(){
    image(this.sprites[this.death*6], this.x, this.y, 300, 225); //renders either alive or dead state

}

function dragonReset(){ //resets the dragon actions and status to default at the end of the round
    this.currentaction = 0;
    this.status = 0;
    this.opponentaction = 0;
    this.shield = false;
}

(Using 1 Grace Day.)

(Recommend clicking the game to start it)

Dueling Dragons is the virtual, 1-computer-2-players version of the playground game “Shotgun”, in which players attempt to defeat their opponent by playing one of 3 moves (shoot, shield, reload) in accordance to a beat, usually clapping twice.

In my version, two dragons are fighting each other while “We Will Rock You”s stomp-stomp-clap plays; The final clap is starts a short time window in which both players can perform an action using the keys displayed.

Each player starts with 3 health and no ammo. Health is displayed, but ammo isn’t, for tactical reasons.

A player can make 4 moves:
0) no action (don’t press a key or press it out of the timing window); left vulnerable
1) attack; will expend an ammo only if there is any, otherwise will become no action
2) shield; protects the player from damage
3) reload; increases ammo by one, can increase indefinitely. A reload is still successful even if the player is damaged.

If both players are attacked, both of them will suffer damage

The game continues until at least one player’s health drops to zero, after which a win screen appears for 3 seconds, and then the page attempts to refresh (recommend manually refreshing the page)

Of course, there are sound effects!

Yiran Xuan – Looking Outwards 12

Greg Borenstein‘s work is quirky and fun, utilizing lofty technology to bring tokens of enjoyment to life. One of these is Ten Seconds, a short game that involves your player ball jumping around trying buy more time for the game by attempting to obtain collectibles, while also avoiding hazards. Containing one interesting and “perpetual” mechanic, this game has the same spirit as the likes of Temple Run, Flappy Bird, and Tiny Wings, “toilet games” as I like to call them, capable of keeping one amused for the duration of a sit, but also having the potential to be competitive and give tryhards a challenge. This is the type of game I’d aspire my own project to be, though it does not have a “perpetual” mechanic. I wish to emulate the clean style and effective sound design of the game, though I wish Mr. Greg would remove gravity from the game and maybe add more depth to the levels, so that it would almost be like an exploration campaign rather than a boxed and contained experience.

Yiran Xuan – Project 12 – Proposal

My project idea is to recreate the schoolyard clapping game “Shotgun” in Javascript. The game consists of each rounds, within each round two (or more) players face each other and simultaneously play one of three moves: “Shoot”, “Reload”, and “Shield”.  Shielding blocks all incoming bullets, and can be used any round; reloading adds one bullet to the chamber, but leaves the player vulnerable; shooting fires a bullet toward the (or an) opponent, but can only be used if the player has at least one bullet in the chamber already. The game ends when one player plays shoot while their target is reloading. This game has a luck element but is also strategic, good players being able to predict when their opponents would be left vulnerable. Another important element to the game is clapping, which helps ensure simultaneous play; typically, players would clap their hands on their thighs twice before making their move, establishing a beat.

For this project, I intend to represent the two players as two animated dragons spitting fireballs at each other. Players would have 4 separate key each to play, 3 for the moves, and 1 for “clapping”. I will play a short sound regularly to establish a beat, and players would need to press the “clap” button within a certain time frame, or they suffer consequences (like a rhythm game). I would need counters to keep track of the refresh rate. I will also build a random move-generating AI. For most of the interface, I will draw outside of the program and import in as images.

 

Yiran Xuan – Looking Outwards – 11

I am a fan of a wide variety of music, including American folk music and Chinese folk music. In the middle of switching between Sanxian (Chinese traditional 3-string banjo) playlists to bluegrass playlists, I suddenly noticed that there was a great many similarities between the two genres. Both featured fast, colorful strings that operated in the major scales, often pentatonic scales, giving them energetic but grounded feelings. The topic of the songs are also similar, describing longings of home, the beauty of rural environments, and amusing interpersonal relationships. What this meant to me was despite the genres being folk, there was room for new growth and cross-cultural synthesis, and that the two cultures I have grown up were strangely bridged.

I searched for any realized union of the two genres, and I was happily surprised to find Redgrass:
The Chinese musical group featured frequently experiments with combining traditional Chinese instrumentation with the music from other cultures, including American jazz, Malaysian percussion, and experimental electronic music.

Website

(Completed with 2-day extension)

Yiran Xuan – Project 11 – Composition

This sketch is based off of my favorite drawing tool in FireAlpaca, the Mirror Brush. It initializes two different turtles that move to the mouse’s position when the mouse is clicked. Buttons on the keyboard can affect the pen’s path; pressing ‘p’ will toggle the penUp and penDown, ‘w’ and ‘t’ will widen and thin the pen tracks respectively, while ‘r’, ‘b’, ‘g’, ‘l’ change the color of the tracks.

(Completed with 2-day extension)

sketch

/*
Yiran Xuan
Section A
yxuan@andrew.cmu.edu
Project 11
*/

//Goal: Create mirroring paint tool that uses the turtle graphics

var brushturtle; //turtle that corresponds to mouse movements
var mirrorturtle; //turtle that mirrors brushturtle

var targetx = 0; //x coord of where brush turtle should go
var targety = 0; //y coord of both turtles
var mirrorx = -targetx; 

var brushweight = 5;

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

    translate(240, 0);

    brushturtle = makeTurtle(0, 0); //initialize turtles
    mirrorturtle = makeTurtle(0,0);

    brushturtle.setColor('black');
    
    mirrorturtle.setColor('black');
    mirrorturtle.setWeight(brushweight);

}

function draw(){
	translate(240, 0); //needs to be constantly retranslated so that 0 line is down the middle of canvas

	brushturtle.setWeight(brushweight);
	mirrorturtle.setWeight(brushweight);

	brushturtle.goto(targetx, targety); //transport brush
	mirrorturtle.goto(mirrorx, targety); //transport mirror
}

function mouseClicked() {
	targetx = mouseX - 240; //mouse coords ignores translate, so needs compensation
	targety = mouseY;
	mirrorx = -targetx;
}



function keyPressed(){
	if(key == 'p'){ //pressing 'p' switches between pen position 
		penUporDown();
	}

	if(key == 'w'){
		penWider();
	}

	if(key == 't'){
		penThinner();
	}

	if(key == 'r'){ //changing pen colors
		brushturtle.setColor('red');
		mirrorturtle.setColor('red');
	}

	if(key == 'g'){
		brushturtle.setColor('green');
		mirrorturtle.setColor('green');
	}

	if(key == 'b'){
		brushturtle.setColor('blue');
		mirrorturtle.setColor('blue');
	}

	if(key == 'l'){
		brushturtle.setColor('black');
		mirrorturtle.setColor('black');
	}

}

function penUporDown(){ //lifts or puts down pen
	if(brushturtle.penIsDown){
			brushturtle.penUp();
			mirrorturtle.penUp();
		}
	else if(brushturtle.penIsDown == false){
		brushturtle.penDown();
		mirrorturtle.penDown();
		}
}

function penWider(){ //widens penstroke
	brushweight+= 0.5;
}

function penThinner(){ //thins penstroke
	if(brushweight > 1){
		brushweight -= 0.5;
	}
}

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

 

Yiran Xuan – Looking Outward 10

I greatly admire Robin Hunicke’s work in the video game industry, especially in two very unique and memorable games, Boom Blox and Journey, the latter of which has won multiple awards for its visuals and interactions. Robin started in the industry at Electronic Arts, working on the Sims, before joining thatgamecompany as a producer. She recently started her own company, Funomena, to create VR and art games, such as Wattam in collaboration with the creator of Katamari Damacy.

Journey is a very emotional game, despite having no dialogue, and this is due to the very deliberate mechanics and environmental design choices that Robin had done research upon; players can be led to feel a certain way simply by an interaction. Robin also does research into dynamic difficulty adjustment, which is when a game procedurally evaluates player interactions and gameplay to adjust its difficulty.

http://danm.ucsc.edu/faculty/robin-hunicke

Yiran Xuan – Project 10 – Landscape

sketch

var mountains = [];
var grass = [];
var stars = [];


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

    stars.push(makeStar());

    for(var j = 0; j < 10; j++){
    mountains.push(makeMountainPoint(width + j*80));
    }

    grass.push(makeGrass());
}


function draw() {
    background(25, 25, 112);
    drawGround();

    updatePositions(); 
    updateArrays();

    newStar(); //one star per x position
    newMountainPoint(); //one mountain point per x position
    newGrass(); //one grass per x position

    for(var i = 0; i < stars.length; i++){
        stars[i].display();
    }

    mountainDisplay();

    for(var k = 0; k < grass.length; k++){
        grass[k].display();
    }

   // textSize(32);
    //text(mountains.length, 240, 240);

}

function drawGround(){
    noStroke();
    fill('green');
    rect(0, 320, 480, 160);
    noFill();
}

function updatePositions(){
    // Update positions of stars, mountains, and grass
    for (var i = 0; i < stars.length; i++){
        stars[i].move();
    }

     for (var j = 0; j < mountains.length; j++){
        mountains[j].move();
    }

     for (var k = 0; k < grass.length; k++){
        grass[k].move();
    }
}

function updateArrays(){ //updating all arrays to delete objects that have gone offscreen
    var starsremaining = []; //temporary transfer array
    for (var i = 0; i < stars.length; i++){
        if (stars[i].x > 0) { //if the star is still in frame, put in transfer array
            starsremaining.push(stars[i]); 
        }
    }
    stars = starsremaining; //update normal array, oldest stars now deleted

    var mountainsremaining = []; //temporary transfer array
    for (var j = 0; j < mountains.length; j++){
        if (mountains[j].x > -80) { //deletes the oldest mountain point only until the second oldest point is at the border
            mountainsremaining.push(mountains[j]); 
        }
    }
    mountains = mountainsremaining; //update normal array, oldest mountain point now deleted

    var grassremaining = []; //temporary transfer array
    for (var k = 0; k < grass.length; k++){
        if (grass[k].x > 0) { //deletes the oldest mountain point only until the second oldest point is at the border
            grassremaining.push(grass[k]); 
        }
    }
    grass = grassremaining; //update normal array, oldest mountain point now deleted

}

function newMountainPoint() {
    if (mountains[6].x < 480 & mountains.length < 10) { //generates new point only if last point has passed onto the screen 
        var onebefore = mountains[mountains.length-1].x;
        mountains.push(makeMountainPoint(onebefore + 80));
    }
}

function newStar(){
    if (random(0, 100) < 10){ //makes a star at x position 10% of the time
        stars.push(makeStar());
    }
}

function newGrass(){
    if (random(0,10) < 8){ //makes a grass stalk at x position 80% of the time
        grass.push(makeGrass());
    }
}


// method to update position of building every frame
function starMove() {
    this.x -= this.speed;
}

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

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

// draw the building and some windows
function starDisplay() {
    stroke('white');
    strokeWeight(this.starsize);
    point(this.x, this.y);
}

function mountainDisplay(){ //displays mountain range all at once, as each line segment uses two array elements
    strokeWeight(1);
    stroke('grey');
    for(var j = 0; j < mountains.length - 1; j++){
        line(mountains[j].x, mountains[j].y, mountains[j+1].x, mountains[j+1].y);
    }
}

function grassDisplay(){
    strokeWeight(3);
    stroke('green');
    line(this.x, 320, this.x, 320 - this.stalkheight);

    if(this.flower < 1){ //grows a flower 10% of the time
        strokeWeight(8);
        stroke('yellow');
        point(this.x, 320-this.stalkheight);
    }
}


function makeStar(){
    var star = {x: width, //stars spawn at the right edge
                pointdistance: 40,
                speed: 3,
                starsize: round(random(0, 4)),
                y: round(random(0,160)),
                move: starMove,
                display: starDisplay}
    return star;
}

function makeMountainPoint(spawnpoint) {
    var mountain = {x: spawnpoint, //mountain peaks spawn past the right edge
                pointdistance: 40,
                speed: 5,
                y: round(random(160,280)), //height of the point
                move: mountainMove}
    return mountain;
}

function makeGrass(){
    var stalk = {x: 480, //grass at the right edge
                pointdistance: 40,
                speed: 7,
                stalkheight: round(random(0,30)), //height of the grass stalk
                move: grassMove,
                flower: random(0,10), //determines whether stalk grows a flower or no
                display: grassDisplay}
    return stalk;
}

This project was inspired by Tiny Wings, specifically the peaceful nighttime scene in which a starry night sky slowly drifts by. I wanted to have a green lawn foreground for extra serenity.

The stars and grass were easy to implement, but I had trouble rendering and generating new instances of the mountain peaks. Because I wanted a continuous mountain ridge, the objects being generated were points, and so the display function was an external function that processed the entire mountains array. Difficulty was also had in timing when a new mountain point would be generated; while grass and stars were generated all the time and deleted whenever they went off-screen, mountain points could only be deleted when the second oldest point started to go offscreen in order to leave no gaps. In the same vein, new mountain points needed to be generated off-screen on the other side.

Yiran Xuan – Project 09 – Portrait

I am using 1 of my grace days for this late submission.

This project was centered around the idea of portraying brightness by the size of the object and letting background shine through as opposed to just coloring them. This was achieved by taking the brightness value of each pixel visited (this version visits only every 15 pixels), multiplying them by a coefficient, and using as the size of the small triangles that make up the picture. The ultimate effect is surprisingly good, with just two colors and shades able to portray nuanced lighting and shadows.

sketch

var portrait;
var spacing =15; // distance between columns
var sizingcoefficient = 12; //ratio that greyscale value is converted into triangle dimension

function preload() {
    //var myImageURL = "./selfportrait.jpg";
    var myImageURL = "https://imgur.com/a/MHAIBeE"
    portrait = loadImage(myImageURL);
}

function setup() {
    createCanvas(480, 480);
    background('grey');
    portrait.loadPixels();
    frameRate(120);

   	/*
	for(var i = 0; i <width*height; i+=15){
    	var pixelx = i%width; //x position of pixel
    	var pixely = floor(i/width); //y position of pixel
    	var greyscalevalue = brightness(portrait.get(pixelx, pixely))/12;

    	
    	noStroke();
    	//fill(greyscalevalue);
   		//ellipse(pixelx, pixely, 3, 3);
   		fill('orange');
   		//ellipse(pixelx, pixely, greyscalevalue, greyscalevalue);
   		triangle(pixelx, pixely, 
   			pixelx + greyscalevalue, pixely - greyscalevalue, 
   			pixelx + greyscalevalue, pixely + greyscalevalue);

  	}
	*/
	}

var i = 0;

function draw() {
		
	var pixelx = i%width; //x position of pixel
   	var pixely = floor(i/width); //y position of pixel
   	var greyscalevalue = brightness(portrait.get(pixelx, pixely))/sizingcoefficient; //retrieving brightness of pixel and converting into "greyscale value"

   	noStroke();
   	//fill(greyscalevalue);
   	//ellipse(pixelx, pixely, 3, 3);
	fill('orange');
   	//ellipse(pixelx, pixely, greyscalevalue, greyscalevalue); 
	triangle(pixelx, pixely, 
   	pixelx + greyscalevalue, pixely - greyscalevalue, 
   	pixelx + greyscalevalue, pixely + greyscalevalue); //drawing triangle; size of triangle is determined by brightness of pixel

	i+= spacing; //incrementing pixel; draw loop is essentially a giant for-loop

	if (i == width*height){
		i = 0;
	}
}

Yiran Xuan – Looking Outwards 09

Audrey Zheng’s Looking Outward 03 Entry about Madeline Gannon’s work, REVERB caught my attention due to my own interest into 3D printing and bringing digital objects into the physical world. I was curious about what the explanation meant by “tracking a squid’s movements through time and space”, and what it had to do with a kinect, until I watched the video. Essentially, a hand’s movements are tracked, and a virtual “squid” follows it and mirrors it across a virtual human model. A snapshot of its tentacle’s positions are recorded and the compilation of these snapshots form a discrete but connected shape. It looks beautiful for 3 reasons I believe: it is complex at first glace, it is symmetrical, and its lines are smooth. I have concerns whether all generated necklaces are structurally sound when printed, however…

Link to the work

Yiran Xuan – LookingOutwards – 08

Darius Kazemi programs and creates what are essentially meme machines, automated generators of jokes based off of a defined format and filling in the blanks using ConceptNet, a program that links words with each other by their meaning. Kazemi’s projects are interesting as they allow our senses of humor, which we take to be organic and characteristic of human nature, to be emulated by machines; some of the examples generated by his bots are quite funny even.

http://tinysubversions.com/