#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

Leave a Reply

Your email address will not be published. Required fields are marked *