Arduino Capacitive Touch Game
This is a game run through processing and controlled using inputs from the arduino. The input is the capacitive touch sensors made by drawing circles using a pencil on a piece of paper. The Arduino Code is implemented from an instructable (http://www.instructables.com/id/Turn-a-pencil-drawing-into-a-capacitive-sensor-for/). The processing code is implemented from a website on the internet, with a folder named "AGC". The background picture for the game, named 'cartoon.png', is from [https://sites.google.com/site/seourpicz/1/games/us/3/mario-world-2]. The picture of the coin, fire, and the sign is all drawn using the SketchBookExpress app on my laptop (idea from mario brothers and pacman). The songs played during the game is from Super Mario, and the website that I downloaded them from is [http://downloads.khinsider.com/game-soundtracks/album/super-mario-world-original-soundtrack].
The simple steps in making this game are:
1. Build your game controller.
2. Connect it to your Arduino and upload the Arduino Code to see if your game controller works successfully.
3. Download all the files that you need (pictures and music) in the data folder.
4. When copying the processing code, be sure to make 2 files with the [class Fire] file (02) and the main one (01).
5. Upload the Processing Code, and enjoy!
** I attached the data folder with the pictures and the music.
The simple steps in making this game are:
1. Build your game controller.
2. Connect it to your Arduino and upload the Arduino Code to see if your game controller works successfully.
3. Download all the files that you need (pictures and music) in the data folder.
4. When copying the processing code, be sure to make 2 files with the [class Fire] file (02) and the main one (01).
5. Upload the Processing Code, and enjoy!
** I attached the data folder with the pictures and the music.
data.zip | |
File Size: | 6495 kb |
File Type: | zip |
Arduino Code:
int capSensePinL = 6;
int capSensePinR = 11;
int capSensePinU = 8;
int capSensePinD = 13;
// how high the sensor needs to read in order to trigger a touch
int touchedCutoff = 60;
static unsigned long currentTime = 0;
void setup(){
Serial.begin(115200);
}
void loop(){
int capSenseL = readCapacitivePin(capSensePinL);
int capSenseR = readCapacitivePin(capSensePinR);
int capSenseU = readCapacitivePin(capSensePinU);
int capSenseD = readCapacitivePin(capSensePinD);
// Serial.print(capSenseL);
// Serial.print(", ");
// Serial.print(capSenseU);
// Serial.print(", ");
// Serial.print(capSenseR);
// Serial.print(", ");
// Serial.print(capSenseD);
// Serial.print(", ");
// Serial.println(" ");
if (abs (millis() - currentTime) > 100) {
if (capSenseL > 33) {
Serial.print("1, ");
}
else {
Serial.print("0, ");
}
if (capSenseR > 34){
Serial.print("1, ");
}
else{
Serial.print("0, ");
}
if (capSenseU > 31){
Serial.print("1, ");
}
else {
Serial.print("0, ");
}
if (capSenseD > 32){
Serial.print("1");
Serial.println("");
}
else{
Serial.print("0");
Serial.println("");
}
currentTime = millis();
}
}
// readCapacitivePin
// Input: Arduino pin number
// Output: A number, from 0 to 17 expressing
// how much capacitance is on the pin
// When you touch the pin, or whatever you have
// attached to it, the number will get higher
// In order for this to work now,
// The pin should have a 1+Megaohm resistor pulling
// it up to +5v.
uint8_t readCapacitivePin(int pinToMeasure){
// This is how you declare a variable which
// will hold the PORT, PIN, and DDR registers
// on an AVR
volatile uint8_t* port;
volatile uint8_t* ddr;
volatile uint8_t* pin;
// Here we translate the input pin number from
// Arduino pin number to the AVR PORT, PIN, DDR,
// and which bit of those registers we care about.
byte bitmask;
if ((pinToMeasure >= 0) && (pinToMeasure <= 7)){
port = &PORTD;
ddr = &DDRD;
bitmask = 1 << pinToMeasure;
pin = &PIND;
}
if ((pinToMeasure > 7) && (pinToMeasure <= 13)){
port = &PORTB;
ddr = &DDRB;
bitmask = 1 << (pinToMeasure - 8);
pin = &PINB;
}
if ((pinToMeasure > 13) && (pinToMeasure <= 19)){
port = &PORTC;
ddr = &DDRC;
bitmask = 1 << (pinToMeasure - 13);
pin = &PINC;
}
// Discharge the pin first by setting it low and output
*port &= ~(bitmask);
*ddr |= bitmask;
delay(1);
// Make the pin an input WITHOUT the internal pull-up on
*ddr &= ~(bitmask);
// Now see how long the pin to get pulled up
int cycles = 16000;
for(int i = 0; i < cycles; i++){
if (*pin & bitmask){
cycles = i;
break;
}
}
// Discharge the pin again by setting it low and output
// It's important to leave the pins low if you want to
// be able to touch more than 1 sensor at a time - if
// the sensor is left pulled high, when you touch
// two sensors, your body will transfer the charge between
// sensors.
*port &= ~(bitmask);
*ddr |= bitmask;
return cycles;
}
Processing Code 01: (actual file)
Fire [] en = new Fire[1000];
Fire [] po = new Fire[500];
import processing.serial.*;
Serial port;
import ddf.minim.*;
AudioPlayer player, player2, player3;
Minim minim, minim2, minim3;
PImage bg;
PImage coin;
PImage fire;
PImage sign;
int score = 0;
int t = 1000;
float ypos = 150;
float xpos = 250;
float speed = 2;
int n = 100;
float [] adata = new float[n];
float [] bdata = new float[n];
float [] cdata = new float[n];
float [] ddata = new float[n];
void setup() {
// list all the ports
// println(Serial.list());
// there are two places port.stop() is called (when you win and when you lose).
// in order to re-enable Serial communication, they need to be un-commented.
port = new Serial(this, Serial.list()[4], 115200);
port.bufferUntil('\n');
size(700,500);
// coins and fire
for (int i = 0; i < en.length; i++){
en[i] = new Fire(1);
}
for (int i = 0; i < po.length; i++){
po[i] = new Fire(2);
}
bg = loadImage("cartoon.png");
coin = loadImage("coin.png");
fire = loadImage("fire.png");
sign = loadImage("sign.png");
minim = new Minim(this);
minim2 = new Minim(this);
minim3 = new Minim(this);
player = minim.loadFile("themesong.mp3", 2048);
player2 = minim2.loadFile("game_over.mp3", 2048);
player3 = minim3.loadFile("win.mp3", 2048);
player.play();
}
void draw() {
background(bg);
enemyFlow();
pointFlow();
eating();
if (score == 10){
player.pause();
player3.play();
fill(0);
text("YOU WIN!!", 330, height/2);
noLoop();
port.stop();
}
}
void enemyFlow(){
int i = 0;
for(t=800; millis() > t; t=t+780){
en[i].appear(255,0);
en[i].collisionR();
i++;
}
}
void pointFlow(){
float r2=random(0,700);
int i = 0;
for(t=5000; millis() > t; t=t+5000){
po[i].appear(0,255);
i++;
}
}
void eating(){
imageMode(CENTER);
image(sign ,xpos, ypos);
if(ypos > height){
ypos = 0;
}
else if(ypos+30<0){
ypos=height;
}
if (xpos > width){
xpos = 0;
}
else if (xpos < 0){
xpos = width;
}
}
void keyPressed() { // if wanted play with j,l,i,k keys
switch(key) {
case 'j': xpos -= 15; break; // left
case 'l': xpos += 15; break; // right
case 'i': ypos -= 15; break; // up
case 'k': ypos += 15; break; // down
}
}
void serialEvent (Serial port){
String inString = port.readStringUntil('\n');
if (inString != null) {
inString = trim(inString);
println(inString);
try {
String[] data = split(inString, ",");
float a = float (data[0]);
float b = float (data[1]);
float c = float (data[2]);
float d = float (data[3]);
adata[0] = a; // left
bdata[0] = b; // right
cdata[0] = c; // down
ddata[0] = d; // up
if (a == 1){
xpos -= 15;
}
if (b == 1){
xpos += 15;
}
if (c == 1){
ypos -= 15;
}
if (d == 1){
ypos += 15;
}
redraw();
}
catch (Exception e) { e.printStackTrace(); }
}
}
Processing Code 02: (declares values, etc. for the actual game)
** be sure to add this file in the same folder as the prior processing code.
** be sure to add this file in the same folder as the prior processing code.
class Fire {
float yloc = 500;
float xloc = width;
float rcol;
float gcol;
float randomizer;
Fire(float tempXloc) {
randomizer = tempXloc;
if (randomizer == 1) {
xloc = random(0, 500);
}
else if (randomizer == 2) {
xloc = random(0, 500);
}
}
void appear(float tempRcol, float tempGcol) {
rcol =tempRcol;
gcol =tempGcol;
if (rectBall(xpos, ypos, 30, 30, xloc, yloc, 10)==true && rcol == 0) {
score++;
xloc = -20;
}
if (tempRcol == 255){
imageMode(CENTER);
image(fire, xloc, yloc) ;
}
else if (tempGcol == 255){
imageMode(CENTER);
image(coin, xloc, yloc) ;
}
yloc=yloc-speed;
text("Score:" +score, 20, 30);
}
void collisionR() {
if (rectBall(xpos, ypos, 30, 30, xloc, yloc, 10)==true) {
player.pause();
player2.play();
fill(0);
text("You Lost", 330, height/2);
noLoop();
port.stop();
}
}
boolean rectBall(float rx, float ry, float rw, float rh, float bx, float by, float d) {
// first test the edges (this is necessary if the rectangle is larger
// than the ball) - do this with the Pythagorean theorem
// if ball entire width position is between rect L/R sides
if (by+d/2 >= ry-rh/2 && by-d/2 <= ry+rh/2 && abs(rx-bx) <= d/2) {
return true;
}
// if not, check if ball's entire height is between top/bottom of the rect
else if (bx+d/2 >= rx-rw/2 && bx-d/2 <= rx+rw/2 && abs(ry-by) <= d/2) {
return true;
}
// if that doesn't return a hit, find closest corner
// this is really just a point, so we can test if we've hit it
// upper-left
float xDist = (rx-rw/2) - bx; // same as ball/ball, but first value defines point, not center
float yDist = (ry-rh/2) - by;
float shortestDist = sqrt((xDist*xDist) + (yDist * yDist));
// upper-right
xDist = (rx+rw/2) - bx;
yDist = (ry-rh/2) - by;
float distanceUR = sqrt((xDist*xDist) + (yDist * yDist));
if (distanceUR < shortestDist) { // if this new distance is shorter...
shortestDist = distanceUR; // ... update
}
// lower-right
xDist = (rx+rw/2) - bx;
yDist = (ry+rh/2) - by;
float distanceLR = sqrt((xDist*xDist) + (yDist * yDist));
if (distanceLR < shortestDist) {
shortestDist = distanceLR;
}
// lower-left
xDist = (rx-rw/2) - bx;
yDist = (ry+rh/2) - by;
float distanceLL = sqrt((xDist*xDist) + (yDist * yDist));
if (distanceLL < shortestDist) {
shortestDist = distanceLL;
}
// test for collision
if (shortestDist < d/2) { // if less than radius
return true; // return true
}
else { // otherwise, return false
return false;
}
}
}