Analog Pong is a simple demo I made at SparkFun to demonstrate how you might use different analog sensors to interface with Processing. Basically, you hook up any two analog sensors you want, and use them to control the paddles on the left and right of the screen. The nice thing about this project is that it provides a nice platform for introducing a lot of nice concepts: different kinds of analog sensors, how to communicate between Arduino and Processing, and a bit of Object Oriented Programming in Processing as well. Plus, every programmer should know how to program pong. If you’re not so sure how to hook up analog inputs to your Arduino, here’s a link. Note: Depending on the range of the analog sensors you’re using, you may have to adjust the code so the paddle covers the height of the screen. Hint: Take a close look at the Processing code for some fun easter eggs.
/* Analog Pong for Processing
* Reads in 2 sensor values connected to an Arduino for the paddle controls
* by Ben Leduc-Mills
* This code is public domain but you buy me a beer if you use this and we meet someday (Beerware license)
*/
import processing.serial.*; //import serial library
Serial myPort;
PFont myFont; //init font for text
Paddle leftPaddle, rightPaddle; //paddle objects
Ball ball; //ball object
int padWidth = 15;
int padHeight = 60;
int leftPaddlePos = 0;
int rightPaddlePos = 0;
int distWall = 15; //paddle distance from wall
float lpp; //scaled left paddle position
float rpp; //scaled right paddle position
int playerOne = 0; //player 1 score (left paddle)
int playerTwo = 0; //player 2 score (right paddle)
boolean oneWins = false;
boolean twoWins = false;
boolean firstContact = false; // Whether we've heard from the microcontroller
void setup() {
size(1000,600);
frameRate(20);
smooth();
rectMode(CENTER);
ellipseMode(CENTER);
myFont = createFont("FFScala", 16);
textFont(myFont);
println(Serial.list()); //list serial ports
myPort = new Serial(this, Serial.list()[0], 9600); //init serial object (picks 1st port available)
myPort.bufferUntil('\n');
leftPaddle = new Paddle(padWidth, padHeight, distWall, leftPaddlePos); //init right paddle (width, height, x, y)
rightPaddle = new Paddle(padWidth, padHeight, width - distWall, rightPaddlePos); //init left paddle
ball = new Ball(15, width/2, height/2, 8, 2, 1, 1);
//init ball (size, x, y, x speed, yspeed, x direction, y direction)
}
void draw() {
}
void serialEvent(Serial myPort) {
background(0);
showGUI(); //shows scores, etc. (see GUI tab)
ball.display();
ball.update();
String myString = myPort.readStringUntil('\n');
// if you got any bytes other than the linefeed:
if (myString != null) {
myString = trim(myString);
// if you haven't heard from the microncontroller yet, listen:
if (firstContact == false) {
if (myString.equals("hello")) {
myPort.clear(); // clear the serial port buffer
firstContact = true; // you've had first contact from the microcontroller
myPort.write('A'); // ask for more
println("contact");
}
}
// if you have heard from the microcontroller, proceed:
else {
// split the string at the commas
// and convert the sections into integers:
int sensors[] = int(split(myString, ','));
// print out the values you got:
// for (int sensorNum = 0; sensorNum < sensors.length; sensorNum++) {
// println("Sensor " + sensorNum + ": " + sensors[sensorNum]);
// }
if(sensors.length > 1) {
leftPaddlePos = sensors[0];
rightPaddlePos = sensors[1];
lpp = (float)leftPaddlePos;
rpp = (float)rightPaddlePos;
//println("right: " + rpp + " " + "left: " + lpp);
//println(lpp);
//adjust these mapping functions to fit the sensors values you're receiving
lpp = map(lpp, 0, 1023, 1, height);
rpp = map(rpp, 130, 270, 1, height); //flex sensor
leftPaddle.display(lpp);
rightPaddle.display(rpp);
}
// when you've parsed the data you have, ask for more:
myPort.write("A");
}
}
}
void keyPressed() {
ball.keyPressed();
}
//class for pong balls
class Ball {
int bSize; //ball size (yeah, yeah, I know)
float xpos, ypos; //starting position in X and Y
float xspeed, yspeed; //speed
float xdir, ydir; //direction
//Constructor
Ball(int ibSize, float ixpos, float iypos, float ixspeed, float iyspeed, float ixdir, float iydir) {
bSize = ibSize;
xpos = ixpos;
ypos = iypos;
xspeed = ixspeed;
yspeed = iyspeed;
xdir = ixdir;
ydir = iydir;
}
void update() {
//move
xpos = xpos + (xspeed * xdir);
ypos = ypos + (yspeed * ydir);
//if hits paddle, change direction
if ((xpos > rightPaddle.xPos - bSize && xpos < width - bSize &&
ypos < rpp + rightPaddle.pHeight/2 - bSize &&
ypos > rpp - rightPaddle.pHeight/2 - bSize) ||
(xpos < leftPaddle.xPos + bSize && xpos < 0 + bSize &&
ypos < lpp + leftPaddle.pHeight/2 - bSize &&
ypos > lpp - leftPaddle.pHeight/2 - bSize) )
{
xdir *= -1; //change ball direction
}
if (xpos > width-bSize) { //player one scored
playerOne++;
if (playerOne < 7) {
//println(playerOne);
reset();
}
else if (playerOne == 7) { //player one won
oneWins = true;
xpos = width/2;
ypos = height/2;
xdir = 0;
ydir = 0;
}
}
if(xpos < 0) { //player two scored
playerTwo++; //player two gets a point
if(playerTwo < 7) {
//println(playerTwo);
reset();
}
else if (playerTwo == 7) { //player 2 won
twoWins = true;
xpos = width/2;
ypos = height/2;
xdir = 0;
ydir = 0;
}
}
if(ypos > height-bSize || ypos < 0) { //ball hits top or bottom
ydir *= -1;
}
}
void display() {
//draw ball to screen
ellipse(xpos+bSize/2, ypos+bSize/2, bSize, bSize);
}
void reset() {
//reset for a new game
xpos = width/2;
ypos = height/2;
ydir = random(-1, 1);
float dir = random(-1, 1);
if (dir > 0) {
xdir = 1;
}
else if (dir <= 0) {
xdir = -1;
}
}
void keyPressed() {
//key presses
if(keyCode=='R') {
newGame();
}
if(keyCode=='1') {
bSize++;
}
if(keyCode=='2') {
bSize--;
}
if(keyCode=='3') {
leftPaddle.pHeight++;
}
if(keyCode=='4') {
leftPaddle.pHeight--;
}
if(keyCode=='5') {
rightPaddle.pHeight++;
}
if(keyCode=='6') {
rightPaddle.pHeight--;
}
}
void newGame() {
playerOne = 0;
playerTwo = 0;
oneWins = false;
twoWins = false;
reset();
}
}
//class for paddles
class Paddle {
float pWidth;
float pHeight;
float yPos;
float xPos;
Paddle(float ipWidth, float ipHeight, float ixPos, float iyPos) {
pWidth = ipWidth;
pHeight = ipHeight;
xPos = ixPos;
yPos = iyPos;
}
void display(float yPos) {
noStroke();
fill(255);
rect(xPos, yPos, pWidth, pHeight);
}
}
//game text
void showGUI() {
stroke(255);
line(width/2, 0, width/2, height);
textSize(16);
text("Player 1: " + playerOne, 100, 20); // player 1 score display
text("Player 2: " + playerTwo, 600, 20); // play 2 score display
if (oneWins == true) { //if player one wins
textSize(24);
text("Player 1 WINS!!!", width/2 - 95, height/2 -50);
text("Press 'R' for New Game", width/2 - 135, height/2 - 20);
}
if (twoWins == true) { //if player two wins
textSize(24);
text("Player 2 WINS!!!", width/2 - 95, height/2-50);
text("Press 'R' for New Game", width/2 - 135, height/2 - 20);
}
}
And here’s the Arduino Code:
/*
* AnalogPong for Arduino
* Reads in 2 sensor values as paddles to send to a processing sketch
* by Ben Leduc-Mills
* This code is public domain but you buy me a beer if you use this and we meet someday (Beerware license)
*/
void setup() {
Serial.begin(9600);
establishContact();
}
void loop() {
if (Serial.available() > 0) {
// read the incoming byte:
int inByte = Serial.read();
int leftPaddle = analogRead(A0);
Serial.print(leftPaddle, DEC);
Serial.print(",");
int rightPaddle = analogRead(A1);
Serial.println(rightPaddle, DEC);
// Serial.println(rightPaddle, DEC);
//Serial.println(leftPaddle, DEC);
}
}
void establishContact() {
while (Serial.available() <= 0) {
Serial.println("hello"); // send a starting message
delay(300);
}
}