Lab 4: FSR game input

Description

I used the force sensitive resistor as an input for a game in Processing where the player flies an airplane to dodge walls. The force on the FSR determines the height of the airplane, which travels past obstacles at a constant speed. Originally, I wanted to have a trail following the airplane to show where it had been, but to improve the feel of the game, it was reduced to very small flames behind airplane to give a sense of the movement. An LED on the Arduino correlates its brightness with the FSR force for debugging when the serial connection has trouble.

I built a force diffuser from paper towels sandwiched between two stacks of sticky notes. The sticky note stacks were approximately the right size to be pushed with a hand, and the paper towels worked well because the amount of force diffused could be easy modulated by adding or removing paper towels. The sticky note stacks could be replaced with any hard object of the same size. While diffusing force is very useful for measuring forces outside of the FSR’s range, I found that when using the FSR as an input device for a game, it was best to have as little diffusing as possible to allow a single finger to have the most control.

Materials

  • Arduino
  • 1 Force Sensitive Resistor (FSR)
  • 1 LED
  • 1 22 KΩ resistor
  • 1 10 KΩ resistor
  • 10 wires

Images

Sticky Note force diffuser
Screenshot from FSR game

Arduino Code

int forceSensorPin = A0;    // select the input pin for the rate potentiometer
int ledPin = 9;      // select the pin for the LED
int forceValue = 0;  // variable to store rate pot value
void setup() {
  // declare the ledPin as an OUTPUT:
  pinMode(ledPin, OUTPUT);
  Serial.begin(9600);
}

void loop() {
  // read the value from the sensors:
  forceValue = analogRead(forceSensorPin);
  
  // turn the ledPin on, convert from 0-1023 scale to 0-255
  analogWrite(ledPin, forceValue / 4);
  // stop the program for 10 milliseconds:
  delay(10);
  // turn the ledPin off:
  digitalWrite(ledPin, LOW);
  Serial.print(forceValue);
  Serial.println();
}

Processing Code

/* PROCESSING FORCE GAME
 * ----------------------
 *
 * Using a force input, guide an airplane
 * to avoid obstacles.
 *
 * Created 23 September 2016
 * Sam Meyer
 */
import processing.serial.*;
// Change this to the portname your Arduino board
String portname = "/dev/cu.usbmodem1411"; // or "COM5"
Serial port;
String buf="";
int cr = 13; // ASCII return == 13
int lf = 10; // ASCII linefeed == 10

int serialVal = 0;
int xPos = 1000;
int rectHeight = 50;
float rand;
boolean gameOver = false;

EvilRectangle er;
ArrayList<EvilRectangle> erList;
int rCounter = 0;
long iDraw = 0;
int rectRate = 60;
PlayerIcon airplane;

class PlayerIcon {
  float x;
  float y;
  float y2; // green flame
  float y3; // blue flame
  
  PlayerIcon() {
    x = 150;
    y = 0;
    y2 = 0;
    y3 = 0;
  }
  
  void draw() {
    //blue flame
    fill(0, 0, 255);
    //ellipse(x3, y3, 100, 15);
    triangle(x - 50, y3 + 8, x - 50, y3 - 8, x - 70, y3); 
    triangle(x - 50, y3 + 8, x - 50, y3 - 8, x - 48, y3); 
    // green flame
    fill(0, 128, 0);
    //ellipse(x2, y2, 100, 23);
    triangle(x - 50, y2 + 12, x - 50, y2 - 12, x - 60, y2); 
    triangle(x - 50, y2 + 12, x - 50, y2 - 12, x - 45, y2); 
    // airplane
    fill(255, 255, 255);
    ellipse(x, y, 100, 25);
    triangle(x - 10, y + 50, x - 10, y - 50, x + 30, y);
    triangle(x - 50, y + 20, x - 50, y - 20, x - 20, y);
  }
  
  void setY(float val) {
    // move y values back through the following shapes
    y3 = y2;
    y2 = y;
    y = val;
  }
}

class EvilRectangle {
  float x;
  float y;
  float w;
  float h;
  EvilRectangle(float a, float b, float c, float d) {
    x = a;
    y = b;
    w = c;
    h = d;
  }

  void draw() {
    fill(255, 0, 0);
    rect(x, y, w, h);
  }
  
  boolean checkCollision(float xC, float yC) {
    return (xC > x) && (xC < x + w) && (yC > y) && (yC < y + h);
  }
}

void setup() {
 size(1000,600);
 frameRate(30);
 smooth();
 background(40,40,40);
 noStroke();
 port = new Serial(this, portname, 9600);
 er = new EvilRectangle(xPos, 600-rectHeight, 55, rectHeight);
 erList = new ArrayList<EvilRectangle>();
 erList.add(er);
 airplane = new PlayerIcon();
}

void draw() {

 if (gameOver) {
   fill(200);
   textSize(52);
   text("Crashed!\nDistance: " + str(int(iDraw)), 100, 200);
 } else {
   // increment counters
   iDraw += 1;
   rCounter += 1;
   
   // erase the screen
   background(40, 40, 40);
     
   // draw the airplane
   airplane.setY(max(50, 600 - serialVal/2 - 50));
   airplane.draw();
   
   // check for collsions
   for (EvilRectangle er : erList) {
     if (er.checkCollision(airplane.x + 50, airplane.y) || 
         er.checkCollision(airplane.x - 50, airplane.y) ||
         er.checkCollision(airplane.x, airplane.y + 50) ||
         er.checkCollision(airplane.x, airplane.y - 50)) {
       gameOver = true;
     }
   }
  
   // draw the rectangle
   for (EvilRectangle er : erList) {
     er.draw();
     er.x -= 5;
   }
   
   // draw current level
   textSize(16);
   fill(200);
   text("Current\nlevel: " + str(61 - rectRate), 10, 40);
   
   // add new rectangles
   if (rCounter >= rectRate) {
     rCounter = 0;
     if (iDraw < 240) {
       rectHeight = 50 * int(iDraw) / 30;
     } else {
       rectHeight = int(random(50, 400));
     }
     
     // increase speed
     if (iDraw > 400 & rectRate > 25) {
       rectRate -= 1;
     }
     
     // pick rectangle direction
     rand = random(1);
     if (rand < 0.4) {
       // bottom rect
       erList.add(new EvilRectangle(xPos, 600-rectHeight, 55, rectHeight));
     } else if (rand > 0.7) {
       // top and bottom
       erList.add(new EvilRectangle(xPos, 600-rectHeight/2, 55, rectHeight/2));
       erList.add(new EvilRectangle(xPos, 0, 55, rectHeight/2));
     } else {
       // top rect
       erList.add(new EvilRectangle(xPos, 0, 55, rectHeight));
     }
     
     // clean up rectangles that have left the screen
     for (int i = erList.size() - 1; i >= 0; i--) {
       if (erList.get(i).x < -100) {
         erList.remove(i);
       }
     }
     
     // reset rCounter
     rCounter = 0;
   }
 }
}

// called whenever serial data arrives
void serialEvent(Serial p) {
 int c = port.read();
 if (c != lf && c != cr) {
 buf += char(c);
 }
 if (c == lf) {
 serialVal = int(buf);
 // println("val="+serialVal);
 buf = "";
 }
}

Leave a Reply