(IL-Moon) Final Project: Twinkle with Fingers (by Eos)

Date: December 7th

Documented on: December 7th

Documented by: Yu Xu (Eos)

Instructor: Moon Junghyun

Demo

Project Description

Twinkle with Fingers is basically a music visualizer: User can mix the music clips by gestures and visualize it as lighting art. The lighting visuals consist of a 2- meter RGB LED strip, whose colors change according to the change of music amplitude, and a projection of the graphs on Processing screen(colorful ellipses move with users’ fingers and their sizes change according to the change of music amplitude, too.).  There are three music clips for users to play with: change the amplitude with hand vertical movement and mix the clips with parallel movement.

Conceptual Development

Inspirations & Motivation:

I, (and I believe everyone), love those twinkling fancy artworks very much! Also, I have the passion for creating such stuff using LEDs since midterm. Here are some specific inspirations & references that contribute to my idea:

1.Leap Motion Apps

There are many apps designed for leap motion, which allows users play with the visuals on the screen using gestures / finger positions. I think this is a good idea to use flexible hand movement as an “analog input” rather than forcing users to trigger a sensor. Users thus have the freedom to make own design instead of following the fixed setting of a project. However, one problem with existing leap motion apps is that few of them can create multi-sensory interaction, such as music (listening) and graphs (visual). Another problem is that those apps are set in a flat screen so they don’t have a physical form. This will reduce the visual impact on users and people’s interest can be distracted from it in a short time.

ReferenceThe Leap Motion Controller Hands-On Demo – Best Apps of Airspace HD

2.Music Fountains

I was at the world famous music fountain show in Lima, Peru this summer. The visual impact is really amazing with generative arts projected on the water and everything changes with music. This is a good example of physical form artwork which combines listening and visuals together. If this kind of thing is made into an interaction project, it will be more popular because people turn into users who can engage in the design instead of audience who stand there still.

Reference: Peru – Dancing Fountains of Lima

3.DIY LED Bottles

This is a kind of DIY artwork that makes me fascinated with LEDs. It is a good decoration artwork but the problem with it is same as the music fountain’s: no interaction. Also, it’s not a multi-sensory project.

Reference: Bottle Brights – 6th DIY of Christmas!

4. Existing Music Visualizer Projects

There are a lot of simple music visualizer projects using Arduino. The problems with it: 1. the LEDs and circuits are exposed outside, resulting in lack of beauty. 2. most of them are not interactive.

Reference: Waren’s L.E.D Music Visualizer – Rainbow Veins by Owl City

Technical Development

1.Design Stage

How my project is going to work

Original Sketch

A acrylic box contains glass marbles, a LED strip and a foam screen. A projector outside of the box to do graph projection. Everything turns out to be realized instead of the fans and the flying foam screen! In practice, I found foam’s absorbability is too strong and when they are blown by fans, they tend to stick on the box instead of flying in the air. So at last, I stick the foams on the box directly to do the projection.

Materials Needed

Acrylic box * 1, glass marbles * 400, foam, 2-meter-long LED strip * 1, power jacket * 1, 12V adapter * 1, LED strip driver * 1, Jumper cables, Arduino UNO * 1.

Schematic

The LED controller in picture 1 is actually the driver in picture 2. Since I’m just using one strip, the chainable output is not used.

Real-life schematic

On the driver’s input side, CLK, DIO and GND are used while NC is not used.

2. Implementation Stage

Fabrication

Glue glass marbles on the box

Glass marbles are really reflective materials and they can create super pretty lighting artworks. Hot gluing them on the box and let the LED shines through them is a good way to create stronger visual impact.

400 marbles, glued one by one, 3 hours.

Done!

How beautiful they look with LEDs.

The foam I’m using. Pic credit to Taobao.

Make up the box and make a foam screen using transparent adhesive tape! Fabrication done!

Prototypes & Experiments

Experiments

First, for the Arduino part, make the LEDs turn RGB respectively with the example code in the LED strip library (available in: ima.nyu.sh- equip- led strip driver- documentation page).

For the Processing part, use mouseX as experiment before using finger position. Divide the canvas into three columns. Each column contains one music clip. By moving mouseY, you change the amplitude. By moving mouseX, you change the clips. (This part will be discussed more in Lessons learned- trials and errors.)

Sound Clips credit to:

  1. a sweet place: A sweet place
  2. violin: Beautiful violin music
  3. symphony: Symphony Sound

Then, I used the sample AmplitudeRMS code in sound library to test amplitude analysis [rms.analyze()] and made the serial communication with Processing and Arduino. Note: this experiment uses one fixed music clip.

However, in this experiment, the LED strip is blinking and does not change accordingly to the volume. To address this problem: 1. double check the code for serial communication, 2. smooth the volume, 3. give less delay to LED, 4. introduce the concept of sin function and ampOffset (These will be discussed in detail in Lessons Learned- trails & errors).

After making the adjustments, the LED visualizer works like this:

The effect after sticking the LEDs on glass marbles:

OK, the visualizer is basically working! Then I started to consider what kind of graphs should be projected. I used to think of galaxy simulation, but I found that it is a little beyond me and I don’t want to copy any of others’ code. So I thought of colorful ellipses that have trajectories inspired by the random color and opacity function that are talked about in class! The images of colorful ellipses also correspond to the presence of glass marbles.

However, I tried to adjust the transparency of the background as we did in class, I found that the background color would not be pure white after the ellipses moved. I turned to fellow Jiwon and we found the “easing” function on Processing.org and I created an array of ellipses. (To be discussed in Lessons Learned)

I thought of changing the diameters of the ellipses into volume values but the effect was bad when the music is too quite or too loud. So I asked some peers to do user testing, they agreed that fixed diameter ellipses are more interesting to play with: they can be in love or in a fight depending on how they move fingers.

After the colorful moving ellipses are in place, I did the test of projection. It looks cool on smooth surface like papers. So afterwards, I tried to make my foam screen also smooth and in uniform distribution.

 

Lessons Learned

Trials & Errors and Know-hows

1.Why my Processing can’t do “loop” and always says “could not run the sketch” when I tried to edit music clips?

Even without any problem in code, this error still occurs and my project was once stuck because of this bug. I asked fellows for help, and after seeking solutions on Processing.org, we found that we can use code to restart the music like this:

In the global area, create these values:

int duration;

float startTime = 0;

int duration1;

float startTime1 = 0;

int duration2;

float startTime2 = 0;

Then, in void setup, we introduce a function for timing effects called millis() (Reference page here):

 duration = int(asweetplace.duration());

  println(duration);

  startTime = millis();

  duration1 = int(symphony.duration());

  println(duration1);

  startTime1 = millis();

  duration2 = int(violin.duration());

  println(duration2);

  startTime2 = millis();

In void draw, we uses the timing function to loop the sound when it ends:

