/*
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!
]]>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.
]]>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.
]]>
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.
(Completed with 2-day extension)
]]>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)
/*
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;
}
]]>
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
]]>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.
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.
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;
}
}
]]>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…
]]>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/
]]>