Helicopter Game (Sensor Display)
For my sensor display I wanted to make use of the proximity sensor. I chose to create a portrayal of an old flash game I remembered playing. I’m not sure what the origin of it is, but a portrayal of it can be found at http://www.helicopter-game.org/ . However, for my version the helicopter is controlled by the height of the player’s hand over the sensor device. The device is meant to look like a black box which is typically associated with aviation. I made sure to use similar colors for the game and the box.
Here is a video of the project running:
Here is the Processing code:
// Helicopter Game
// If available this game will play using input from an arduino based accessory.
// Author: Matthew Kellogg
// Date: October 29, 2014
//Copyright 2014 Matthew Kellogg
//
// Import the Serial library and create a Serial port handler
import processing.serial.*;
import ddf.minim.*;
Serial myPort;
ArrayList ceilHeight = new ArrayList();
ArrayList floorHeight = new ArrayList();
float heliX = 200.0;
float heliY = 100.0;
color neonGreen = color(20, 200, 40);
int strokeW = 10;
int finalScore = 0;
boolean wrecked = false;
AudioPlayer player;
AudioPlayer explode;
Minim minim; //context
// Hey you! Use these variables to do something interesting.
// If you captured them with analog sensors on the arduino,
// They're probably in the range from 0 ... 1023:
int valueA; // Sensor Value A
int valueB; // Sensor Value B
//------------------------------------
void setup() {
size(1280,800);
// List my available serial ports
int nPorts = Serial.list().length;
for (int i=0; i < nPorts; i++) { println("Port " + i + ": " + Serial.list()[i]); } // Choose which serial port to fetch data from. // IMPORTANT: This depends on your computer!!! // Read the list of ports printed by the code above, // and try choosing the one like /dev/cu.usbmodem1411 // On my laptop, I'm using port #4, but yours may differ. if (Serial.list().length > 0){
String portName = Serial.list()[0];
myPort = new Serial(this, portName, 9600);
serialChars = new ArrayList();
}
resetBounds();
//audio
minim = new Minim(this);
player = minim.loadFile("helicopter-hovering-01.mp3", 2048);
explode = minim.loadFile("explosion.mp3", 2048);
player.loop();
}
void stop(){
player.close();
explode.close();
minim.stop();
super.stop();
}
void resetBounds(){
ceilHeight.clear();
floorHeight.clear();
// ceiling and floor
for (int i = 0; i -2 <= width/200; i++){
ceilHeight.add(10);
floorHeight.add(height - 10);
}
}
void keyPressed(){
if ((key == 'R' || key == 'r') && wrecked){
wrecked = false;
frameCount = 0;
finalScore = 0;
resetBounds();
player.loop();
explode.pause();
explode.rewind();
}
}
//------------------------------------
void draw() {
background (10,20,60);
if (wrecked){
if ((frameCount/5)%2 == 0){
fill(neonGreen);
drawHelicopter(200, (int) heliY, false);
}else{
fill(255,0,0);
}
textAlign(CENTER);
textSize(60);
text("FINAL SCORE: " + finalScore,width/2, height/2-70);
text("Press 'R' to restart", width/2, height/2 + 10);
}else{
// Process the serial data. This acquires freshest values.
if (myPort != null){
processSerial();
heliY = 0.9*heliY + 0.1*(map(valueA, 0, 100, height - 14*4, 100));
}
else{
heliY = mouseY;
}
//Check bounds
int checkIndex = (0 + 2*(frameCount%100))/100;
int ceilBound = max((Integer)ceilHeight.get(checkIndex), (Integer)ceilHeight.get(checkIndex +1));
int floorBound = min((Integer)floorHeight.get(checkIndex), (Integer)floorHeight.get(checkIndex + 1));
if ((heliY - 100 < ceilBound) || (heliY + 60 > floorBound)){
wrecked = true;
finalScore = frameCount/5;
player.pause();
player.rewind();
explode.play();
}
if (frameCount%100 == 0){
ceilHeight.remove(0);
floorHeight.remove(0);
int pCeil = (Integer)ceilHeight.get(ceilHeight.size()-1);
int pFloor = (Integer)floorHeight.get(floorHeight.size()-1);
ceilHeight.add((int)random(max(10,pCeil-100), min(pCeil+100, pFloor -350)));
floorHeight.add((int)random(max(pCeil +350, pFloor -100), min(height, pFloor + 100)));
}
drawBounds();
drawHelicopter(200, (int)heliY,false);
//score
textAlign(RIGHT);
textSize(30);
String score = "Score: "+frameCount/5;
int scoreW = (int)(textWidth(score));
fill(0);
strokeWeight(strokeW);
stroke(neonGreen);
rect(width - scoreW -20, -10, scoreW+30, 80);
fill(neonGreen);
noStroke();
text(score, width - 10, 50);
}
}
//draws the ceiling and floor
void drawBounds(){
fill(0);
strokeWeight(strokeW);
stroke(neonGreen);
beginShape();
vertex(width+10, -20);
vertex(-20,-20);
println("Ceil size is " + ceilHeight.size());
for (int i = 0; i< ceilHeight.size(); i++){
vertex(200*i - 2*(frameCount%100), (Integer)ceilHeight.get(i));
}
endShape(CLOSE);
beginShape();
vertex(width+20, height+20);
vertex(-20, height+20);
for (int i = 0; i< floorHeight.size(); i++){ vertex(200*i - 2*(frameCount%100), (Integer)floorHeight.get(i)); } endShape(CLOSE); strokeWeight(1); } //bounds of the helicopter are -45,-25 to 30, 14 void drawHelicopter(int x, int y, boolean bounds){ pushMatrix(); fill(neonGreen); noStroke(); translate(x,y); scale(4, 4); ellipse(0,-20, 60, 10); ellipse(0, 0, 25, 16); rect(-30, -4, 30, 6); ellipse(-30,0, 15, 15); if (bounds){ noFill(); rectMode(CORNERS); rect(-45, -25, 30,14); rectMode(CORNER); } strokeWeight(2); stroke(neonGreen); line(-8, 14, 8, 14); line(8, 14, 12, 10); popMatrix(); } //--------------------------------------------------------------- // The processSerial() function acquires serial data byte-by-byte, // as it is received, and when it is properly captured, modifies // the appropriate global variable. // You won't have to change anything unless you want to add additional sensors. /* The (expected) received serial data should look something like this: A903 B412 A900 B409 A898 B406 A895 B404 A893 B404 ...etcetera. */ ArrayList serialChars; // Temporary storage for received serial data int whichValueToAccum = 0; // Which piece of data am I currently collecting? boolean bJustBuilt = false; // Did I just finish collecting a datum? void processSerial() { while (myPort.available () > 0) {
char aChar = (char) myPort.read();
// You'll need to add a block like one of these
// if you want to add a 3rd sensor:
if (aChar == 'A') {
bJustBuilt = false;
whichValueToAccum = 0;
} else if (aChar == 'B') {
bJustBuilt = false;
whichValueToAccum = 1;
} else if (((aChar == 13) || (aChar == 10)) && (!bJustBuilt)) {
// If we just received a return or newline character, build the number:
int accum = 0;
int nChars = serialChars.size();
for (int i=0; i < nChars; i++) { int n = (nChars - i) - 1; int aDigit = ((Integer)(serialChars.get(i))).intValue(); accum += aDigit * (int)(pow(10, n)); } // Set the global variable to the number we captured. // You'll need to add another block like one of these // if you want to add a 3rd sensor: if (whichValueToAccum == 0) { valueA = accum; // println ("A = " + valueA); } else if (whichValueToAccum == 1) { valueB = accum; // println ("B = " + valueB); } // Now clear the accumulator serialChars.clear(); bJustBuilt = true; } else if ((aChar >= 48) && (aChar <= 57)) {
// If the char is between '0' and '9', save it.
int aDigit = (int)(aChar - '0');
serialChars.add(aDigit);
}
}
}
Here is the Arduino code:
/*
* Helicopter controller
* Author: Matthew Kellogg
* Date: October 22, 2014
*/
int ledPin = 5; // LED connected to digital pin 9
void setup() {
pinMode(ledPin, OUTPUT);
Serial.begin(9600);
}
static const int lowThresh = 200;
static const int hiThresh = 612;
void loop() {
int val = analogRead(0);
if (val > lowThresh && val < hiThresh){
analogWrite(ledPin, map(val, lowThresh, hiThresh, 0, 255));
//tone(11, map(val, lowThresh, hiThresh, 60, 4400));
Serial.print("A" + String((int)map(val, lowThresh, hiThresh, 100, 0)) + "\n");
} else {
analogWrite(ledPin, 0);
//noTone(11);
}
}