if (startTime + duration * 1000< millis()) {

    println("start again!");

    asweetplace.play();

    startTime = millis();

  }

  if (startTime1 + duration1 * 1000< millis()) {

    println("start again!");

    symphony.play();

    startTime1 = millis();

  }

  if (startTime2 + duration2 * 1000< millis()) {

    println("start again!");

    violin.play();

    startTime2 = millis();

If you only want to play three sound files, this method is fine. But for me, it doesn’t work when I plan to edit the sound file (change the amplitudes). So trial 1, failed.

Then I tried to use minim library as suggested by Prof. Moon. Failed again and it says: volume is not supported.

Finally I find that in the sample code, the sound file is mono and the mode is .aiff. But the Processing.org says sound library supports mp3 and wav, which is my sound files. However, after the music clips are transferred into mono and .aiff, they work perfectly!

2.Why my LED is blinking regardless of the music? How to make the visualization work?

After double-checking my original code, I didn’t find any mistakes other than the long delay. And after Prof. Moon helped me check the code, we found that it is the fault of serial communication in the Arduino part, which I copied and pasted from the examples: the code should not begin with “if (Serial.available())”, it should be while!

When the LED works constantly, new problems come. The volume derived from analyze function changes too fast and the LED seems to work abnormally. In this case, “lerp()” function is introduced for smoothing! Reference page here

Basically, how lerp functions work is similar to a mathematical idea called linear interpolation:

The aim is similarly to make the journey from x0 to x1 slower.

To prevent the amplitude bouncing quickly from one value to another, it is smoothed by crossing the point (x,y). Then, use the smoothed amplitude.

After smoothing, the idea of sin function and ampOffset is introduced by Prof. Moon. By claiming “ampOffset += amp”, it adds the values of the right to the left and the LED can reflect the accumulated amplitude effect. Afterwards, the ampOffset value should be put into sin function, whose range is -1 to 1, and map it from 0-255. Finally assign these values to R,G,B and send them to Arduino.

3.How to create an ellipse with trajectory but without tradeoff effect on the background?

I used to create colorful ellipses with trajectory using a rectangle with opacity whose center is 0,0. However, it works only with dark background. When using white one, it will be dyed.

After turning to Processing.org, I found a useful function called easing. Reference page here. In this method, I basically created an array of 50 ellipses and make them follow the first one controlled by leap motion. Also, change their opacity to make them look more transparent.

Tips & Conclusions

1.Individual work can be more efficient and meaningful! My midterm project is done with Phyllis so I want to try making a project on my own in the final! Actually, I found individual work is better: no chance for being a free-rider, freedom to arrange your time and knowing better about my weakness. I feel I have learned more by doing on my own.

2. Turn to the right person for help! Fellows have their own strengths, make sure you turn to those who are experienced of the skill. (Thanks to Jiwon for introducing me to sound editing softwares!)

3.Try not to have others write the code for you. A good way to learn may be: When you are struggling with something, ask for the direction to go and do trials on your own. I used to say: “I can’t do this” but now I’ll say: “Let me try first.” If there’s anything goes wrong, turn to Processing.org or Arduino reference page for help first.

4.Try to start doing your project as early as possible! There may be a ton of bugs and errors to deal with. Though I started almost two weeks before the due, I still feel a little bit hurry.

 

import processing.serial.*;
import processing.sound.*;

int NUM_OF_VALUES = 3;
Serial myPort;
String myString;

int values[] = new int[NUM_OF_VALUES];

int prevArea;
int amp;
float ampTarget;
float volume;
int ampOffset= 0;
Amplitude rms;

SoundFile asweetplace;
SoundFile symphony;
SoundFile violin;

float fingerIndexX, fingerIndexY;
float fingerMiddleX, fingerMiddleY;
float fingerRingX, fingerRingY;

float[] x1 = new float[50];
float[] y1 = new float[50];
float[] x2 = new float[50];
float[] y2 = new float[50];

boolean x;
boolean y;
boolean z;


void setup() {
  fullScreen();
  //size(500, 500);
  background(255);
  setupLeapMotion();

  asweetplace = new SoundFile(this, "a sweet place.aiff");
  symphony = new SoundFile(this, "symphony.aiff");
  violin = new SoundFile(this, "violin.aiff");

  initial();

  rms = new Amplitude(this);
  rms.input(asweetplace);


  for (int i = 0; i < x1.length; i++) {
    x1[i] = 0;
    y1[i] = 0;
    x2[i] = 0;
    y2[i] = 0;
  }

  printArray(Serial.list());
  myPort = new Serial(this, Serial.list()[ 1 ], 9600);
  // check the list of the ports,
  // find the port "/dev/cu.usbmodem----" or "/dev/tty.usbmodem----" 
  // and replace PORT_INDEX above with the index of the port
  myPort.clear();
  // Throw out the first reading,
  // in case we started reading in the middle of a string from the sender.
  myString = myPort.readStringUntil( 10 );  // 10 = 'n'  Linefeed in ASCII
  myString = null;
}

void initial() {
  x=false;
  y=false;
  z=false;

  asweetplace.loop();
  violin.loop();
  symphony.loop();
}

void draw() {

  background(255);
  noStroke();

  updateLeapMotion();


  int area = 0;
  
  if (fingerMiddleX < width/3) {
    x=true;
    y=false;
    z=false;
    area = 0;
  } else if (fingerMiddleX >= width/3 && fingerMiddleX <= width*2/3) {
    y=true;
    x=false;
    z=false;
    area = 1;
  } else if (fingerMiddleX > width*2/3) {
    z=true;
    x=false;
    y=false;
    area = 2;
  } 

  if (x) {
    asweetplace.amp(map(fingerMiddleY, 0, height, 1.0, 0.0));
  } else {
    asweetplace.amp(0);
  }
  if (y) {
    violin.amp(map(fingerMiddleY, 0, height, 1.0, 0.0));
  } else {
    violin.amp(0);
  }
  if (z) {
    symphony.amp(map(fingerMiddleY, 0, height, 1.0, 0.0));
  } else {
    symphony.amp(0);
  }
  
  
  if (area == 0 && prevArea != area) {
    println("yes");
    rms.input(asweetplace);
  } 
  if (area == 1 && prevArea != area) {
    println("yes1");
    rms.input(violin);
  }
  if (area == 2 && prevArea != area) {
    println("yes2");
    rms.input(symphony);
  }
 prevArea = area;
 
 volume = rms.analyze() * 500;


  ampTarget = map(volume, 0, 500, 0, 255);
  amp = int(lerp(amp, ampTarget, 0.2)); //smooth
  ampOffset += amp;

  int red = int(map(sin(ampOffset*0.05), -1, 1, 0, 255));
  int green = int(map(sin(ampOffset*0.03), -1, 1, 0, 255));
  int blue = int(map(sin(ampOffset*0.01), -1, 1, 0, 255));

  values[0] = red;
  values[1] = green;
  values[2] = blue;

  //printArray(values);

  sendSerialData();
  
  for (int i = 0; i < x1.length; i++) {
    float easing = map(i, 0, 50, 0.01, 0.3);
    float opacity = map(i, 0, 50, 0, 255);
    float targetX1 = fingerIndexX;
    float dx1 = targetX1 - x1[i];
    x1[i] += dx1 * easing;
    float targetX2 = fingerRingX;
    float dx2 = targetX2 - x2[i];
    x2[i] += dx2 * easing;

    float targetY1 = fingerIndexY;
    float dy1 = targetY1 - y1[i];
    y1[i] += dy1 * easing;
    float targetY2 = fingerRingY;
    float dy2 = targetY2 - y2[i];
    y2[i] += dy2 * easing;

    fill(random(255), random(255), random(255), opacity);
    ellipse(x1[i], y1[i], 70, 70);
    ellipse(x2[i], y2[i], 70, 70);
  }
}

void sendSerialData() {
  String data = "";
  for (int i=0; i<values.length; i++) {
    data += values[i];
    //if i is less than the index number of the last element in the values array
    if (i < values.length-1) {
      data += ","; // add splitter character "," between each values element
    } 
    //if it is the last element in the values array
    else {
      data += "n"; // add the end of data character "n"
    }
  }
  //write to Arduino
  myPort.write(data);
}


void echoSerialData(int frequency) {
  //write character 'e' at the given frequency
  //to request Arduino to send back the values array
  if (frameCount % frequency == 0) myPort.write('e');

  String incomingBytes = "";
  while (myPort.available() > 0) {
    //add on all the characters received from the Arduino to the incomingBytes string
    incomingBytes += char(myPort.read());
  }
  //print what Arduino sent back to Processing
  //print( incomingBytes );
}

Final Project (Sean)

For my final project i wanted to create a game similar to Pac-Man. I drew my inspiration from this game and put my own twist on it. I replaced the Pac-Man with a hongbao, the ghosts with Mao Zedong, and the coins that are eaten by 100 RMB bills. I changed it from a course like setting to an open plane arena so that you had complete mobility of your character. the RMB bills also spawned constantly and were randomized across the screen. The game was started by an Arduino button that I built into a box that was decorated to look like a RMB covered treasure chest. There was a slot that you could press a 1 RMB coin into and it would start the game. I had originally planned to do a pressure sensor but as you pointed out, there was no need to make it any more difficult as no one would see the inner workings of what I had built. This chest would then hold all of the coins that had been entered into it until someone reached the 1,000,000 point mark at which point they would be instructed to take the chest contents. Additionally, after this stage they were also able to continue playing the game by pressing the “C” key on the keyboard to set a high score if they wanted to. Also, After death the player is instructed to insert another coin to continue playing. They player gets 3 lives and loses one every time the hongbao touches a Mao character, they are awarded 100 points for every 10 RMB bill they collect. For the video example I will attach I changed the settings to be able to show more of the functions within the game. but from the code you will be able to see the original settings of the game. I will also attach my PPT as I was not able to fully go through it because of the reduced timeframe of the class. This was a very fun final project to make, and I am happy to now have 2 games I built that I am able to play if I wanted to. I am quite proud of how much I learned this semester, and believe that this game shows off much of what was

taught in the class.

File 1

import processing.serial.*;

Serial myPort;  
int val;      

PImage bag;
PImage enemy;
PImage money;
boolean start;
boolean gameOver;
int score;
Bag player;
ArrayList<MovingObject> enemies = new ArrayList<MovingObject>();
ArrayList<MovingObject> moneys = new ArrayList<MovingObject>();
int life;
boolean pause;
boolean pauseOnce;
int limit = 1000000;

void setup() {
  size(1080, 720);
  background(0);
  bag = loadImage("bag.png");
  money = loadImage("money.png");
  enemy = loadImage("enemy.png");
  String portName = Serial.list()[1];
  myPort = new Serial(this, portName, 9600);
  printArray(Serial.list());
  textAlign(CENTER, CENTER);
  imageMode(CENTER);
  init();
}

void init() {
  player = new Bag();
  score = 0;
  gameOver = false;
  start = false;
  val = 0;
  pause = false;
  pauseOnce = true;
  for (int i = 0; i < 100; i++) {
    moneys.add(new MovingObject(money));
  }
  for (int i = 0; i < 4; i++) {
    enemies.add(new MovingObject(enemy));
  }
  life = 3;
}

void draw() {
  background(0);
  if ( myPort.available() > 0) {  
    val = myPort.read();        
  }
  if (!start) {
    welcome();
    if (val == 49)
      start = true;
  } else
    game();
}

void welcome() {
  textSize(height/10);
  fill(255);
  text("CAPITALIST CRACKDOWN", width/2, height/4);
  textSize(height/20);
  text("REACH 1,000,000 POINTS TO WIN CHEST CONTENTS", width/2, height/2);
  textSize(height/20);
  fill(150);
  text("INSERT COIN TO START.", width/2, 2*height/3);
}

void game() {
  if (!gameOver) {
    if (!pause) {
      fill(255);
      textSize(height/20);
      text("SCORE: "+score, height+(width-height)/1.8, height/3);
      text("LIFE: "+life, height+(width-height)/1.8, 2*height/3);
      player.display();
      player.update();

      if (random(1)<0.1) {
        moneys.add(new MovingObject(money));
      }
      
      if (random(1)<0.005) {
        enemies.add(new MovingObject(enemy));
      }

      for (int i = enemies.size() - 1; i >= 0; i--) {
        MovingObject e = enemies.get(i);
        e.display();
        e.update();
        if (e.isHitted(player.loc, player.size)) {
          life --;
        }
        if (e.dead)
          enemies.remove(i);
      }

      for (int i = moneys.size() - 1; i >= 0; i--) {
        MovingObject m = moneys.get(i);
        m.display();
        m.update();
        if (m.isHitted(player.loc, player.size)) {
          score += 100;
        }
        if (m.dead)
          moneys.remove(i);
      }
      if (score >= limit && pauseOnce){
        pause = true;
      }
      if (life == 0)
        gameOver = true;
    } else {
      fill(255);
      textSize(height/8);
      fill(255);
      text("YOU WIN", width/2, height/3);
      textSize(height/20);
      fill(150);
      text("PRESS 'C' TO CONTINUE.", width/2, 2*height/3);
      if (keyPressed && key == 'c'){
        pause = false;
        pauseOnce = false;
      }
    }
  } else {
    fill(255);
    textSize(height/8);
    fill(255);
    text("SCORE: "+score, width/2, height/3);
    textSize(height/20);
    fill(150);
    text("INSERT COIN TO START.", width/2, 2*height/3);
    if (val == 49) {
      for (int i = enemies.size() - 1; i >= 0; i--) {
        enemies.remove(i);
      }
      for (int i = moneys.size() - 1; i >= 0; i--) {
        moneys.remove(i);
      }
      init();
    }
  }
}

File2

class Bag{
  PVector loc;
  int size;
  PVector velocity;
  float speed;
  PVector targetPlace;
  Bag(){
    loc = new PVector(height/2,height/2);
    size = width/10;
    velocity = new PVector(0,0);
    speed = 3;
    targetPlace = new PVector(width/2,height/2);
  }
  
  void display(){
    float angle = 0;
    angle = velocity.heading();
    pushMatrix();
    translate(this.loc.x, this.loc.y);
    if (this.velocity.x != 0 && this.velocity.y != 0)
      rotate(PI / 2 + angle);
    image(bag, 0, 0,size,size);
    popMatrix();
  }
  
  void update(){
    loc.add(velocity);
    
    if(keyPressed){
      if(keyCode == UP){
        velocity = new PVector(0,-3);
      }else if(keyCode == DOWN){
        velocity = new PVector(0,3);
      }else if(keyCode == LEFT){
        velocity = new PVector(-3,0);
      }else if(keyCode == RIGHT){
        velocity = new PVector(3,0);
      }
    }else{
      velocity = new PVector(0,0);
    }
    loc.x = constrain(loc.x,0,height);
    loc.y = constrain(loc.y,0,height);
  }
}


File3

class MovingObject{
  PVector loc;
  PVector dir;
  float speed;
  PImage look;
  boolean dead;
  float size;
  
  MovingObject(PImage t){
    look = t;
    loc = new PVector(random(height),random(height));
    float a = random(TWO_PI);
    dir = new PVector(cos(a),sin(a));
    speed = random(4,6);
    size = height/10;
    dead = false;
  }
  
  void display(){
    float h = look.height/float(look.width);
    image(look, loc.x, loc.y,size,h*size);
  }
  
  void update(){
    if (loc.x < -size / 2 || loc.x > height + size / 2)
      dir.x *= -1;
    if(loc.y < -size / 2 || loc.y > height + size / 2)
      dir.y *= -1;
    PVector vel = new PVector(speed*dir.x,speed*dir.y);
    loc.add(vel);
  }
  
  boolean isHitted(PVector target,float dia){
    if(dist(target.x,target.y,loc.x,loc.y)<size / 2 + dia/2){
      dead = true;
      return true;
    }else{
      return false;
    }
  }

}



Arduino Part:

bool pressed = false;
bool send0 = true;
bool send1 = true;
void setup() {

  Serial.begin(9600);
  pinMode(8,INPUT_PULLUP);
}

void loop() {
  
  if(digitalRead(8) == HIGH)
    pressed = false;
  else
    pressed = true;
    
  if(pressed){
    if(send1){
      Serial.print(1);
      send1 = false;
    }
    send0 = true;
  }
  else{
    if(send0){
      Serial.print(0);
      send0 = false;
    }
    send1 = true;
  }
}

Final Project Documentation (Marcela)

By Sabina Olsson
Partner: Katniss Kang

Project Name: “Mapping IMA”

For our final project, we decided to build an interactive map that explores the 8th floor IMA section of the NYU Shanghai AB. We built a physical map, to which we connected three pressure sensors, an Rotary Angle sensor (with a potentiometer) and Arduino. We then connected the Arduino to Processing using serial communication, and displayed values from Arduino in Processing. The concept of the project was that three pressure sensors were located on the map, in different rooms of the “8th floor” (drawn on our map), and when the rotary angle sensor (that acted as a “doll” to move around the map) was put on any of the sensors, that one triggered a reaction from Arduino, which sent values to Processing. In Processing then, a panoramic 360 degree photo of the room of which the pressure sensor was located in, appears. Using then the potentiometer on the rotary angle sensor, when turning it, the image in Processing would also turn, making the user “look” around that room of which the doll was located in. We also added elements of smaller photos or videos popping up on the screen in Processing, as the potentiometer changed angle, making the experience more interactive and interesting, as they got close-ups of many different things.

Video of our project!

So, basically the process of using our project is:
1) The user places the doll/rotary angle sensor on any of the three specified points on the map.
2) The pressure sensor on that spot is triggered and activates Processing on the computer screen, which then displays the specific room the sensor is located on at the map.
3) The user turns the doll (i.e. the potentiometer on the rotary angle sensor), which triggers Processing to move the image on the screen, giving the user a full look of the room, in a broader angle.
4) When the doll is in specific angles, various elements (photos or videos) will be triggered to appear on the screen as well, giving a more detailed account of the room that is being explored.

Google Maps street view: inspiration #1

We had been planning this project for a while, and initially we had a bunch of ideas, which some turned out to be too advanced for us. For instance, we had planned on having more rooms displayed, and more pictures and videos added to each room. But as we soon realized, we had not enough time to complete all of that, especially not when we struggled a lot with figuring out the code, so we had to cut down the ideas a bit. But we started already back in November, when we did the concept presentation in class, as we had found some inspiration for our work.

Materiable: inspiration #2

