Physical Interaction Design Using Processing |
|||
PongHere's the code from my Pong game. Here's the Basic Stamp 2 code. My sensors read in a range from about 0 - 12800, so I divided by 64 to get them into a byte range (0-255). inByte VAR BYTE leftVar VAR WORD leftByte VAR BYTE rightVar VAR WORD rightByte VAR BYTE main: ' wait for data in at 9600 bps: SERIN 16, 16468, [inByte] ' raise both sensor inputs high so we can run RCTIME: HIGH 12 HIGH 13 ' pause 1 ms to let pins stabilize: PAUSE 1 ' read inputs into variables: RCTIME 12, 1, leftVar RCTIME 13, 1, rightVar ' convert variables into bytes: leftByte = leftVar/64 rightByte = rightVar/64 ' send data out: SEROUT 16, 16468, [leftByte, rightByte] GOTO main Here's the Processing code. Note that you'll need a font. You can make your own font in Processing from the Tools menu and replace the one I used with your own. You'll also need to pick the appropriate serial port, but the setup() method prints a list of the serial ports, so you can figure out which one is the one you want.
/*
Physical Pong
for Processing v.91
by Tom Igoe
Takes values on from the serial port to move the paddles
in a pong game. Score goes to 11.
Space bar toggles calibration mode.
In calibration mode, hit enter to move through the upper and lower
extremes of the paddles
Updated 5 July 2005
*/
import processing.serial.*;
int bgcolor; // Background color
int fgcolor; // Fill color
Serial port; // The serial port
int[] serialInArray = new int[3]; // Where we'll put what we receive
int serialCount = 0; // A count of how many bytes we receive
float xpos, ypos; // Starting position of the ball
boolean firstContact = false; // Whether we've heard from the microcontroller
int leftPaddle = 0; // x position of the left paddle
int rightPaddle = 0; // x position of the right paddle
int signalByte = 255; // first byte we get in from the microcontroller
int ballSize = 20; // size of the ball
int paddleSize = 50; // height of the paddle
int xDirection = 2; // number of pixels to move horizontally each move
int yDirection = 1; // number of pixels to move vertically each move
int paddleMargin = 20; // space from the paddle to the edge of the screen
int leftScore = 0;
int rightScore = 0;
boolean gameOver = true;
int countDownTime = 100; // time before ball moves after each point, in millis
int countDown = countDownTime; // countdown timer
boolean calibrate = false; // calibrate mode flag
int calibrateStep = 0; // where we are in the calibration routine
int leftBottom; // highest value of the left sensor
int rightBottom; // highest value of the right sensor
int leftTop = 0; // lowest value of the left sensor
int rightTop = 0; // lowest value of the right sensor
int leftNumber = 0; // rav value incoming from left sensor
int rightNumber = 0; // rav value incoming from right sensor
int thisDigit = 0; // I think this relates to the digit drawing routines, but I can't remember
PFont myFont; // text font
void setup() {
size(400, 300); // Window size
noStroke(); // No border on the next thing drawn
// Set the starting position of the ball (middle of the stage)
resetBall();
// set starting limits of paddles:
leftBottom = height;
rightBottom = height;
// Print a list of the serial ports, for debugging purposes:
println(Serial.list());
// initialize font:
myFont = loadFont("GillSans-14.vlw");
textFont(myFont, 14);
// I know that the third port in the serial list on my mac
// is always my Keyspan adaptor, so I open Serial.list()[2].
// Open whatever port is the one you're using.
port = new Serial(this, Serial.list()[2], 9600);
port.write(signalByte); // Send a byte to start the microcontroller sending
}
void draw() {
// draw the background:
background(0);
// color for the foreground:
fill(255);
// draw the paddles:
drawPaddles();
// Get any new serial data
if (port.available() > 0) {
serialEvent();
// Note that have we heard from the microntroller at least once:
firstContact = true;
}
// If there's no initial serial data, send again until we get some.
// (in case you tend to start Processing before you start your
// external device):
if (firstContact == false) {
delay(300);
port.write(signalByte);
}
// if we're in the calibration routine, do it:
if (calibrate) {
doCalibrate();
}
// if we're not in the calibration routine, play the game:
else {
// if the game's not over, keep the ball moving:
if (!gameOver) {
animateBall();
checkScore();
}
else {
// draw the "GAME OVER":
drawGameOver(width/2 - 155, height/2 -15);
}
}
}
/*
This method draws the paddles in their current position based on
the latest serial data.
*/
void drawPaddles() {
// draw paddles:
rect(paddleMargin, leftPaddle, 10, paddleSize);
rect(width-(paddleMargin+10), rightPaddle, 10, paddleSize);
// draw the center line:
for (int y=0; y<=height; y=y+(paddleSize + 15)) {
rect((width/2)-5, y, 10, paddleSize);
}
}
/*
This routine moves the ball, and keeps track of whether it's
hit a paddle or a border. The paddle check could use some work.
*/
void animateBall() {
// ball control:
// if the ball is moving left:
if (xDirection < 0) {
if ((xpos <= paddleMargin)) {
if ((leftPaddle <= ypos) && (ypos <= leftPaddle + paddleSize)) {
// reverse the horizontal direction:
xDirection =-xDirection;
}
}
}
// if the ball is moving right:
else {
if ((xpos >= (width - (paddleMargin + ballSize/2)))) {
if ((rightPaddle <= ypos) && (ypos <= rightPaddle + paddleSize)) {
// reverse the horizontal direction:
xDirection =-xDirection;
}
}
}
if (xpos > width) {
// right loses a point
leftScore++;
// make up a random number to change the speed of the ball:
float someNum = random(2) + 1;
xDirection = (int)-someNum;
//set the ball back to the center:
resetBall();
}
if (xpos < 0) {
// left loses a point
rightScore++;
// make up a random number to change the speed of the ball:
float someNum = random(2) + 1;
xDirection = (int)someNum;
//set the ball back to the center:
resetBall();
}
// stop the ball going off the top or the bottom of the screen:
if ((ypos - ballSize/2 <= 0) || (ypos +ballSize/2 >=height)) {
// reverse the y direction of the ball:
yDirection = -yDirection;
}
if (countDown >= 0) {
// count down 2 seconds before starting a new ball:
delay(10);
countDown--;
}
else {
// move that ball!
xpos = xpos + xDirection;
ypos = ypos + yDirection;
}
// display the score:
showScores();
// Draw the ball
rect(xpos, ypos, ballSize, ballSize);
}
/*
Keystrokes activate secondary functions:
*/
void keyReleased() {
int thisKey = keyCode;
switch (thisKey) {
// tab key resets game:
case 9:
resetGame();
break;
// return key toggles calibrate mode steps:
case 10:
if (calibrate) {
calibrateStep++;
}
break;
// spacebar enters and exits calibrate mode:
case 32:
calibrateStep = 0;
calibrate = !calibrate;
break;
// case
default:
println(thisKey);
}
thisKey = 0;
}
void checkScore() {
if (leftScore >= 11) {
gameOver = true;
}
if (rightScore >= 11) {
gameOver = true;
}
}
/*
This method calibrates the paddles so that
their ranges match the vertical height of the window
*/
void doCalibrate() {
// display top and bottom values:
text(leftBottom, 10, height-20);
text(rightBottom, width-50, height-20);
text(leftTop, 10, 20);
text(rightTop, width-50, 20);
// depending on which step we are on, reset the appropriate paddle limit:
switch (calibrateStep) {
case 0:
text("move left paddle to lowest point and press enter.", 10, 10);
leftBottom = leftNumber;
break;
case 1:
text("move left paddle to highest point and press enter.", 10, 10);
leftTop = leftNumber;
break;
case 2:
text("move right paddle to lowest point and press enter.", 10, 10);
rightBottom = rightNumber;
text(leftBottom, 10, height-20);
text(rightBottom, width-50, height-20);
break;
case 3:
text("move left paddle to highest point and press enter.", 10, 10);
rightTop = rightNumber;
break;
case 4:
// leave the calibration routine:
calibrate = false;
default:
}
}
/*
This method reads the values in from the serial port
and sets the paddle values based on what it reads.
*/
void serialEvent() {
// Add the latest byte from the serial port to array:
serialInArray[serialCount] = port.read();
serialCount++;
// If we have 3 bytes, read them:
if (serialCount > 2 ) {
signalByte = serialInArray[0];
leftNumber = serialInArray[1];
rightNumber = serialInArray[2];
// if we're not in the calibrate routine, use the values to move the paddles:
if (!calibrate) {
leftPaddle = ((leftNumber - leftTop) * height/(leftBottom - leftTop)) - paddleSize ;
rightPaddle = ((rightNumber - rightTop) * height/(rightBottom - rightTop)) - paddleSize;
}
// Send a byte to request new sensor readings:
port.write(signalByte);
// Reset serialCount:
serialCount = 0;
}
}
/*
This method resets the ball.
*/
void resetBall() {
xpos = width / 2 - (ballSize/2);
ypos = height / 2 - (ballSize/2);
countDown = countDownTime;
}
/*
This method displays the scores:
*/
void showScores() {
makeDigit(leftScore % 10, width/2 - 40, 10);
if (leftScore > 9) {
makeDigit(leftScore / 10, width/2 - 60, 10);
}
makeDigit(rightScore % 10, width/2 + 60, 10);
if (rightScore > 9) {
makeDigit(rightScore / 10, width/2 + 40, 10);
}
}
/*
This method draws the digits because
there isn't a Pong font.
*/
void makeDigit(int numeral, int left, int top) {
switch (numeral) {
case 1:
rect(left, top, 5, 35);
break;
case 2:
// top
rect(left, top, 20, 5);
// right vertical
rect(left + 15, top, 5, 15);
//center
rect(left, top+15, 20, 5);
// left vertical
rect(left, top+15, 5, 15);
// bottom
rect(left, top+30, 20, 5);
break;
case 3:
// top
rect(left, top, 20, 5);
// right vertical
rect(left + 15, top, 5, 30);
//center
rect(left, top+15, 20, 5);
// left vertical
// rect(left, top+15, 5, 15);
// bottom
rect(left, top+30, 20, 5);
break;
case 4:
// top
//rect(left, top, 20, 5);
// right vertical
rect(left + 15, top, 5, 35);
//center
rect(left, top+15, 20, 5);
// left vertical
rect(left, top, 5, 15);
// bottom
//rect(left, top+30, 20, 5);
break;
case 5:
// top
rect(left, top, 20, 5);
// right vertical
rect(left+15, top+15, 5, 15);
//center
rect(left, top+15, 20, 5);
// left vertical
rect(left, top, 5, 15);
// bottom
rect(left, top+30, 20, 5);
break;
case 6:
// top
rect(left, top, 20, 5);
// right vertical
rect(left + 15, top+15, 5, 15);
//center
rect(left, top+15, 20, 5);
// left vertical
rect(left, top, 5, 30);
// bottom
rect(left, top+30, 20, 5);
break;
case 7:
// top
rect(left, top, 20, 5);
// right vertical
rect(left + 15, top, 5, 35);
//center
// rect(left, top+15, 20, 5);
// left vertical
// rect(left, top+15, 5, 15);
// bottom
// rect(left, top+30, 20, 5);
break;
case 8:
// top
rect(left, top, 20, 5);
// right vertical
rect(left + 15, top, 5, 30);
//center
rect(left, top+15, 20, 5);
// left vertical
rect(left, top, 5, 30);
// bottom
rect(left, top+30, 20, 5);
break;
case 9:
// top
rect(left, top, 20, 5);
// right vertical
rect(left + 15, top, 5, 30);
//center
rect(left, top+15, 20, 5);
// left vertical
rect(left, top, 5, 15);
// bottom
rect(left, top+30, 20, 5);
break;
case 0:
// top
rect(left, top, 20, 5);
// right vertical
rect(left+15, top, 5, 30);
//center
//rect(left, top+15, 20, 5);
// left vertical
rect(left, top, 5, 30);
// bottom
rect(left, top+30, 20, 5);
break;
// default:
}
}
/*
This method draws GAME OVER because there isn't a Pong font:
*/
void drawGameOver(int x, int y) {
int left = x;
int top = y;
//G
// line 1
rect(left, top, 25, 5);
// line 2
rect(left, top+5, 5, 5);
// line 3
rect(left, top+10, 5, 5);
// line 4
rect(left, top+15, 5, 5);
rect(left+15, top+15, 10, 5);
// line 5
rect(left, top+20, 5, 5);
rect(left+20, top+20, 5, 5);
// line 6
rect(left, top+25, 5, 5);
rect(left+20, top+25, 5, 5);
// line 7
rect(left, top+30, 25, 5);
left += 35;
//A
// line 1
rect(left+10, top, 5, 5);
// line 2
rect(left+5, top+5, 5, 5);
rect(left+15, top+5, 5, 5);
// line 3
rect(left, top+10, 5, 5);
rect(left+20, top+10, 5, 5);
// line 4
rect(left, top+15, 5, 5);
rect(left+20, top+15, 5, 5);
// line 5
rect(left, top+20, 25, 5);
// rect(left+20, top+20, 5, 5);
// line 6
rect(left, top+25, 5, 5);
rect(left+20, top+25, 5, 5);
// line 7
rect(left, top+30, 5, 5);
rect(left+20, top+30, 5, 5);
left += 35;
//M
// line 1
rect(left, top, 5, 5);
rect(left+20, top, 5, 5);
// line 2
rect(left, top+5, 10, 5);
rect(left+15, top+5, 10, 5);
// line 3
rect(left, top+10, 5, 5);
rect(left+10, top+10, 5, 5);
rect(left+20, top+10, 5, 5);
// line 4
rect(left, top+15, 5, 5);
rect(left+20, top+15, 5, 5);
// line 5
rect(left, top+20, 5, 5);
rect(left+20, top+20, 5, 5);
// line 6
rect(left, top+25, 5, 5);
rect(left+20, top+25, 5, 5);
// line 7
rect(left, top+30, 5, 5);
rect(left+20, top+30, 5, 5);
left += 35;
//E
// line 1
rect(left, top, 25, 5);
// line 2
rect(left, top+5, 5, 5);
// line 3
rect(left, top+10, 5, 5);
// line 4
rect(left, top+15, 25, 5);
// rect(left+15, top+15, 10, 5);
// line 5
rect(left, top+20, 5, 5);
// rect(left+20, top+20, 5, 5);
// line 6
rect(left, top+25, 5, 5);
// rect(left+20, top+25, 5, 5);
// line 7
rect(left, top+30, 25, 5);
left += 70;
//O
// line 1
rect(left+5, top, 20, 5);
// line 2
rect(left, top+5, 5, 5);
rect(left+25, top+5, 5, 5);
// line 3
rect(left, top+10, 5, 5);
rect(left+25, top+10, 5, 5);
// line 4
rect(left, top+15, 5, 5);
rect(left+25, top+15, 5, 5);
// line 5
rect(left, top+20, 5, 5);
rect(left+25, top+20, 5, 5);
// line 6
rect(left, top+25, 5, 5);
rect(left+25, top+25, 5, 5);
// line 7
rect(left+5, top+30, 20, 5);
left += 35;
//V
// line 1
rect(left, top, 5, 5);
rect(left+20, top, 5, 5);
// line 2
rect(left, top+5, 5, 5);
rect(left+20, top+5, 5, 5);
// line 3
rect(left, top+10, 5, 5);
rect(left+20, top+10, 5, 5);
// line 4
rect(left, top+15, 5, 5);
rect(left+20, top+15, 5, 5);
// line 5
rect(left, top+20, 5, 5);
rect(left+20, top+20, 5, 5);
// line 6
rect(left+5, top+25, 5, 5);
rect(left+15, top+25, 5, 5);
// line 7
rect(left+10, top+30, 5, 5);
left += 35;
//E
// line 1
rect(left, top, 25, 5);
// line 2
rect(left, top+5, 5, 5);
// line 3
rect(left, top+10, 5, 5);
// line 4
rect(left, top+15, 25, 5);
// rect(left+15, top+15, 10, 5);
// line 5
rect(left, top+20, 5, 5);
// rect(left+20, top+20, 5, 5);
// line 6
rect(left, top+25, 5, 5);
// rect(left+20, top+25, 5, 5);
// line 7
rect(left, top+30, 25, 5);
left += 35;
//R
// line 1
rect(left, top, 20, 5);
// line 2
rect(left, top+5, 5, 5);
rect(left+20, top+5, 5, 5);
// line 3
rect(left, top+10, 5, 5);
rect(left+20, top+10, 5, 5);
// line 4
rect(left, top+15, 20, 5);
// rect(left+15, top+15, 10, 5);
// line 5
rect(left, top+20, 5, 5);
rect(left+10, top+20, 5, 5);
// line 6
rect(left, top+25, 5, 5);
rect(left+15, top+25, 5, 5);
// line 7
rect(left, top+30, 5, 5);
rect(left+20, top+30, 5, 5);
}
/*
This method resets the game:
*/
void resetGame() {
leftScore = 0;
rightScore = 0;
gameOver = false;
resetBall();
}
|
|||