#Blog 19: Final INDIVIDUAL REFLECTION GUIDELINES
Food Except đ
Charlotte Xu & Younian LiuïœProfessor Marcela Godoy
- CONCEPTION AND DESIGN:
This project aims to reflect on the situation almost every one locked down in Shanghai are experiencingâhunting for food every day, and also to give people some hope and cheer people up. In my previous preparatory research, I generalized 6 criterias of interactive projects, that is âsimply react doesnât countâ, âindividuality and uniquenessâ, and âvariability of interactivityâ, âsustaining and relatingâ, and âinput and output formatsâ. In consideration of these criteria, we came up with this project that will allow users to play a catcher game with physical sensors instead of keyboards to âhunt for foodâ in the digital world. During the user testing session, we found that it might be a bit dull for the users to just press the pressure sensor, so we decorated them with sheep-shaped cardboard and hang high up on the wall so that people will need to raise their arms to press it. This intends to change peopleâs physical movement from the usual one that they put their arms down to use digital devices on a daily basis, especially under lockdown. They also might be confused by the differences between different categories of food they catch, so we add different sound effects to separate them.
- FABRICATION AND PRODUCTION:
Our main production process is almost all about coding, so we spend a lot of time learning how to adjusting small elements in Processing. We decided to use the microphone input to begin the game to express a sense of desperation. The player shouts out âFOODâ to let the food start falling. People have 0 points that represent LIFE at the beginning of the game and the game has a time limit of 140 seconds for one round, which represents 14 days of quarantine. LIFE increases when people get food and decreases when the fork touches sheep since sheep is âçŸyĂĄngâ in Chinese and its pronunciation is the same as éł, which means positive. Because the code is relatively difficult and new to us, so Ran and I donât have a clear work distribution. We just both try our best to study it and support each other. But I do have to give credit to Ran for editing the video.
- CONCLUSIONS:
The goal of our project is to make a game world that people can fetch the food they want through an unrealistic way like â怩äžæéŠ é„Œâ( a saying in China that means getting something without hard work. It can be translated directly asâ pies falling down from the sky without effortâ). I do believe we achieved this goal. It can form cyclic conversation with the user and give individualized feedback according to their actions. It can also have a sustaining and relating impact on the user to reflect on their mood and states in the lockdown. If I have more time and material, I would try to make the input more fun by changing it to a pillow-like stuff so that people can punch it to relief stress. Also, the game seems a bit slow and lagging, so I might need to figure out how to make it smoother. I can also change the voice of different objects when the users catch them to present, for example the sound of the animal, a burger on a grill, or an ice cream machine, etc.
What I have learned from this project is that every little detail should not be overlooked and that we should be more user-centered when doing projects. Our idea for this project can apply not only to this lockdown, but it also applies to situations when people are exploited of other rights and freedom that they should have.
- TECHNICAL DOCUMENTATION:
Code for Processing
(Source: https://www.youtube.com/watch?v=ndlbz0kwu58 and https://www.youtube.com/watch?v=zSEyjilHD_c)
Tag 1:
import processing.serial.*; import processing.sound.*; import processing.video.*; int NUM_OF_VALUES_FROM_ARDUINO = 2; int sensorValues[]; String myString = null; Serial myPort; AudioIn microphone; Amplitude analysis; Movie myMovie; //added this to have a feedback when you catch something //you can replace the sound SinOsc sine; Env env; float attackTime = 0.001; float sustainTime = 0.004; float sustainLevel = 0.5; float releaseTime = 0.4; Catcher c; Dropfruit[] dropf; Droprefresh[] dropr; Dropsheep[] drops; //Apple[] apples; Timer dropTimer; int numDrops; int dropInterval; int activeDrops; String gameState; int score; Timer countDownTimer; int timeLeft; int maxTime; PImage catcherImage; int x; int r =150; void setup() { size(800, 800); background(237, 222, 139); microphone = new AudioIn(this, 0); microphone.start(); analysis = new Amplitude(this); analysis.input(microphone); sine = new SinOsc(this); env = new Env(this); countDownTimer = new Timer(1000); maxTime = 140; timeLeft = maxTime; PFont myFont; myFont = createFont("Phosphate-Inline", 50); textFont(myFont); gameState = "TITLE"; score = 0; myMovie = new Movie(this, "Never.mp4"); c= new Catcher(); numDrops = 20;//need to adjust, now the orange will run out after we catch them dropf = new Dropfruit[numDrops]; dropr = new Droprefresh[numDrops]; drops = new Dropsheep[numDrops]; //apples = new Apple[numDrops]; for ( int i =0; i < numDrops; i++) { dropf[i] = new Dropfruit(); dropr[i] = new Droprefresh(); drops[i] = new Dropsheep(); } activeDrops = 0; dropInterval = 1000; dropTimer = new Timer(dropInterval); dropTimer.start(); setupSerial(); x = width/2; catcherImage = loadImage("hunter.png"); catcherImage.resize(r, r); //d = new Drop(); } void draw() { background(237, 222, 139); //println(analysis.analyze()); getSerialData(); //printArray(sensorValues); if (gameState =="TITLE") { title(); } else if (gameState == "GAME") { //noCursor(); game(); } else if (gameState == "WIN") { cursor(); win(); } else if (gameState == "LOSE") { cursor(); lose(); } else { println("something went wrong with gameState"); } } //h.update(); //h.display(); boolean intersect (Catcher c, Dropfruit d) { float distance = dist(c.forkX, c.forkY, d.x, d.y); if (distance < 50) { return true; } else { return false; } } boolean intersect (Catcher c, Droprefresh r) { float distance = dist(c.forkX, c.forkY, r.x, r.y); if (distance < 50) { return true; } else { return false; } } boolean intersect (Catcher c, Dropsheep s) { float distance = dist(c.forkX, c.forkY, s.x, s.y); if (distance < 50) { return true; } else { return false; } } void game() { noStroke(); background(237, 222, 139); c.update(); c.display(); //time management if ( dropTimer. complete() == true) { if (activeDrops < numDrops) { activeDrops++; } dropTimer. start(); } for (int i =0; i<activeDrops; i++) { dropf[i].update(); dropf[i].display(); dropr[i].update(); dropr[i].display(); drops[i].update(); drops[i].display(); if (intersect (c, dropf[i])==true) { dropf[i].caught(); score++; println("score= "+score); fill(1,77,103); textSize(36); String a = "basics +1"; text(a, 650, 700); sine.play(); sine.freq(500); env.play(sine, attackTime, sustainTime, sustainLevel, releaseTime); } if (intersect (c, dropr[i])==true) { dropr[i].caught(); score=score+2; println("score= "+score); fill(1,77,103); textSize(36); String m ="snack +2"; text(m, 650, 700); sine.play(); sine.freq(1000); env.play(sine, attackTime, sustainTime, sustainLevel, releaseTime); } if (intersect (c, drops[i])==true) { drops[i].caught(); score=score-10; println("score= "+score); fill(1,77,103); textSize(36); String u = "sheep -10"; text(u, 650, 700); sine.play(); sine.freq(200); env.play(sine, attackTime, sustainTime, sustainLevel, releaseTime); } } if (score >=10) { //need to adjust gameState = "WIN"; } if (score < 0) { //need to adjust gameState = "LOSE"; } fill(1,77,103); textSize(36); String p = "LIFE:" + score; text(p, 90, 90); if (countDownTimer. complete() == true){ if (timeLeft >1){ timeLeft--; countDownTimer.start(); }else{ gameState = "LOSE"; } } String s = "Time Left:" + timeLeft; textAlign(LEFT); textSize(36); fill(1,77,103); text(s,50,50); }//end game void clearBackground() { fill (237, 222, 139); rect(0, 0, width, height); } void resetGame() { for (int i = 0; i< numDrops; i++) { dropf[i].reset(); dropr[i].reset(); drops[i].reset(); } activeDrops=0; score = 0; gameState = "GAME"; timeLeft = maxTime; countDownTimer.start(); } void title() { background(237, 222, 139); fill(1,77,103); textSize(36); textAlign(CENTER); String s = "Shout out FOOD to Play"; text(s, 400, 200); //Sound code float volume = analysis.analyze(); float diameter = map(volume, 0, 1, 0, 1000); if (diameter > 20) { //startTime = millis(); gameState = "GAME"; countDownTimer.start(); } } void win() { background(237, 222, 139); fill(1,77,103); textSize(36); textAlign(CENTER); String s = "You win!\n Dancing is better than hunting "; text(s, 400, 50); float volume = analysis.analyze(); float diameter = map(volume, 0, 1, 0, 1000); if (diameter > 0.1) { if (myMovie.available()) { myMovie.read(); } play(); } } void play(){ myMovie.play(); image(myMovie, 25, 130); } void lose() { background(237, 222, 139); fill(1,77,103); textSize(36); textAlign(CENTER); String s = "You will starve to death\nSHOUT to Play Again"; text(s, 400, 200); float volume = analysis.analyze(); float diameter = map(volume, 0, 1, 0, 1000); if (diameter > 20) { resetGame(); } } void setupSerial() { printArray(Serial.list()); myPort = new Serial(this, Serial.list()[ 3 ], 9600); myPort.clear(); myString = myPort.readStringUntil( 10 ); // 10 = '\n' Linefeed in ASCII myString = null; sensorValues = new int[NUM_OF_VALUES_FROM_ARDUINO]; } void getSerialData() { while (myPort.available() > 0) { myString = myPort.readStringUntil( 10 ); // 10 = '\n' Linefeed in ASCII if (myString != null) { String[] serialInArray = split(trim(myString), ","); if (serialInArray.length == NUM_OF_VALUES_FROM_ARDUINO) { for (int i=0; i<serialInArray.length; i++) { sensorValues[i] = int(serialInArray[i]); } } } } }
Tag 2:
class Catcher { //properties float x, y, w, h, forkX, forkY; //constructor Catcher() { x= width/2; y = height-r; } //methods void update() { if (sensorValues[0]>600) { x=x+20; } if (sensorValues[1]>600) { x=x-20; } //x = mouseX; image(catcherImage, x, y); w = catcherImage.width; h = catcherImage.height; forkX = x+w-30; forkY = y+30; if (x>width-r) { x=width-r; } if (x<0) { x=0; } } void display() { image(catcherImage, x, y); //you can delete this part, it is just to figure out where the fork is fill(0, 0, 0, 0 ); rectMode(CENTER); rect(forkX, forkY, 60, 10); } }
Tag 3:
PImage cucmber; PImage greens; PImage onion; PImage orange; PImage steak; PImage fruit; class Dropfruit { float x, y, w, h; float speedY; String pix; Dropfruit() { x = random(width); y = -10; h = 16; w = 16; speedY = random(5, 10); float r = int(random(1,8)); if (r <= 2) { pix = "cucumber.png"; } else if (r == 3) { pix = "greens.png"; } else if (r == 4) { pix = "onion.png"; } else if (r == 5) { pix = "orange.png"; } else if (r >= 6) { pix = "steak.png"; } } void update() { y+= speedY; if (y > height +h/2) { y = -h/2; } } void display() { fruit = loadImage(pix); fruit.resize(60, 50); image(fruit, x, y); //orange = loadImage("orange.png"); //apple = loadImage("apple.png"); //apple.resize(60, 50); //image(apple,x+100, y+60); } void caught() { speedY =0; y = 0; } void reset() { y=0; speedY = random(5, 10); } }
Tag 4:
PImage cake; PImage hamburger; PImage ice; PImage refresh; class Droprefresh { float x, y, w, h; float speedY; String pix; Droprefresh() { x = random(width); y = -10; h = 16; w = 16; speedY = random(5, 10); float r = int(random(8,13)); if (r <= 9) { pix = "cake.png"; } else if (r == 10) { pix = "hamburger.png"; } else if (r >= 11) { pix = "ice.png"; } } void update() { y+= speedY; if (y > height +h/2) { y = -h/2; } } void display() { refresh = loadImage(pix); refresh.resize(60, 50); image(refresh, x, y); } void caught() { speedY =0; y = 0; } void reset() { y=0; speedY = random(5, 10); } }
Tag 5:
PImage sheep; class Dropsheep { float x, y, w, h; float speedY; //String pix; Dropsheep() { x = random(width); y = -10; h = 16; w = 16; speedY = random(3, 7); //float r = int(random(15,18)); //if (r == 15||r == 16) { // pix = "sheep.png"; //} } void update() { y+= speedY; if (y > height +h/2) { y = -h/2; } } void display() { sheep = loadImage("sheep.png"); sheep.resize(60, 50); image(sheep, x, y); } void caught() { speedY =0; y = 0; } void reset() { y=0; speedY = random(5, 10); } }
Tag 6:
class Timer { int startTime; int interval; Timer (int timeInterval) { interval = timeInterval; } void start() { startTime = millis(); } boolean complete() { int elapsedTime = millis()- startTime; if (elapsedTime > interval) { return true; } else { return false; } } }
Code for Arduino
void setup() { Serial.begin(9600); } void loop() { // to send values to Processing assign the values you want to send //this is an example int sensor1 = analogRead(A0); int sensor2 = analogRead(A1); // send the values keeping this format Serial.print(sensor1); Serial.print(","); // put comma between sensor values Serial.print(sensor2); Serial.println(); // add linefeed after sending the last sensor value // too fast communication might cause some latency in Processing // this delay resolves the issue. delay(100); // end of example sending values }
Circuits: it’s just two pressure sensors, one connecting too A0, one connecting to A1.
Video
May 17, Jingqiao, Younian Liu