Basically, our main inspiration came from two different sources. Our first one was the interactive table from MIT, called “Materiable”, which we found interesting mainly because of its physical interactiveness, as it was possible to touch it and see a response immediately. We wanted to do something similar in terms of physical interactivity, as in creating something physical that could be touched like that, and would respond well. However, even though we were both fascinated by this project and kept it in mind, our second source gave us more clear and direct inspiration. Our second main source was Google Street View, the explore and view function of the basic mapping program of Google, where you can look at any street in the world, and move around it, as if you were there in real life. We used this as our main source of inspiration, as we definitely wanted to do something with a map, and especially with the type of view that Street View provides, where you can see a place on your screen without actually being there. However, we also wanted to add some more interactivity to it, as Google’s problem is that the image seen is static and the user is unable to click on it and explore it. We instead wanted to give the user the chance to look around the place and explore different parts of it as if they were there, taking a closer look at some things, instead of just looking at an unchanging photo.

Our initial sketches! 

However, creating this vision, of an interactive map, just like the MIT Materiable, with the interactiveness of Google Map’s street view, turned out to be rather difficult. We had a lot of ideas, but little knowledge on how to actually achieve all of them. But we started with drawing some sketches as a plan, and we researched what type of sensors we wanted (we knew that pressure sensors were the best option, but we happened to find the rotary angle sensor by chance, and decided that it’d be perfect too). We drew up the map quickly on some paper, using pencil, and spent half a day making the little cardboard papers, cutting and taping.


The beginning of the map!

After that, we started working on the code, doing the basics of it, like connecting the circuit with Arduino and all the sensors, and just getting them all to function properly. We had some small issues with our original rotary angle sensor, as the potentiometer didn’t return accurate values, which we needed to be able to set specific angles for Processing to react to. So, we had to switch it for another rotary angle sensor, but that was fine, and the second one worked better. After doing the basic coding, we finished drawing and taping the physical map together, coloring it and establishing where to put the pressure sensors on it. We also went around the 8th floor collecting most of our photographic material, which we took just with the panorama function on the iPhone camera, as well as regular photos of various things. We also recorded a short video during a class with Marcela. Then, we photoshopped all the panoramic photos together, to create a 360 degrees view, as well as we edited in some symbols to mark where we planned on having additional images pop up on the screen (marked with yellow stars). We also had to significantly decrease the size of the photos, as the files were too large for Processing to be able to handle all at once, and the program couldn’t run the code with our photos then.

Examples of some of our panorama photos!Starting with the Arduino circuit!

Finally, as we headed in to the final week of classes, we spent literally half a day figuring out the code, and how to get the values from Arduino to cause a response in Processing similarly to what we wanted. We struggled a lot with various issues such as; rotating a video, having an image pop up multiple times (getting the function to run more than once), getting the main image to stay on screen and not disappear when another image showed up, and just getting a timely reaction that was accurate. Luckily we got some help from our amazing fellows and professors (big shout-out to Jiwon, Jack, Nick, Marcela, and Moon, we couldn’t have gotten everything done without their help!!), and we managed to get the code running. We also took some time to finish up the physical map in terms of decoration and final touches.

The map is almost done!Trying to get our code to work…

Helpful drawing by professor Moon of how to rotate video in Processing

However, we faced even more struggles the last day we were finish up the project, as with the code running, we just had to place the sensors on the map, and make the doll work. Initially we had planned to put the pressure sensors underneath the white paper of the map, so that they’d not be seen by the user. But when we did that, we couldn’t get them to respond to pressure. Their location, on top of cardboard, and beneath some paper, was probably not ideal for them to work, so after much frustrations, we decided to just put the sensors on top of the map instead, which worked better. We also had to switch one of the pressure sensors, as the original one wasn’t working well and suddenly not responding to any pressure when placed on the map. The equipment room only had one other sensors left (a big one), as they were all checked out by that point otherwise, so we had to go for the bigger one. Finally, the map was fully put together, and the pressure sensors were all working fine. However, we had to make the “doll” heavy enough to trigger the pressure sensors, which the rotary angle sensor wasn’t on its own. We struggled a lot with this too, as the smaller pressure sensors required a smaller force specifically directed on to them, and the 1 yuan coins we initially decided to use were too big, so the force wasn’t concentrated enough. However, Katniss solved this perfectly by cutting up some wire, rolling it smaller, and putting it on the bottom of the coin stack, which then could concentrate the force on a smaller area. We ended up using 8 coins (“this project cost us 8 kuai…”), since that showed to be heavy enough, and then we just taped them together, and put the rotary angle sensor and the cardboard doll on top. Then, the project was finally working fine!

About to place the finished map onto the cardboard.

Little piece of wire under the coins to concentrate the force of the coins.Connected the doll to the rotary angle sensor!

Our users would mainly be prospective students/faculty of IMA or NYUSH, who are not familiar with the space but want to take a closer look at it. Also, outside visitors of the school would also fall into the range of users for this, as they have no other previous knowledge of the area presented. The motivation for the users to use it would be their desire to discover and explore a new area, and it’d give them a better look at something that they might be interested in but haven’t been able to visit. It could also be used as an advertisement for IMA by NYUSH, to show off to visitors or prospective students/faculty, which would make it commercially useful too. The motivations for us making it was basically to have fun and design something interesting, but taking a new perspective on something simple that we see everyday, showing it in a different light.

Final version of the doll!

We definitely learnt so much from this project, as it was a bit difficult to complete, and it required tons of hard work in order to get everything fixed. We learnt a lot of new coding, everything from how to rotate images/videos in Processing, to how to work with booleans to get the right pictures to show up at the right time. We also definitely learned some more intense serial communication, as we had to work a lot with Arduino, and we had to retrieve 2 different types of values from Arduino (both from pressure sensors, as well as the angle of the potentiometer on the rotary angle sensor), and get those values to produce a correct response in Processing, which was a bit complicated too. We also learnt how to work with new types of sensors, and how they function, which was interesting. We also learnt how to incorporate multiple images and videos in Processing, and how to control them in terms of when they appear. Finally, we learnt that these types of projects require a lot of energy, planning, and patience, and that it is important to go with the flow, not be too upset if it doesn’t work out as planned. We also learned how the 8th floor really looks like (“wait, is there a door there..? really?”)… and we learned how to successfully build cardboard tables that don’t fall apart too quickly!

Final physical map!

I’d say that we achieved a lot with this project, we managed to correctly connect Arduino and Processing, build circuits, build a physical map, and do all of this together as a group without arguing too much. We definitely achieved our main goal, and we didn’t have to change our idea too drastically, we managed to perform it completely. We also achieved new knowledge in coding and using Arduino/Processing, as well as knowledge on how to structure, plan, and build a major project such as this. For what we didn’t achieve, we had some bigger initial ideas, such as doing a larger map, and doing more code that would allow for further in-depth exploration of the 8th floor. However, we didn’t have the time or the resources to finish that, so we had to put that aside. We also didn’t really achieve the aesthetic component, in terms of making a pretty map, and hiding all the wires and the breadboard/Arduino, which we also had planned on doing. But in terms of failure, I think we failed a little bit in regard to some physical or coding matter, while in full experience and learning matter, we didn’t fail, since we learned so much, got a good insight to project designing, and we got a chance to see how well we could do starting from scratch. Our project worked fine as well, so it was actually pretty successful.

Our code for Arduino is:

#define ROTARY_ANGLE_SENSOR A5
#define ADC_REF 5//reference voltage of ADC is 5v.If the Vcc switch on the seeeduino
//board switches to 3V3, the ADC_REF should be 3.3
#define GROVE_VCC 5//VCC of the grove interface is normally 5v
#define FULL_ANGLE 300//full value of the rotary angle is 300 degrees
int sensorValue;
void setup()
{
Serial.begin(9600);
}

void loop() {
int degrees;
degrees = getDegree();
int RAS = map (degrees, 0, 300, 0, 255); //Rotery Angle Sensor
int valueA = int (map (analogRead(A0), 0, 1000, 0, 255));
int valueB = int (map (analogRead(A2), 0, 1000, 0, 255));
int valueC = int (map (analogRead(A4), 0, 1000, 0, 255));

Serial.print(valueA);
Serial.print(“,”); // put comma between sensor values

Serial.print(valueB);
Serial.print(“,”);

Serial.print(valueC);
Serial.print(“,”);

Serial.print(RAS);
Serial.println();
delay(100);
}
int getDegree()
{
int sensor_value = analogRead(ROTARY_ANGLE_SENSOR);
float voltage;
voltage = (float)sensor_value * ADC_REF / 1023;
float degrees = (voltage * FULL_ANGLE) / GROVE_VCC;
return degrees;
}

Our code for Processing is:

PImage img_M;
PImage img_S;
PImage img_C;
PImage img_L;
PImage img_3DP;
PImage img_Ch;
PImage img_EQ;
PImage img_PF;
PImage img_MA;
PImage img_WB;
PImage img_PJ;
PImage img_Chair;
PImage img_LC;
PImage img_LPKF;
PImage img_WaT;
PImage img_BO;
PImage img_BT;
PImage img_BTh;
Movie myMovie;
SoundFile backgroundMusic;

boolean mainPage = true;
boolean studio = false;
boolean classroom = false;
boolean lab = false;
boolean P1 = false;
boolean P2 = false;
boolean P3 = false;
boolean P4 = false;
boolean P5 = false;
boolean P6 = false;
boolean P7 = false;
boolean P8 = false;
boolean P9 = false;
boolean P10 = false;
int imgXOne;
int imgXTwo;
int imgXThree;
int RAS;
int x;

import processing.serial.*;
import processing.video.*;
import processing.sound.*;

String myString = null;
Serial myPort;

int NUM_OF_VALUES = 4; /** YOU MUST CHANGE THIS ACCORDING TO YOUR PROJECT **/
int[] sensorValues;

void setup() {

fullScreen();

img_M = loadImage(“nyu_sh.png”);
img_L = loadImage(“lab_final.jpg”);
img_C = loadImage(“classroom_final.jpg”);
img_S = loadImage(“Studio_final.jpg”);
img_BO = loadImage(“blackOne.jpg”);
img_BT = loadImage(“blackTwo.jpg”);
img_BTh = loadImage(“blackThree.jpg”);
img_3DP = loadImage(“3DP.jpg”);
img_Ch = loadImage(“Ch.jpg”);
img_EQ = loadImage(“equip.jpg”);
img_PF = loadImage(“fellows.jpg”);
img_MA = loadImage(“mac.jpg”);
img_WB = loadImage(“wb.jpg”);
img_PJ = loadImage(“pj.jpg”);
img_Chair = loadImage(“chairs.jpg”);
img_LC = loadImage(“LC.jpg”);
img_LPKF = loadImage(“LPKF.jpg”);
img_WaT = loadImage(“WaT.jpg”);
background(0);
setupSerial();
myMovie = new Movie(this, “MCL.mp4”);
myMovie.loop();
backgroundMusic = new SoundFile(this, “BGM.mp3”);
}

void draw() {
if (backgroundMusic.isPlaying() == 0) {
backgroundMusic.play();
}
updateSerial();
//printArray(sensorValues);

background(0);

if (sensorValues[0] >= 10 && sensorValues[1] < 10 && sensorValues[2] < 10) {
studio = true;
classroom = false;
lab = false;
mainPage = false;
RAS = int(map(sensorValues[3], 0, 255, 0, 300));
} else if (sensorValues[1] >= 10 && sensorValues[0] < 10 && sensorValues[2] < 10) {
classroom = false;
studio = false;
lab = true;
mainPage = false;
RAS = int(map(sensorValues[3], 0, 255, 0, 300));
} else if (sensorValues[2] >= 10 && sensorValues[1] < 10 && sensorValues[0] < 10) {
classroom = true;
studio = false;
lab = false;
mainPage = false;
RAS = int(map(sensorValues[3], 0, 255, 0, 300));
} else if (sensorValues[0] < 10 && sensorValues[1] < 10 && sensorValues[2] < 10) {
classroom = false;
studio = false;
lab = false;
mainPage = true;
}

if (mainPage == true) {
P1 = false;
P2 = false;
P3 = false;
P4 = false;
P5 = false;
P6 = false;
P7 = false;
P8 = false;
P9 = false;
P10 = false;
imageMode(CORNERS);
image(img_M, width/3, 0, width*2/3, 600);
image(img_BTh, 0, 600, width, height);
image(img_BO, 0, 0, width/3, 600);
image(img_BT, width, 0, width*2/3, 600);
}
if ( studio == true) {
imageMode(CORNERS);
imgXOne = int (map(RAS, 0, 300, 0, 1800));
image(img_S, imgXOne-1800+width/3, 0, 1800, height/2);

imageMode(CORNERS);
image(img_BTh, 0, height/2, width, height);
image(img_BO, 0, 0, width/3, height/2);
image(img_BT, width, 0, width*2/3, height/2);
//check the knob location
if (RAS < 300 && RAS > 250) { // angle 250 ~ 300: stage 0
P1 = true;
//show image: left image
pushMatrix();
translate(width/6, height/4);
rotate(PI + PI/2);
imageMode(CORNER);
image(img_WaT, 0 – width/6, 0 – height/4, width/3, height/2);
popMatrix();
} else if (RAS > 140 && RAS < 200) { //angle 140 ~ 200: stage 1
P2 = true;
//show image: right
pushMatrix();
translate(width*5/6, height/4);
rotate(PI + PI/2);
imageMode(CENTER);
image(img_EQ, 0, 0, width/3, height/2);
popMatrix();
} else if (RAS > 20 && RAS < 100) { //angle 20 ~ 100: stage 2
P3 = true;
//show image: bottom
//showImage();
image(img_PF, x, height/2);
}
} else {
P1 = false;
P2 = false;
P3 = false;
}
if (classroom == true) {

imgXTwo = int (map(RAS, 0, 300, 0, 1800));
image(img_BTh, 0, height*2/3, width, height/2);
image(img_C, imgXTwo-1800+width/3, 0, 1800, height/2);
imageMode(CORNERS);
image(img_BO, 0, 0, width/3, height/2);
image(img_BT, width, 0, width*2/3, height/2);
//check the knob location
if (RAS < 300 && RAS > 250) { // angle 250 ~ 300: stage 0
P4 = true;
//show image: left image
imageMode(CORNER);
image(img_Chair, 0, 0);
} else if (RAS > 140 && RAS < 200) { //angle 140 ~ 200: stage 1
P5 = true;
//show image: right
imageMode(CORNERS);
image(img_WB, width*2/3, 0, width, height/2);
} else if (RAS > 20 && RAS < 100) { //angle 20 ~ 100: stage 2
println(“p6”);
P6 = true;
image(img_PJ, 0, height/2);
pushMatrix();
translate(width/2 + myMovie.width, height/2 + myMovie.height);
rotate(PI);
image(myMovie, 0, 0);
popMatrix();
} else {
println(“none”);
P4 = false;
P5 = false;
P6 = false;
}
}

if (lab == true) {

imgXThree = int (map(RAS, 0, 300, 0, 1800));
image(img_BTh, 0, height*2/3, width, height/2);
image(img_L, imgXThree-1800+width/3, 0, 1800, height/2);
imageMode(CORNERS);
image(img_BO, 0, 0, width/3, height/2);
image(img_BT, width, 0, width*2/3, height/2);
if (RAS < 300 && RAS > 260) { // angle 250 ~ 300: stage 0
P7 = true;
//show image: left image
image(img_3DP, 0, 0, width/3, height/2);
} else if (RAS > 200 && RAS < 250) { //angle 140 ~ 200: stage 1
P8 = true;
//show image: right

image(img_LC, width*2/3, 0);
} else if (RAS > 120 && RAS < 180) { //angle 140 ~ 200: stage 1
P9 = true;
//show image: right
image(img_LPKF, 0, 0);
} else if (RAS > 20 && RAS < 80) { //angle 20 ~ 80: stage 2
P10 = true;
//show image: bottom
//showImage();
image(img_Ch, width/2, height/2);
}
} else {
P7 = false;
P8 = false;
P9 = false;
P10 = false;
}
}

void movieEvent(Movie m) {
m.read();
m.volume(0);
}
void setupSerial() {
myPort = new Serial(this, Serial.list()[0], 9600);
myPort.clear();
myString = myPort.readStringUntil(10); // 10 = ‘\n’ Linefeed in ASCII
myString = null;

sensorValues = new int[NUM_OF_VALUES];
}
void updateSerial() {
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) {
for (int i=0; i<serialInArray.length; i++) {
sensorValues[i] = int(serialInArray[i]);
}
}
}
println(“Rotary: “, sensorValues[3], “FSR3:”, sensorValues[2],
“FSR2:”, sensorValues[1], “FSR1:”, sensorValues[0]
);
}
}

//void showImage(PImage pic) {
// int y = height/2;
// image(pic, x, y);
// x += xSpeed;
// if(x > rb || x < lb) xSpeed *= -1;
//}

My favorite things of the project are the cute little doll, the colors of the map and the little tables (just the fact that we took the time to handcraft all of those little tables!!), and the soothing background music that really sets the comfortable mood. I also really liked that we managed to figure out the function of getting the doll (potentiometer) to turn and having the image turn too, so the viewer can look around the room, I think that’s really cool. I generally think that it’s the fact that we put down so much time into details, and that we did put in a lot of effort into our project, that made it so nice. It might not be perfect, but it worked well, and I’m really proud of us for managing it.

Obviously, our project isn’t perfect, and there are some stuff to change. Firstly, we could have done the physical map more aesthetically nicer, by covering the wires and the circuits, and maybe placing the pressure sensors underneath the paper (and still get them to work), and maybe add some more details to the map, like people, chairs, or machinery. This we could do by maybe making the pressure sensors more sensitive to pressure, or get some more specialized sensors. For the wires and stuff, we could have added boxes to keep them in, or just carefully planned the design of the map to hide the wires and circuits underneath it. For the added physical details, we could have just built more stuff, or maybe thrown away our little homemade cardboard tables and just ordered small furniture online and placed there (or 3D-printed it?).

We could also have made the “doll” more stable, and not used coins to make it heavy, since it was a bit unstable (because of the tape) and could easy have fallen apart if someone was a bit rough with it. This we could have maybe gotten some smaller metal pieces or rocks, that would be more heavy, and shaped to fit the pressure sensors. We could also have covered the rotary angle sensor part and its wires more, which would have made it look much nicer. For the on-screen elements, we could have made the Processing look nicer too, as design the layout to look better, and have the main panorama picture a little bit bigger. We could also have added more elements to be discovered and explored in the room, as we all made the timing of those elements to pop up more accurate. All of these improvements would be in Processing, and would mainly need us to just sit down and explore, learn, and work with Processing much more.

Screenshot of the code.

But overall, I’m very happy with our project, and even if it’s not perfect, we still put in a lot of effort into it, and I’m proud of our work. The map and its code works fine, and gives a result that we expected and hoped for, and I feel that we did a pretty good job, considering the creativity of our idea and all of the energy and dedication we put into it!

IMA Final Projects–LOVE Katie Yan(Marcela)

Date: 12/14

Instructor: Marcela

Group Member: Shir Zhang/ Katie Yan

Aim:

Inspiring Project:

 

Materials:

Idea and Goals:

Process:

 

Credit:

Reference:

Presentation:

Improvement and Reflection:

[Moon] Final Project: Pixel Portal (Alexandru)

Title: Final Project

Date: December 14, 2017

Instructor: Jung Hyun Moon

Project

  • Pixel Portal

Conclusion, Results, Difficulties and Achievements

My final Interaction Lab project is an updated, polished version of my Pixel Portal game. In creating this final version, I had set a number of goals which I believe I eventually achieved successfully.

  1. Quality UI. In order to make Pixel Portal much more of a standalone game than a proof of concept, I created a standard, intuitive menu which enables navigation between the various game modes. I also added help screens which familiarize the user with the controls, and made sure that there is no redundant information.
  2. Beautiful UX. In order to make sure the UI navigation is successful, I found a fitting font, colors that preserve the game’s atmosphere, but also create good contrast, and also added a theme song. These elements, together with my attention to detail, should make for an intuitive, pleasant experience.
  3. Physical controller. Figuring out the controls was a struggle for most people in the initial version of the game, so designing the real “portal gun” was a huge challenge. On the one hand, I wanted to make sure that playing the game is as intuitive as possible. On the other hand, I was also hoping to make the controller interesting and unique. Based on people’s feedback, I decided to create a “portal gun” which enables the user to aim based on a natural tilt motion, and move the character with the very familiar joystick (with the catch that this joystick is placed on the gun itself). After a lot of 3D modeling, I eventually gave up in front of the numerous technical difficulties and improvised a perhaps less pretty, but just as functional “gun” out of a box of Pringles. Inside there is an Arduino with an accelerometer, as well as the wires coming from the joystick.
  4. Story mode. Another major feedback point was the feeling of “lack of purpose” in the game. In order to keep the concept simple, while also addressing this concern, I decided to introduce a goal that can be reached by finishing the level, thus giving the player the motivation to solve the maze, as well as “traps” that need to be avoided in order not to lose and have to start over. Together with the mysterious soundtrack and the storyline introduction, I believe this adds a clear narrative to the game without distracting from its main concept.
  5. Level Builder. One of the most fun aspects of this game, in my view, is simply exploring and attempting to find a way through the difficult (though random) scenarios created. In order to make it even more interesting, players can also build their own maps, so that they can challenge themselves (or others) to solve any kind of complex, strange, or even impossible mazes.

In the end, while the game may still have some minor glitches, and although there is still room to take the idea further, I believe that I did manage to bring it to a point where it makes sense entirely on its own, and can constitute a fun, enjoyable experience.

Final Project Documentation | Boyan Xu (Sean’s Section)

1.Idea’s Origin:

When I first reflected on what I should do with my final project, I was accidentally listening to a lot of classical music. Maybe I just recalled a game called “Wii Music” in my sub-consciousness, I just come up with an idea of making a project that can give you a chance to become a conductor, which means you can control the playing speed of an orchestra.

My original idea was using the accelerometer to sense the waving of your hand and use three ultrasonic sensors to make it possible to control the three parts of a single song.

2.Background Research

My initial idea for my project is creating an interactive system that can give people a chance to become a conductor. At that time I didn’t realize how huge the task was to not only achieve the basic requirement but also make it a complete and reasonable project. And when I write this documentation, recalling how many difficulties I have solved, I suddenly realize how long the distance I have gone.

At the beginning of my project, I did a wide investigation of the relevant hardware(Arduino) and software(processing) information that I may be going to use, and how do conductors conduct. Here are parts of the URL of my primary investigation reading materials:”

 

3.How to read the waving speed

First I tried Genuino 101( another kind of chip), which is loaded an accelerometer, trying to use the violent change of the acceleration as the signal of counting. But later I found that the wire will largely destroy the experience of conducting. Later I tried to use Bluetooth to solve this problem, but another problem is that the electric source, namely the battery will still cause the same problem.

Finally, I decided to Leap Motion to capture the waving signal.

How to get the statistic that I need is another interesting challenge for me. I have to design an algorithm that can finally provide me with the speed of your waving or the equivalent. At first I fell into a meditation on how to use the derivatives of the change in position of your finger to calculate the speed, but later I realized that I do not need that accurate velocity, so I convert the requirement of velocity into the requirement of counting how many times your finger across three blocks in every 3 seconds.

After a further thought on this, I found that this problem can be changed into a calculus problem!!!!!! What I want to get is the temporary  speed of the index finger’s position, which will be captured be leap motion and then converted into two position values: (x,y)

When I add more blocks rather than only three until the number of blocks approaches to infinite, I can finally get a pretty precise velocity value!!!!!!! But since my project’s requirement for accuracy isn’t so strict, I can use mere 3 blocks to achieve the basic function. The core counting part is in below.

 

// ====================================================

if(fingerIndexX> 400 && fingerIndexX<1200 && fingerIndexY>0 && fingerIndexY< 500){
x1=255;

if(alreadyExecuted1 == false) {
x = x+1;
alreadyExecuted1 = true;

}
if(alreadyExecuted1 == true){
x = x;
}
alreadyExecuted2 = false;
alreadyExecuted3 = false;

}
else{x1 = 155;}

if(fingerIndexX> 0 && fingerIndexX<800 && fingerIndexY> 500 && fingerIndexY< 1000 ){
x2=255;
if(alreadyExecuted2 == false) {
x = x+1;
alreadyExecuted2 = true;
}
if(alreadyExecuted2 == true){
x = x;
}

alreadyExecuted1 = false;
alreadyExecuted3 = false;
alreadyExecuted4 = false;
alreadyExecuted5 = false;

}
else{x2=155;}

if(fingerIndexX> 800 && fingerIndexX<800+800 && fingerIndexY>500 && fingerIndexY< 1000){
x3=255;
if(alreadyExecuted3 == false) {
x = x+1;
alreadyExecuted3 = true;
}
if(alreadyExecuted3 == true){
x = x;
}
alreadyExecuted1 = false;
alreadyExecuted2 = false;
alreadyExecuted4 = false;
alreadyExecuted5 = false;
}
else{x3=155;}
// ====================================================

 

4. Feedback on conducting

Another problem I Met was the lack of feedback on your conducting.

First I tried to add a simple ellipse which can later trace your index finger’s position. But that design is lack of aesthetic value. So I get my inspiration from a sample code inserted in processing.js. I created a class called particle and let hundreds of particles give you the feedback.

That was proved pretty cool!  🙂

 

5. Multimedia’s effect on creating atmosphere

As you can see in the upper picture, there was no background at first. The conducting interface was so bare that can’s effectively hint that you are conduting an orchestra.

So I use this pic as the interface’s background.

Again, that was proved pretty cool!  🙂

6. Make the experience a “complete process”

My project only contained a conducting interface before. That’s not complete, for there was no necessary instruction as an outset or assessment for your conducting as an endpoint.

/* instructions pic

 

*/

/*assesment pic

*/

To stop the conducting sketch component when the instruction surface is on your screen I used “if” and “boolen” to ahcieve that.

// ==================================================

if(alreadyChosen == false){
soundfiles[0].rate(0);
soundfiles[1].rate(0);
soundfiles[2].rate(0);

if(instructionDone == false){

……

// ==================================================

To make the instruction surface last necessary time, I created a function as below:

int sec = second();
if(sec % 3 == 0 && visites != sec){
r = r+1;
visites = sec;
}

 

7.  Combining Arduino

The Leap Motion alone can already realize all my idea for the project. But thanks to professor Sean’s reminding, I notice that we are required to build an interacting system using processing AND arduino.

My first idea was using three infrared distance sensors to control the volume of the song, but when I finished my sketch and started to test, I found the statistic provided by IR distance sensor contains a looooooooooooooot of noise value which can make the volume of song totally a mass.

So I gave up the arduino sketch I already finished and trying to apply joy-sticker to my project.

 

8.Song Choosing System

The result of applying joy-sticker is that I came up with a new idea of creating a song choosing system!

That was later proved to make the project more interactive.

9. Appearance Designing
In this part, I mainly focused on two things:
1). Hide the Arduino, breadboard, and wires to avoid hinting experiencer this is mocking process.
2). Use two styrofoam block pedestals to hint the experiencer that there are only two components.
10. Tester feedback
I invited two of friends, in the condition of knowing nothing about my project, to test my project, in order to find whether the necessary information was given in time.
And I received some feedbacks and later I made some tiny change in my project.

 

import processing.serial.*;
import processing.sound.*;
import de.voidplus.leapmotion.*;
int totalParticles = 100;
float targetX = 0.0;
float targetY = 0.0;
float rms_scaled;
PImage img;
PImage img1;
PImage img2;
PImage back1;
PImage back2;
PImage ins1;
PImage res1;
PImage res2;
PImage res3;



PFont f1;
PFont f2;
PFont f3;
PFont f4;
PFont f5;

// ======================================================
String myString = null;
processing.serial.Serial myPort;

int NUM_OF_VALUE = 3;
int[] sensorValues;
// ======================================================




SoundFile[] soundfiles = new SoundFile[3];
int i = 0;


Amplitude rms;
float smooth_factor =0.35;
float sum;
float scale=5;




LeapMotion leap;
Particle[] particles = new Particle[totalParticles];
float fingerIndexX ;
float fingerIndexY ;
int x = 0;
int y = 0;
int z = 0;
int q = 0;
int r = 0;
int s1 =0;
int s2 =0;
int s3 =0;
int sm =0;

int Jx =0;
int Jy =0;

int posx =800;
int posy =600;

int Oldx = 0;
float rate = 4;
float realRate = 4;
int diff = 4;
int x1 = 155;
int x2 = 155;
int x3 = 155;
boolean alreadyExecuted1 = false;
boolean alreadyExecuted2 = false;
boolean alreadyExecuted3 = false;
boolean alreadyExecuted4 = false;
boolean alreadyExecuted5 = false;
long visited = 0;
long visites = 0;

boolean alreadyChosen = false;
boolean instructionDone= false;


int freq1;
int freq2;
int freq3;
int compare;


void setup() {
  size(1600, 1200);  
  background(0); 
  img = loadImage("pic1.png");
  img1 = loadImage("1.jpg");
  img2 = loadImage("2.png");
  back1 = loadImage("back1.png");
  back2 = loadImage("back2.png");
  ins1 = loadImage("ins1.png");
  res1 = loadImage("res1.png");
  res2 = loadImage("res2.png");
  res3 = loadImage("res3.png");

   // ==================================================
  f1 = createFont("Arial",32,true);
  f2 = createFont("Arial",32,true);
  f3 = createFont("Arial",32,true);
  f4 = createFont("Arial",32,true);
   
   // ==================================================
 for(int i = 0; i < totalParticles ; i++){
   Particle particle = new Particle();
   particles[i] = particle;
 }
    
   // ==================================================
    
  
  
  
  soundfiles[0] = new SoundFile(this, "6.mp3");
  soundfiles[1] = new SoundFile(this, "2.mp3");
  soundfiles[2] = new SoundFile(this, "4.mp3");
  
  
  soundfiles[0].loop();
  soundfiles[1].loop();
  soundfiles[2].loop();
  

  

  leap = new LeapMotion(this);
  
  // ================================================== 
  setupSerial();
}




void draw() {

 // ==================================================
  updateSerial();
  printArray(sensorValues);
 // ==================================================
  
  
    if(alreadyChosen == false){
       soundfiles[0].rate(0);
       soundfiles[1].rate(0);
       soundfiles[2].rate(0);
      
      if(instructionDone == false){
       
       int sec = second();
       if(sec % 3 == 0 && visites != sec){
        r = r+1;
        visites = sec;
        }
       
         
       if(r <= 6){
       image(back1,0,0);}
       
       else if(r <=14){
       image(ins1,0,0);}
       
       else if( r<= 16){
       image(back2,0,0);}
       
       else{instructionDone = true;}
       
        
      } else{
      
      background(0);
       soundfiles[0].rate(0);
       soundfiles[1].rate(0);
       soundfiles[2].rate(0);
       
       image(img1,900,0);
       pushMatrix();
       scale(2);
       image(img2,500,300);
       popMatrix();
       
       
       pushMatrix();
       fill(#F08E4D);
       translate(200,100);
       rect(0,0,600,250);
       rect(0,400,600,250);
       rect(0,800,600,250);
       popMatrix();
       

       textFont(f1,64);
       fill(0);
       text("1.La Java Va Bien",250,225);
     
       
       textFont(f2,64);
       fill(0);
       text("2.The Blue River",250,625);     
       
       textFont(f3,64);
       fill(0);
       text("3.Strauss Waltz",250,1025);  
      

       fill(#F08E4D);
       strokeWeight(5);
       ellipse(posx,posy,200,200);
       
       textFont(f4,25);
       fill(255);
       text("Press to Choose",posx-90,posy);  
      
      if(sensorValues[0]<441){
        Jx = 10;
      } else if(sensorValues[0]< 682){
        Jx = 0;
      } else if(sensorValues[0]<923){
        Jx = -10;
      }
        
      if(sensorValues[1]<441){
        Jy = -10;
      } else if(sensorValues[1]< 682){
        Jy = 0;
      } else if(sensorValues[1]<923){
        Jy = 10;        
      }
      

      posx = posx - Jx;
      posy = posy - Jy;
 

 if(posx >= 200 && posx <= 800 && posy >= 100 && posy <= 350 && sensorValues[2] == 0){
   i = 0;
   alreadyChosen = true;
   
 } 
 if(posx >= 200 && posx <= 800 && posy >= 500 && posy <= 750 && sensorValues[2] == 0){
   i = 1;
   alreadyChosen = true;
 } 
 if(posx >= 200 && posx <= 800 && posy >= 900 && posy <= 1125 && sensorValues[2] == 0){
   i = 2;
   alreadyChosen = true;
 }     
      } 
       
    }
    
    
    else{

  
noStroke();
smooth();
background(0);

image(img,0,0);

fill(#CE723C,x1);
rect(400,0,800,500);
fill(#AF5C2C,x2);
rect(0,500,800,500);
fill(#83401C,x3);
rect(800,500,800,500);
   // ==================================================
for (int i =0; i < totalParticles ; i++){
  particles[i].update();
  particles[i].render();
// Type different value into the parameter + make some change to the function render!
}

   // ==================================================

  // ...

  int fps = leap.getFrameRate();
  for (Hand hand : leap.getHands ()) {


    // ==================================================
    // 2. Hand

    int     handId             = hand.getId();
    PVector handPosition       = hand.getPosition();
    PVector handStabilized     = hand.getStabilizedPosition();
    PVector handDirection      = hand.getDirection();
    PVector handDynamics       = hand.getDynamics();
    float   handRoll           = hand.getRoll();
    float   handPitch          = hand.getPitch();
    float   handYaw            = hand.getYaw();
    boolean handIsLeft         = hand.isLeft();
    boolean handIsRight        = hand.isRight();
    float   handGrab           = hand.getGrabStrength();
    float   handPinch          = hand.getPinchStrength();
    float   handTime           = hand.getTimeVisible();
    PVector spherePosition     = hand.getSpherePosition();
    float   sphereRadius       = hand.getSphereRadius();

    // --------------------------------------------------
    // Drawing
   

    // ==================================================
    // 3. Arm

    if (hand.hasArm()) {
      Arm     arm              = hand.getArm();
      float   armWidth         = arm.getWidth();
      PVector armWristPos      = arm.getWristPosition();
      PVector armElbowPos      = arm.getElbowPosition();
    }


    // ==================================================
    // 4. Finger

    Finger  fingerThumb        = hand.getThumb();
    // or                        hand.getFinger("thumb");
    // or                        hand.getFinger(0);

    Finger  fingerIndex        = hand.getIndexFinger();
    // or                        hand.getFinger("index");
    // or                        hand.getFinger(1);

    Finger  fingerMiddle       = hand.getMiddleFinger();
    // or                        hand.getFinger("middle");
    // or                        hand.getFinger(2);

    Finger  fingerRing         = hand.getRingFinger();
    // or                        hand.getFinger("ring");
    // or                        hand.getFinger(3);

    Finger  fingerPink         = hand.getPinkyFinger();
    // or                        hand.getFinger("pinky");
    // or                        hand.getFinger(4);
    
    
    
    
    
     fingerIndexX = (fingerIndex.getPosition().x);
     fingerIndexY = (fingerIndex.getPosition().y);
     
     ellipse(fingerIndexX,fingerIndexY,100,100);
     

    for (Finger finger : hand.getFingers()) {
      // or              hand.getOutstretchedFingers();
      // or              hand.getOutstretchedFingersByAngle();

      int     fingerId         = finger.getId();
      PVector fingerPosition   = finger.getPosition();
      PVector fingerStabilized = finger.getStabilizedPosition();
      PVector fingerVelocity   = finger.getVelocity();
      PVector fingerDirection  = finger.getDirection();
      float   fingerTime       = finger.getTimeVisible();

      // ------------------------------------------------
      // Drawing

      // Drawing:
      // finger.draw();  // Executes drawBones() and drawJoints()
      // finger.drawBones();
      // finger.drawJoints();

      // ------------------------------------------------
      // Selection

      switch(finger.getType()) {
      case 0:
        // System.out.println("thumb");
        break;
      case 1:
        // System.out.println("index");
        break;
      case 2:
        // System.out.println("middle");
        break;
      case 3:
        // System.out.println("ring");
        break;
      case 4:
        // System.out.println("pinky");
        break;
      }


      // ================================================
      // 5. Bones
      // --------
      // https://developer.leapmotion.com/documentation/java/devguide/Leap_Overview.html#Layer_1

      Bone    boneDistal       = finger.getDistalBone();
      // or                      finger.get("distal");
      // or                      finger.getBone(0);

      Bone    boneIntermediate = finger.getIntermediateBone();
      // or                      finger.get("intermediate");
      // or                      finger.getBone(1);

      Bone    boneProximal     = finger.getProximalBone();
      // or                      finger.get("proximal");
      // or                      finger.getBone(2);

      Bone    boneMetacarpal   = finger.getMetacarpalBone();
      // or                      finger.get("metacarpal");
      // or                      finger.getBone(3);

      // ------------------------------------------------
      // Touch emulation

      int     touchZone        = finger.getTouchZone();
      float   touchDistance    = finger.getTouchDistance();

      switch(touchZone) {
      case -1: // None
        break;
      case 0: // Hovering
        // println("Hovering (#" + fingerId + "): " + touchDistance);
        break;
      case 1: // Touching
        // println("Touching (#" + fingerId + ")");
        break;
      }
    }


    // ==================================================
    // 6. Tools

    for (Tool tool : hand.getTools()) {
      int     toolId           = tool.getId();
      PVector toolPosition     = tool.getPosition();
      PVector toolStabilized   = tool.getStabilizedPosition();
      PVector toolVelocity     = tool.getVelocity();
      PVector toolDirection    = tool.getDirection();
      float   toolTime         = tool.getTimeVisible();

      // ------------------------------------------------
      // Drawing:
      // tool.draw();

      // ------------------------------------------------
      // Touch emulation

      int     touchZone        = tool.getTouchZone();
      float   touchDistance    = tool.getTouchDistance();

      switch(touchZone) {
      case -1: // None
        break;
      case 0: // Hovering
        // println("Hovering (#" + toolId + "): " + touchDistance);
        break;
      case 1: // Touching
        // println("Touching (#" + toolId + ")");
        break;
      }
    }
  }


  // ====================================================
  // 7. Devices

  for (Device device : leap.getDevices()) {
    float deviceHorizontalViewAngle = device.getHorizontalViewAngle();
    float deviceVericalViewAngle = device.getVerticalViewAngle();
    float deviceRange = device.getRange();
  }


  
    // ====================================================

    if(fingerIndexX> 400 && fingerIndexX<1200 && fingerIndexY>0 && fingerIndexY< 500){
    x1=255;
    
      if(alreadyExecuted1 == false) {
    x = x+1; 
    alreadyExecuted1 = true;
   
    }
    if(alreadyExecuted1 == true){
      x = x;
    }
   alreadyExecuted2 = false;
   alreadyExecuted3 = false;
    
  }
   else{x1 = 155;}
  

  if(fingerIndexX> 0 && fingerIndexX<800 && fingerIndexY> 500 && fingerIndexY< 1000 ){
    x2=255;
    if(alreadyExecuted2 == false) {
     x = x+1; 
    alreadyExecuted2 = true;
    }
    if(alreadyExecuted2 == true){
       x = x;
    }
    
   alreadyExecuted1 = false;
   alreadyExecuted3 = false;
   alreadyExecuted4 = false;
   alreadyExecuted5 = false;
    
  }
  else{x2=155;}
  
  

  if(fingerIndexX> 800 && fingerIndexX<800+800 && fingerIndexY>500 && fingerIndexY< 1000){
   x3=255;
    if(alreadyExecuted3 == false) {
   x = x+1; 
    alreadyExecuted3 = true;
    }
    if(alreadyExecuted3 == true){
   x = x;
    }
   alreadyExecuted1 = false;
   alreadyExecuted2 = false;
   alreadyExecuted4 = false;
   alreadyExecuted5 = false;
   }
  else{x3=155;}
  
 
  
  
  // ====================================================
  
  
      
  int sec = second();
  if(sec % 3 == 0 && visited != sec){
    diff = x - Oldx;
    rate = diff;
    Oldx = x  ;
    visited = sec;
    q = q + 1;
   //delay(1001);
  }
    

  // ====================================================
 if(rate == 7.0 || rate == 8.0){
   rate = 7.5;
   s1 = s1 +1;
   freq2 = freq2+1;
 }
 if(rate == 9.0 ||rate == 10.0|| rate == 11.0){
   rate = 10;
   s2 = s2 +1;
   freq3 = freq3+1;
 } 
 if(rate ==0.0 || rate == 4.0 || rate == 6.0 || rate == 5.0){
   rate = 5.0;
   s3 = s3 +1;
   freq1 = freq1+1;
 }
 
 
 realRate = realRate + 0.5*(rate - realRate);
 if(realRate == 7.0 || realRate == 8.0){
   realRate = 8.5;
 }
  
  soundfiles[i].rate(map(realRate,4,11,0.6,1.4));


print("x");
println(x);
print("Oldx");
println(Oldx);
print("realRate");
println(realRate);

// ====================================================
// ====================================================

 targetX += (fingerIndexX - targetX) * 1;
 targetY += (fingerIndexY - targetY) * 1;
 Particle nextParticle = particles[particleIndex];
 nextParticle.reset(targetX, targetY);
    if (++particleIndex >= particles.length) 
    {particleIndex = 0;
}

if(q>=20){
 if(freq1 >= freq2){
   
   if(freq1 >= freq3){
     compare = 1;}
   else if(freq1 < freq3){
     compare = 3;}
}
   
  if(freq1 <= freq2){
    
    if(freq2 >= freq3){
      compare =2;}
    else if(freq2 < freq3){
      compare =3;}
  }
}

  if(compare == 1){
  image(res1,0,0);
  soundfiles[i].rate(0);
  
  }
  if(compare == 2){
  image(res2,0,0);  
  soundfiles[i].rate(0);
}
  if(compare == 3){
  image(res3,0,0);  
  soundfiles[i].rate(0);  
}




}



}



int particleIndex = 0;






class Particle {
  float x = 0.0;
  float y = 0.0;
  float vx = 0.0;
  float vy = 0.0;
  float r = 255;
  float g = 255;
  float b = 255;
  float a = 255;
  color pColor = color(255, 255, 255, 255);
  int life = 0;

  void update() {
    if (this.life > 0) {
    
        this.life--;
        if (this.life < 50) {
            this.x += this.vx;
            this.y += this.vy;
            this.vx += random(4) - 2;
            this.vy += random(4) - 2;
            this.vx *=0.9;
            this.vy *=0.9;
            this.a = 255 * (this.life / 50.0);
        }
    }
  }
  
  void reset(float _x, float _y) {
      this.x = _x;
      this.y = _y;
      this.vx = random(4) - 2;
      this.vy = random(4) * 4 - 2;
      this.life = 55;
      this.g = map(_x, 0, width, 0, 255);
      this.b = map(_y, 0, height, 0, 255);
      this.a = 255;
  }

  void render(){
      fill(color(this.r, this.g, this.b, this.a));
      ellipse(this.x, this.y, 30, 30);
  }

}




 // ====================================================
 // ====================================================
void setupSerial() {
  printArray(processing.serial.Serial.list());
  myPort = new processing.serial.Serial(this, processing.serial.Serial.list()[0], 9600);

  myPort.clear();
  myString = myPort.readStringUntil( 10 );  // 10 = 'n'  Linefeed in ASCII
  myString = null;

  sensorValues = new int[3];
}



void updateSerial() {
  while (myPort.available() > 0) {
    myString = myPort.readStringUntil( 10 ); // 10 = 'n'  Linefeed in ASCII
    if (myString != null) {
      String[] serialInArray = split(trim(myString), ",");
      if (serialInArray.length == 3) {
        for (int i=0; i<serialInArray.length; i++) {
          sensorValues[i] = int(serialInArray[i]);
        }
      }
    }
  }
}

Final Project: Monkey Catch(Moon) Mackenzie

Title
  • Final Project: Monkey Catch

Date 13 December 2017

Instructor Moon

Aim/Goal

  • Complete a game design in Processing, encompassing the styles of setting and object-oriented programming
  • Complete a circuit using pressure force sensors and Accelerometer by Arduino to control the bounce of ball and the movement of monkey
Materials needed / used / provided
  • Arduino, breadboard, cables, 220 ohm resistor, pressure force sensor, accelerator

 

Exercises / Experiments

This project consists of four steps. First is to complete the circuit of pressure force sensors and accelerator. Second is to draw two objects controlled by key code, representing monkey and ball by processing. The third step is serial communication, connecting Arduino with Processing and replace controller from key code to sensors.

  1. Arduino

In the first step, I searched online and follow the diagrams below to complete the circuit of pressure force sensor and Accelerometer.

PFS:

Accelerometer:

The circuit is very simple, so I completed like this.

2. Processing
The second step is to draw two objects controlled by key code, representing monkey and ball by processing. Although this project is a combining work of Arduino and Processing. Processing is the key part of the work.
  • There are three lessons I learnt from coding the monkey and the setting. Firstly, how to insert GIF format image. Firstly, I downloaded the GIF library from Google https://extrapixel.github.io/gif-animation/. Then I put the file inside the libraries folder under Processing. Under the guidance of Jowon, I insert GIF into Processing in the way below, which is very similar to insert Image. The only difference is to input GIF example from library first.
Secondly, using keyboard to control the movement of monkey. (UPDATE).
It turns out like this.
Thirdly, how to code Count Down. Firstly, I studied from the example online (https://forum.processing.org/one/topic/how-to-create-countdown-timer.html). Based on the coding learnt from Processing Forum, I created my own code that fits my project as following.
It works in counting down the number. But the problem is that it keeps counting down after zero. So I add t = 0 into the condition of  t<= 0 in order to make the number still in zero. However, it still does not work.
Under the guidance of Professor Moon, I change the way from making it still in zero to make the part of Count Down disappear, by put it in the condition of t > 0, which is also the supplement conditions of t <=0, as following.
I made it!! Plus, since if the player can insist the ball not falling on the ground for 30 seconds, it means he or she wins the game. I set a Winning setting as a consequence.
  • Above is what I learnt from coding the monkey and the setting. The next step is to draw the ball by object-oriented programming. There are three things I learnt from this part.
Firstly, I set a general structure of object-oriented programming, which is like this.
It works pretty good, but the speed is the same along the way rising up and falling down, which is not so realistic. Thanks to Moon’s suggestion of adding the effect of gravity to make the ball bouncing more polished, in the way as following.
It turns out like this (UPDATE video of ball bouncing)
  • Finally, I come to the part of serial communication. In this project, I will use pressure force sensor to bounce the ball and accelerometer to control the movement of monkey.

Starting from PFS, the circuit is shown as the picture below.

The first step is to set up a basic structure of serial communication.
( Sending data by Arduino)
(Receiving data by Processing)
What I learnt most is from how to make the ball bouncing out in control, and bounce out one by one according to pressure. The problem I faced is the ball would automatically bounce out without input the pressure, which can be solved by setting an condition that when input data>0, the ball would run. However, since the code run repeatedly and fast, it would generate many balls at the same time. One solution to solve this problem is to increase the time of delay in Arduino, like this.
However, the sensor become far less sensitive after changing the time of delay, which cause very bad user experience when playing the game. Thank Moon for his suggestion in setting a boolean variable, which is false before initiator. At the initial point of first pressing, in which the previous value equals to zero and the current value is larger than the previous value. The boolean value will change from false to true, and bouncing out the ball.
(In variables)
(In class)
( In draw)
Generally, what I have improved in coding by Processing is organization. Using subtitle as an guidance, it becomes much more clear when refining the work.

[Marcela] Final Project Proposal- Ariel Wang

Basketball bombs is my final project, first let me show you how it works:

  • Overview

It is an entertaining double-player game which uses vibration sensor and several buttons to control the main character–Safari and Chrome, and the goal is “not die”. Users can tap the ball to let the bomb roll in the map and “kill” the other player. Learned from my midterm project, I add an instruction page before the game start:

This is the main scene, you can see there are Safari, Chrome, Power-up shoes and little monsters which make user moves slowly

Finally is the ending scene, winner is Chrome of Safari, or if Safari and Chrome die at the same time, the bomb–basketball wins! Also learn form my midterm project, I made the Restart function here.

 

  • Inspiration

I was inspired a lot by the game BomberMan. It is a childhood-game for me, and I really like it. So I based my game on “bomb”, which is inspired by BomberMan, and make it a double-player game so that it is more competitive and interesting. But besides that, I also made a lot of change to that. Firstly, traditional BomberMan usually have a map with many “obstacles” or “brick” which you need to put down bombs to erase them, but in my game, the map is blank and you can walk anywhere you want. Secondly, in my game, you don’t “produce” bomb yourself, but it will fall down every 8 seconds and you just need to escape it or “play” with it by tapping the ball. Thirdly, in traditional BomberMan, there are usually no power-ups or other interesting items for users to pick, but here, I made two items, one is shoe, which can make user move quickly in a period of time, the other is a little monster, which makes you move slowly. One more interesting thing I think is interesting in my project is that I create an interesting outcome–if both players die at the same time, the bomb wins, which I think add the game much more fun.

  • My users

Our users are mainly children and teenagers who want to play game for fun. So there is no bloodiness or violence in our game–all things are presented in a cute way, and the game is easy to control and to understand–just use the buttons to control direction and tap the ball to move the bomb, and the subjective is very direct and simple–not die. More over, our users are people who seek relaxation and entertainment, they can play our game to entertain themselves during the final. People who have a good childhood memory with BomberMan are also my users, because I provided them with a more interactive way to play it, and it is interesting.

  • How we make it happen

Because it is a double-player game, I need to use two Arduino to send data and control the characters. But first I build the circuit and code the single Arduino:



By the way, in order to make it more convenient to put the circuit into the box, I sealed all the buttons. First I used the small buttons in my Arduino Kit:

But after test I found that it is not convenient to control, so I then sealed the bigger buttons and it works much better:

Then is about the ball, at first I made two bigger “balls” and want to decorate it to a basketball:

But then I found that I cannot find cloth with the patter of a basketball to decorate it, and if I use the A4 paper to envelop it, the paper will get broken easily when users tap it, so finally I had to use to balls and stick them on the box.

Then after combine the buttons and the vibration sensor with the ball, the hardware part is finished:

Then is the processing part, in order to make it easier to arrange, I created many different class or function, and basically it looks like this:

Here is the main part code, this part is mainly to set fundamental things, build a basic model of the game, and connect with two Arduino:

Then is the class bomb, here I set the bomb time, random position, and the different stage for the bomb to explode. Also, I use Boolean to judge where the player will die here:

Then is the Collision function, without this, once user tap the ball, the bomb will roll to one direction without stop, but I want it to stop when it touched another bomb or reach the border, which makes more sense, this is how it works:

Then is the judge part, here is for the judgement of the outcome, whether Safari or Chrome, or basketball wins, I also use boolean here:

Then is the kick functon, this is for the bomb to roll when user tap the ball:

Then is the player function, what I did here is to use the button to control the player’s move:

Then is the restart function, I learned from my midterm project that it is better to have a restart function because people would like to play it again and again:

Then is the speed up and down class, this works for the shoes and the little monsters which makes players moves slower:

Finally is the timer class, it’s a class that takes an integer as a parameter which is the time interval. It has two methods, when the method start count is called, the timer will start to count time. When the time reach the set value, the other method isFinished will turn to true which gives the information that the time is up, and it works like this:

  • Reflections

Though in all I consider my project successful, here are still something I failed to achieve.

  1. First thing is about the name. Actually we want to have a better name than “Basketball bombs”. Maybe something like “basketball player bombers” or something like “War between Safari and Chrome”. However, after struggling a lot of time with the name, I finally choose “Basketball bombs” which is easy and safe. But I only think I may come up with something better. Next time maybe we will collect ideas from others and name our project better.
  2. Another thing I failed is to make more kinds of power-ups. For example, you can pick a “house” and use it when you think you are going to be bombed–a house can protect you. Besides, something like a “egg” that enable users to produce a bomb themselves wherever they want. Also, maybe something like a “bird” which enables user to “fly over” all the obstacles and will not be stopped by the bombs on the map. But because of the time limit and I am not sure how to add buttons to control these things, I failed to make it happen.
  3. Though I mentioned the bomb will explode after 3s in my instruction page, it will be good if I can add countdowns during the game so that players can have a better sense about when the bomb is going to explode.
  4. My original plan is actually football, and the map looks like this: But if I use football, to make more sense, I made four buttons on the ground and users are supposed to control the direction by feet, but later on, after tested it with 4 users, they said that it is difficult to control the direction with feet because the feet will be too busy. As my game requires users to move very fast, I finally decided to use hand to control both direction and roll the ball, so i changed the theme to basketball. But in the end I found that though users can play the game fluently, the game is not as interactive as I expected it to be because users moves a little. I hope I can find a better way where users can both control the game without difficulty and moves more during they play the game.

 

  • If I have more time

If I have more time, I would improve the following things:

1.I would add more functions into the game. Like the time-limited mode, and maybe add more kinds of power-ups as mentioned above.

2.I would make the background, the character and the obstacles more beautiful and reasonable. For example, I will have more kinds of characters so that players can choose one he/she likes, which will make the game more interesting. Or I’ll use camera to take a photo of users and use the photo rather than Safari and Chrome.

3.I will have more level of difficult for users to choose from, such as “simple”, where there is only a few bombs every time, and “middle” and “hard”.

4.I would spend more time to make a fully functional prototype, to test the market, to collect more feedbacks from users and to launch my game!

 

Finally, I really appreciate a lot to those who help me during the process. Thanks a lot to professor Marcela, Professor Moon, and many fellows. Thanks a lot!

 

Final project by Qinfei Zou (Marcela)

Preface:

Finally it’s done! I’m so proud I made it to this point! Before I start to talk about my project, I  want to first thank everyone who has helped and encouraged me during the process. I was really freaking out when I found nothing was working as I expected in the middle of the process. I have another two major projects and exams due the same day as this project so I was struggling with time management and was always afraid of running out of time. I can’t describe how stressed I was during the passed three weeks. I had minimal sleep and devoted all my time to the final projects. Again I really thank people who have convinced me that I could make it. Without support from these kind people, there is no way for me to make this project happen.

 

The project:

My project is a “never have I ever” game with heart rate detection. This video shows how it works. On the first page is a flashing title and a start button. After clicking the button, an instruction window will pop up, and users need to click the red close button to move on to the next page. Then users can choose between “normal mode” and “crazy mode”. I included some everyday questions in the normal mode. For the crazy mode, I intentionally locked it because I don’t want to just copy the normal mode. I want to design some crazier visualiser in this mode, but I just do not have time to do that. It’s also because a lot of games actually have done this inside their programs, so I think this design makes sense. After clicking into the normal mode, a total of 10 questions will pop up in a random sequence. After appearing, the question will be removed from the arraylist so there won’t be repeated questions. The heart rate value from Arduino (sensor see below) is linked to the volume of the heart rate sound. The sound will go through frequency analysis (which is the fft analysis in minim library), and the frequency is linked to the height of the rectangles. Users can click the next button in the middle to jump to the next questions. After questions all run out, it will notice users that they are finished.

My users are those who want to play never have I ever with their friends. I think what differentiates my program from other similar games is that my program includes a heart rate sensor, which I believe will make the atmosphere more exciting. I design the interface to be visually stimulating, which I believe also appeals to people who want to play game with friends at parties.

 

The prototype:

At the beginning I wanted to build some lie detector. I wanted to convert the data from arduino into a line graph in processing, and copy users’ facial expressions onto a 3d model in processing to show their eye blinks. Below is my original storyboard and the very preliminary interface that I coded based on this storyboard. This video shows how the line graph works. I actually did not like it because it is ugly and nobody’s gonna play this dull game. Therefore I started to think about what else could I do to make the interface prettier.

In addition to this, I also encountered some technical problems that made me give up this design. First, even though I have drawn the 3d models of a head (which is supposed to be Pinnochio’s head because he is well known for his story of lying) in tinkercad, I can’t import the stl file in processing. I found this possible stl reader, but the code is way too complicated for me to understand. I turned to Nick for help but even he did not understand how it works, so I have to give up importing 3d model from tinkercad to processing.

Then intuitively I wanted to draw the whole thing in processing. But as what I have found about 3d objects in processing, there are only two basic shapes box and sphere. I tried to draw a sphere to be like a head, but ideally I would prefer an ellipsoid. Also there is no direct function for cylinder, so this cylinder below takes a lot of work by using the begin shape and end shape functions. I think it would take way too much time to build a head in processing, and it is not really worth it because the head thing is not the main point. So I decided to take this part out of the story.

Also I wanted to use FaceOSC to detect eye blinks. After searching for sample codes online, I  found that FaceOSC is only able to detect the location of eyes and eyebrows. There is no evidence that it could detect the frequency of eye blinks. Therefore I have to give this part up because the location of eyes is not really contributing to lie detection. Even though I did not use FaceOSC at last, I still did a lot of experiments with it and learned how to use this outside software in processing to detect the locations on face. Below is a face I drew with FaceOSC in processing based on the sample codes.

So basically this is how I found nothing is working as I expected when I was exploring in the things I wanted to use. Therefore I started to brainstorm again what else should I do.

 

The new idea:

When I had no idea what to do, I saw this interactive never have i ever that really inspired me. I like the colourful background and I think it gives users a very fresh and entertaining experience. So I decided also to focus on the visual design in my project and make it visually entertaining.

My second inspiration is this music visualiser. When I was thinking about what kind of graph should I use to show the change in heart rate, music visualisers hit my mind. I like how the change of beat and volume in music change the visualisers. It inspires me that instead of directly relating heart rate with a graph, I could use music as an intermediary. Heart rate is only one variable, whereas music could contain a number of variables. So I think by using music to change the graph will make it more diverse.

When I started to design the new interface, I really focus a lot on the visual design. I drew all the buttons on Piskel because I think buttons in pixel look cool in games, and it is more intuitive to draw in outside platforms rather than directly drawing in processing. This is also what I learned that I do not necessarily have to achieve all the effects I want in processing. It’s ok to use outside sources if it’s more convenient and time-saving. Moreover, I used audition to edit the heartbeat sound because I could not find any heartbeat sound online that I could directly use in my project. I cut a short period from another mp3 and edited into a file on my own. I also did a lot of image editing in Photoshop because I think it is more convenient to see the effects and then adjust it than using the functions in processing.

When I started to test the heart rate sensor, I didn’t know how to use it because the wiki of ear-clip heart rate sensor is not very straightforward to understand. I copied the code into arduino and tested it. For some unknown reason I always got the same figure in arduino, which I didn’t think is right because there definitely should be some changes in it. I guess it is because I connected the sensor to digital (which it should), so I tried to reconnect it to analog but still it was not right. I turned to Marcela for help and thanks to her I finally understood how heart rate sensor works and found the right value that I need to send to processing. In the sample code from seed, by default it creates a pool of 20 values and calculates the average heart rate through some formula when the pool is filled, which means I have to wait for several seconds to get the actual heart rate. I don’t really need the actual heart rate to create the graph, so I simply use another value that is shown in the serial monitor consistently.

 

The reason why I chose to build a 3d visualiser is that I have already touched a little bit on 3d objects in my midterm project, so I wanted to explore more in this area. I turned to this open source to build the visualiser. Basically how the visualiser works is that it uses the get() function to get all the pixels on the current interface, which is like taking a screenshot, and then downsize what it gets a little bit behind the original layer to create a fading effect. When I set the heights of rectangles to a single random range, the graph is not that cool. It confirms my idea to use music as an intermediary.

When I put the visualiser into my main sketch, I met a big problem like this. Because the get() function is like taking a screenshot, so it takes screenshot of the whole interface which means that the questions I have put above the visualiser is also resolved into the fade function.

To solve this problem, I turned to Jack for help. He very smartly suggested that I should change the range of the get function to make it only get pixels from lower range of the interface. So I used a variable “top” to lower the range and it works. As you can see in the video below, only the lower half of the interface will fade.

I used the setGain function in minim to change the volume of the heartbeat sound. Because the change in heart rate is very subtle, so I mapped the heart rate to a bigger range to amplify the change. But still the change in volume is only clear in headphones. It can hardly be noticed in speakers. Because when I finished at this point is already the midnight before deadline, so I didn’t have time to look for some other way to amplify the change in volume.

For the question part, most of the questions are credited to a mobile app called “Never Ever”.

I first simply used a string array (effect in video) to code the questions, but one big problem I met is that the questions will appear repeatedly. I searched online and found that variables can be removed after appearing if using arraylist. I had a little difficulty understanding the arraylist because I have never used it before, and also I need to code a class to use the arraylist which seems complicated for me. I turned to Marcela for help and she suggested that I should review the class example of arraylist. I really thank Marcela for teaching me step by step how to use arraylist and I finally understand how to use it.

 

Reflection:

One aspect that I believe I should have improved is user experience. Both my friends and I are super busy with finals, so I think this is the reason why I didn’t do much testing on them. If I had more time, I definitely will do more user testing during the progress. Based on the feedback I got in class, the part of heartbeat sound is a little confusing because I also have background music. In response to this feedback, I added a line in the instruction page to clarify the process.

What I like about my project is the visual design. I really like the pixel buttons and the little shiny hover effect. I also like the 3d visualiser because I think it’s really cool. If I had more time, the first thing is to figure out how to make the heartbeat part more clear. Then I will code another visualiser for the crazy mode. I really want to explore something more about visualisers.

Overall, it is a tough process to build this project but in the end I was really glad and proud of my self for all the efforts I spent on it. Things are not always working as I expected, so I should be flexible and be ready to embrace new changes.

 

Looking back to this semester in interaction lab, I think it was really inspiring and I learned a lot of new things. I gained great sense of achievement every time I finished a major project. Also through the projects I found that I am a more “visual” person. Comparing with building some physical effects in arduino, I’m more interested in creating some cool effects in processing. Realising this, I look forward to learning more about coding and visual designing in IMA.

Thanks Marcela for such a great semester!

// Function: This program can be used to measure heart rate, the lowest pulse in the program be set to 30.
    //         Use an external interrupt to measure it.
    // Hardware: Grove - Ear-clip Heart Rate Sensor, Grove - Base Shield, Grove - LED
    // Arduino IDE: Arduino-1.0
    // Author: FrankieChu       
    // Date: Jan 22, 2013
    // Version: v1.0
    // by www.seeedstudio.com
    #define LED 13//indicator, Grove - LED is connected with D4 of Arduino
    boolean led_state = LOW;//state of LED, each time an external interrupt 
                                    //will change the state of LED
    unsigned char counter;
    unsigned long temp[21];
    unsigned long sub;
    bool data_effect=true;
    unsigned int heart_rate;//the measurement result of heart rate

    const int max_heartpluse_duty = 2000;//you can change it follow your system's request.
                            //2000 meams 2 seconds. System return error 
                            //if the duty overtrip 2 second.
    void setup()
    {
        pinMode(LED, OUTPUT);
        Serial.begin(9600);
        Serial.println("Please ready your chest belt.");
        delay(5000);
        arrayInit();
        Serial.println("Heart rate test begin.");
        attachInterrupt(0, interrupt, RISING);//set interrupt 0,digital port 2
    }
    void loop()
    {
        digitalWrite(LED, led_state);//Update the state of the indicator
    }
    /*Function: calculate the heart rate*/
    void sum()
    {
     if(data_effect)
        {
          heart_rate=1200000/(temp[20]-temp[0]);//60*20*1000/20_total_time 
         // Serial.print("Heart_rate_is:t");
         // Serial.println(heart_rate);
        }
       data_effect=1;//sign bit
    }
    /*Function: Interrupt service routine.Get the sigal from the external interrupt*/
    void interrupt()
    {
        temp[counter]=millis();
        //Serial.println(counter,DEC);
        //Serial.println(temp[counter]);
        switch(counter)
        {
            case 0:
                sub=temp[counter]-temp[20];
                Serial.println(sub);
                Serial.print(sub);
                break;
            default:
                sub=temp[counter]-temp[counter-1];
                Serial.println(sub);
                Serial.print(sub);
                break;
        }
        if(sub>max_heartpluse_duty)//set 2 seconds as max heart pluse duty
        {
            data_effect=0;//sign bit
            counter=0;
            //Serial.println("Heart rate measure error,test will restart!" );
            arrayInit();
        }
        if (counter==20&&data_effect)
        {
            counter=0;
            sum();
        }
        else if(counter!=20&&data_effect)
        counter++;
        else 
        {
            counter=0;
            data_effect=1;
        }

    }
    /*Function: Initialization for the array(temp)*/
    void arrayInit()
    {
        for(unsigned char i=0;i < 20;i ++)
        {
            temp[i]=0;
        }
        temp[20]=millis();
    }

-----------------------------------------------------------------

import ddf.minim.*;
import ddf.minim.analysis.*;
import ddf.minim.effects.*;
import ddf.minim.signals.*;
import ddf.minim.spi.*;
import ddf.minim.ugens.*;

import gifAnimation.*;

import processing.serial.*;

Serial myPort;
int valueFromArduino;

Gif loopingGif;

Minim minim;
AudioPlayer song;
FFT fft;

AudioOutput out;
AudioPlayer mp3;

visualizer classicVi;

PImage fade;
float rWidth,rHeight;
int hVal;

boolean started; //boolean for telling if the program has started 
boolean selection; //boolean for telling if a song has been selected
boolean canPlay;//boolean for allowing the audio to play 
boolean canPauseAndPlay; //boolean for allowing the pause and play function 
PImage title;//Image etup for the title screen 

PImage img1;
PImage img2;
PImage img3;
PImage img4;
PImage img5;
PImage img6;
PImage img7;
PImage img8;
PImage img9;
PImage img10;
PImage img11;
PImage img12;
PImage img13;
PImage img14;
PImage img15;
PImage img16;
PImage img17;
PImage img18;
PImage img19;

PFont font1;
PFont font2;
PFont font3;
PFont font4;

short counter;

int x = 0;

float volume;

void setup() {
  size(1000, 700,P3D);
  background(#cefcfc);
  minim = new Minim(this);
  song = minim.loadFile("5.mp3");
  mp3 = minim.loadFile("7.mp3",2048);// load your mp3 file here make sure you use processing's built in add file feature and type the name of the loaded mp3 inside the quotation marks
  out = minim.getLineOut(Minim.STEREO,44100);
  fft = new FFT(mp3.bufferSize(),mp3.sampleRate());//allows for the decription of the sound waves 
  rectMode(CORNERS);
  loopingGif = new Gif(this, "5.gif");
  loopingGif.loop();
  font1 = createFont("Montez-Regular.ttf", 32);
  font2 = createFont("Audiowide-Regular.ttf", 32);
  font3 = createFont("Bangers-Regular.ttf", 32);
  font4 = createFont("VT323-Regular.ttf", 32);
  img1 = loadImage("1.jpg");
  img2 = loadImage("6.png");
  img3 = loadImage("7.png");
  img4 = loadImage("8.jpg");
  img5 = loadImage("9.png");
  img6 = loadImage("19.png");
  img7 = loadImage("20.png");
  img8 = loadImage("26.png");
  img9 = loadImage("25.png");
  img10 = loadImage("11.jpg");
  img11 = loadImage("4.jpg");
  img12 = loadImage("6.jpg");
  img13 = loadImage("32.png");
  img14 = loadImage("33.png");
  img15 = loadImage("27.png");
  img16 = loadImage("28.png");
  img17 = loadImage("31.png");
  img18 = loadImage("35.png");
  img19 = loadImage("40.png");
   started = false;
  selection = false;
  
   myPort = new Serial(this, Serial.list()[1], 9600);
   
   fade = get(0,0, width,height);// parameter for making the background fade
   classicVi = new visualizer(); // initializes the class visualizer 
  rWidth = width*0.99;
  rHeight = height*0.99;
   
 
  if(!started)
  {
    mp3.pause();
  }
}

void draw() { 
  song.play(); 
  song.setGain(0.1);
 
  cover();
  
   while ( myPort.available() > 0) {
    valueFromArduino = myPort.read();
  }
   println(valueFromArduino);
   map(valueFromArduino,45,60,1,20);
   
   volume=valueFromArduino;
   
   if(started == true){
     song.pause();
     song.mute();
    mp3.play();
    mp3.setGain(volume);
    }
}
  
void mouseReleased(){
  if(mouseX>440&&mouseX<560&&mouseY>520&&mouseY<570){
  counter=1;
  }
  if(mouseX>766&&mouseX<820&&mouseY>100&&mouseY<122){
  counter=2;
  }
  
  if(mouseX>325&&mouseX<675&&mouseY>170&&mouseY<270){
    counter=3;
    }
    
  if(mouseX>325&&mouseX<675&&mouseY>400&&mouseY<500){
    counter=4;
    }
  println(counter);
}

void mousePressed(){
  if (mouseX>430&&mouseY>280&&mouseX<580&&mouseY<330) {     

    if (nextLetters >0) {
      particles.remove(k);
      nextLetters = nextLetters-1;
      k=int(random(nextLetters));
    }
  }
}

void cover(){
  
  image(img10, 0, 0, 1000, 700);
  
  textFont(font3);
  textSize(120);
  for(int q = 0; q < 10;){
  fill(random(200,250),random(80,150),random(50,100));
  text("NEVER HAVE n    I EVER", 275,170);
  q+=2;
  }
  textFont(font2);
  textSize(50);
  fill(100,100,100);
  text("with HEART RATE DETECTION", 100,410);
  
  image(img5, 440, 520, 120,50);
  if(mouseX>440&&mouseX<560&&mouseY>520&&mouseY<570){
  image(img2, 440, 520, 120,50);
  }
  if(mousePressed){
  image(img4, 170, 100, 660,500);
  image(img3, 325, 150, 350,80);
  }


  if(counter>0){
  image(img4, 170, 100, 660,500);
  image(img3, 325, 150, 350,80);
  textFont(font4);
  textSize(35);
  fill(0);
  text("1.This device is for entertainment n only. n2. Think about 'never' or 'have done'n after reading the question. For privacy n concern, no answer will be recorded. n3. Please close this window to begin. n Enjoy!", 220,280);
  }
  
  if(counter>1){
    //textFont(font2);
    textSize(50);
    image(img10,0,0,1000,700);
    image(img6,400,170,200,80);
    image(img7,400,400,200,80);
    image(img19,472,412,45,45);
    
    if(mouseX>325&&mouseX<675&&mouseY>170&&mouseY<270){
    image(img8,395,166,210,88);    
    image(img13,625,50,350,200);
    textFont(font4);
    textSize(35);
    text("Question topics are nrelated to everydaynlife.",675,100);
    }
    
    if(mouseX>325&&mouseX<675&&mouseY>400&&mouseY<500){
    image(img9,394,396,212,88);
    image(img14,25,400,350,200);
    image(img19,472,412,45,45);
    textFont(font4);
    textSize(31);
    text("More exciting questions nincluded. Purchase nto unlock! ",50,460);
  }
     
     if(counter>2){
      //image(img11,0,0,1000,700);
      if(counter==3){
        image(img10,0,0,1000,700);
        //counter = 5;
        //q1();
        q3();
        classicVi.drawEQ(); 
        started = true;
      }
        if(counter==4){       
         //image(img18, 735, 50, 50, 50);
         image(loopingGif, 200, 50, 600, 600);
         if(mousePressed){
           counter=5;
         }
      }
      else if(counter==5){
        image(loopingGif, 200, 50, 0, 600);
        image(img18, 750, 50, 0, 600);
      }
     }
  }
}

class visualizer{
  PImage fade;
  float rWidth,rHeight;
  int hVal;
  int top = 360;
  visualizer(){  
  hVal = 0;
  rectMode(CORNERS);
  fade = get(0,top, width,height-top);
  rWidth = width*0.99;
  rHeight = (height-top)*0.99;
  }  
  
  void drawEQ()
{
  //creates the values needed to make the 3D effect 
  tint(255,255,255,254);
  image(fade,(width-rWidth)/2,(height-top-rHeight)/2+top, rWidth,rHeight);
  noTint();
  
  // rainbow Effect parameters
  smooth();
  colorMode(HSB);// sets color mode value 
  fill(hVal, 255,255);//cycles through hue and brightness to expose a greater color palete
  stroke(hVal, 255,225);// sets the stroke to cycle through the whole color spectrum 
  colorMode(RGB);//sets color mode back to Red green and blue 
  //fill(EQColorR,EQColorG,EQColorB);
  
  
  //for loop for creating the audio bars
  
  fft.forward(mp3.mix);// used to analyze the frequency coming from the mix 
  for(int i = 0; i < fft.specSize(); i += 50)// specSize is changing the range of analysis
  {
    strokeWeight(2);
    rect(i,height, i + 50, height - fft.getFreq(i/2)*1.2);// draws a rect and alters its height based on the translated frequency 
  }
  fade = get(0,top, width,height-top);
  hVal +=1;
  
  if(hVal > 255)
  {
    hVal = 0;
  }
  }
  }

ArrayList<Particle> particles = new ArrayList<Particle>();

String [] question1  = 
  {
  "Never have I ever Googled my name.", 
  "Never have I ever tried to guess someone'sn password.", 
  "Never have I ever fell asleep at class.", 
  "Never have I ever been so drunk that I didn something I regret.", 
  "Never have I ever been in a love triangle.", 
  "Never have I ever drunk Vodka.",
  "Never have I ever drunk while underage.",
  "Never have I ever wished I had studied n something else instead",
  "Never have I ever smoked a cigarette.",
  "Never have I ever been to a concert",
};

int nextLetters = 6;
int k = int(random(nextLetters-1));


void q3(){
 image(img17, 30, 30, 940, 200);
  for (int k=0; k < nextLetters; k++) {
    particles.add( new Particle(question1[k], 60, 110) );
  }
  
  if (nextLetters >0) {
    Particle p = particles.get(k);
    p.display();
  }
  
  fill(0);
  textSize(60);
  noStroke();
  
  if(nextLetters==0){
  text("You are finished!", 300, 150);
  }
  
  image(img15, 430, 280, 110, 50);
 // text("switch", 700, 300);
  //rect(150, 200, 120, 30);
  
  if(mouseX>430&&mouseY>280&&mouseX<580&&mouseY<330){
    fill(255,100,200);
    //text("next", 300, 300);
    image(img16, 426, 276, 118, 58);
  }
}
  
class Particle {
  float x, y;
  String letter;
  
  Particle(String tempLetter,float tempX, float tempY){
    letter = tempLetter;
    x = tempX;
    y = tempY;
  }
  
  void display() {
   fill(0);
   textSize(40);
   text(letter,x,y);
  }
  
}

09/15 Arduino Basics | Tiger Tian

(This is a laaaaaate documentation of the recitation on September 15th)

The first task was to make a traffic light. Below is the kit and stuff.

After the circuit was assembled according to the instructions, it looked like this. It works just like a real traffic light, expect for it does not automatically turn as time goes but is controlled with a button switch instead.

This is a temperature alarm from the second task. The buzzer buzzes when the sensor senses higher temperature, such as that from somebody’s skin, which made the whole classroom a little noisy because everyone’s alarm went off at the same time XD

And this third one is an ambient light that shines when the sensor senses darkness.

On!

And… off!