When I first got the Tiny Video Game Kit at the San Mateo Maker Faire, I thought that pong would be perfect for a little handheld like this so after about a week of trial and error and countless bowls of ice cream, I created my version of pong called Ping.
////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////
////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////
//Copyright 2015 TheYoHanSolo
#include <TinyScreen.h>
#include <SPI.h>
#include <Wire.h>
#define BLACK 0x00
#define BLUE 0xE0
#define RED 0x03
#define GREEN 0x1C
#define WHITE 0xFF
#define GREY 0x6D
// Constant variables for the paddles
//
int kPaddleStep = 1;
int kLoopDelay = 0;
int kPaddleWidth = 2;
int kPaddleHeight = 15;
int kStickSensitivity = 100;
int kScreenTop = 0;
int kScreenLeft = 0;
int kScreenWidth = 96;
int kScreenHeight = 64;
int kScreenRight = kScreenWidth-1;
int kScreenBottom = kScreenHeight-1;
//Paddle Location
//
int kLPaddleX = 10;
int kRPaddleX = kScreenRight - kLPaddleX;
TinyScreen display = TinyScreen(0);
//Button Variables (Top 4 are x and y axis of joysticks)
int RX=0;
int RY=0;
int LX=0;
int LY=0;
byte leftButton=0;
byte rightButton=0;
//Paddle Variable Values
//
int paddleX = 10;
int lPaddleY = 10;
int rPaddleY = 10;
// Constant variables for the ball.
//
int kBallWidth = 4;
int kBallHeight = 4;
int ballX = 48;
int ballY = 32;
int bMovementX = 1;
int bMovementY = 1;
int kRight = 1;
int kLeft = -1;
//Score Values
int leftPlayerScore = 0;
int rightPlayerScore = 0;
//Goal Start and end positions for the 3 modes of pong
int kTennisGoalUpper = 0;
int kTennisGoalLower = 64;
int kHandballGoalUpper = 0;
int kHandballGoalLower = 64;
int kHockeyGoalUpper = 22;
int kHockeyGoalLower = 42;
//Three modes of pong
enum { kTennisMode = 0, kHandballMode, kHockeyMode , kMaxModes };
int mode = kTennisMode;
//Initial value of button before being pressed
int preLeftButton = 0;
int preRightButton = 0;
//Pausing booleans
bool prevPaused = false;
bool paused = false;
////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////
void setup(void)
{
// Initilize the WIRE interface used to talk to the graphics chip.
Wire.begin();
// Initialize the serial interface for output of debug strings.
Serial.begin(9600);
// Initialize the display library so we can draw.
display.begin();
// Select the font we will use for scoring and other messages.
display.setFont(liberationSans_8ptFontInfo);
// Default to displaying white text on a black background.
display.fontColor(WHITE,BLACK);
} // setup(void)
////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////
void loop()
{
// Read the paddle positions and buttons. Left paddle position goes into LX, LY.
// Right paddle possition goes into RX, RY.
//
getJoysticks();
//Serial.print( leftButton );
//Serial.print( preLeftButton);
//Serial.println();
//delay(500);
// Pause
// Pausing function of the left button(Is left button equal to the previous value?
if( leftButton && !preLeftButton)
{
// Yes, so make it equal its opposite
paused = !paused;
if( paused && !prevPaused )
{
// Sets color and location of the "PAUSED" label and resets the color back to default
display.fontColor(RED,BLACK);
display.setCursor(31, 25);
display.print("PAUSED");
display.fontColor(WHITE, BLACK);
}
//Resets when pushed again
if( !paused && prevPaused )
{
//Calls the clear screen function to wipe the screen of the "PAUSED"
clearScreen();
}
}
prevPaused = paused;
preLeftButton = leftButton;
//Tells program to halt
if( paused)
{
return;
}
//Asks if the right button has been pressed before and if it's something else
if( rightButton && !preRightButton)
{
//If the ball hits the goal, it stops and resets back to the center of the field and gets tossed to start the game
if(bMovementX == 0)
{
drawBall(ballX, ballY, kBallWidth, kBallHeight, BLACK);
ballX = 48;
ballY = 32;
bMovementX = -1;
bMovementY = 1;
//When the mode is handball, make sure the right score is reset every time it's selected
if( mode == kHandballMode)
{
rightPlayerScore = 0;
}
}
else
{
mode = mode+1;
//If the mode is called and the number is higher than the 3 modes, reset it back to tennis and start again
if( mode == kMaxModes)
{
mode = kTennisMode;
}
//Full reset, screen clear, score reset, ball reset, and movement reset
clearScreen();
leftPlayerScore = 0;
rightPlayerScore = 0;
ballX = 48;
ballY = 32;
bMovementX = 1;
bMovementY = 1;
}
}
preRightButton = rightButton;
// Update the Left Paddle with the new position.
//
if( mode != kHandballMode)
{
paddleUpdate(LY, kLPaddleX, lPaddleY);
}
// Update the Right Paddle with the new position.
//
paddleUpdate(RY, kRPaddleX, rPaddleY);
ballUpdate( ballX, ballY);
//Score Counter display on the screen
//
display.setCursor(60, 3);
display.print(rightPlayerScore);
if( mode != kHandballMode)
{
display.setCursor(36, 3);
display.print(leftPlayerScore);
}
displayField();
// Limit the animation speed.
//
delay(kLoopDelay);
} // loop()
////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////
//Function that clears the screen when called without and weird lines that show up without a delay
void clearScreen()
{
display.clearWindow(kScreenLeft, kScreenTop, kScreenWidth, kScreenHeight );
delayMicroseconds(50);
}
////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////
//This function draws the playing field based on which mode is selected
void displayField()
{
//Is it handball?
if( mode == kHandballMode)
{
//It's handball, so draw the a line on the top, bottom, and left sides of the screen
display.drawLine(kScreenLeft,kScreenTop,kScreenLeft,kScreenBottom,WHITE);
display.drawLine(kScreenLeft,kScreenTop,kScreenRight,kScreenTop,WHITE);
display.drawLine(kScreenLeft,kScreenBottom,kScreenRight,kScreenBottom,WHITE);
}
//Is it tennis?
if( mode == kTennisMode)
{
//Yes, so draw a top and bottom line and make a center field line
display.drawLine(kScreenLeft,kScreenTop,kScreenRight,kScreenTop,WHITE);
display.drawLine(kScreenLeft,kScreenBottom,kScreenRight,kScreenBottom,WHITE);
display.drawLine(kScreenWidth/2, kScreenTop, kScreenWidth/2, kScreenBottom, GREY);
}
//Is it Hockey?
if( mode == kHockeyMode)
{
//Yes so draw the goal lines, the center divide, and the top and bottom lines
display.drawLine(kScreenLeft,kScreenTop,kScreenRight,kScreenTop,WHITE);
display.drawLine(kScreenLeft,kScreenBottom,kScreenRight,kScreenBottom,WHITE);
display.drawLine(kScreenWidth/2, kScreenTop, kScreenWidth/2, kScreenBottom, GREY);
display.drawLine(kScreenLeft,kScreenTop,kScreenLeft,kScreenBottom/2 - 10,WHITE);
display.drawLine(kScreenLeft,kScreenBottom,kScreenLeft,kScreenBottom/2 + 10,WHITE);
display.drawLine(kScreenRight,kScreenTop,kScreenRight,kScreenBottom/2 - 10,WHITE);
display.drawLine(kScreenRight,kScreenBottom,kScreenRight,kScreenBottom/2 + 10,WHITE);
}
}
////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////
//This function determines the properties of the paddle and creates its movement
void paddleUpdate(int stickY, int positionX, int& positionY)
{
// Did the user move the stick enough to move the paddle?
//
if( stickY > kStickSensitivity )
{
// Yes, so update the position
//
positionY = positionY - kPaddleStep;
// Did the paddle fall off the top of the screen?
//
if( positionY < kScreenTop )
{
// Yes, so put it at the top of the screen.
//
positionY = kScreenTop;
}
// Nope, didn't fall off the top of the screen.
//
else
{
// Erase from the bottom of the paddle by how much we moved up
//
drawPaddle(positionX, positionY + kPaddleHeight, kPaddleWidth, kPaddleStep, BLACK);
}
} //if( stickY > kStickSensitivity )
// Did the user move the stick enough to move the paddle?
//
if( stickY < -kStickSensitivity )
{
//Is the paddle at the bottom of the screen?
//
if( positionY >= kScreenBottom - kPaddleHeight )
{
//Yes it is
positionY = kScreenBottom - kPaddleHeight;
}
//Draw over the old position of the paddle
else
{
drawPaddle(positionX, positionY, kPaddleWidth, kPaddleStep, BLACK);
positionY = positionY + kPaddleStep;
}
}
drawPaddle( positionX, positionY, kPaddleWidth, kPaddleHeight, WHITE);
} //paddleUpdate(void)
////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////
//Code for ball movement
//
void ballUpdate(int& positionX, int& positionY)
{
int upperGoal;
int lowerGoal;
//Draw the ball black
drawBall(positionX, positionY, kBallWidth, kBallHeight, BLACK);
//The movement on the screen
positionX = positionX + bMovementX;
positionY = positionY + bMovementY;
//The ball being drawn in white
drawBall(positionX, positionY, kBallWidth, kBallHeight, WHITE);
//Delay of one millisecond in the drawing
/*if( positionX = positionX - bMovementX + 1)
{
drawBall(positionX, positionY, kBallWidth, kBallHeight, BLACK);
}
if( positionX = positionX - bMovementX - 1)
{
drawBall(positionX, positionY, kBallWidth, kBallHeight, BLACK);
}
*/
//Is the ball at the right side of the screen?
//Will switch between what mode it is and determines the properties of the right wall
switch(mode)
{
//Tennis goal boundaries
case kTennisMode:
upperGoal = kTennisGoalUpper;
lowerGoal = kTennisGoalLower;
break;
//Handball goal boundaries
case kHandballMode:
upperGoal = kHandballGoalUpper;
lowerGoal = kHandballGoalLower;
break;
//Hockey goal boundaries
case kHockeyMode:
upperGoal = kHockeyGoalUpper;
lowerGoal = kHockeyGoalLower;
break;
}
//Is the ball at the right side of the screen?
if( positionX >= kScreenRight - kBallWidth )
{
//Is the ball in the goal area?
if( positionY > upperGoal && positionY + kBallHeight < lowerGoal )
{
//Is the mode handball and the ball is in the same position as the paddle?
if( mode != kHandballMode && bMovementX != 0)
{
//Yes, so increase the score
leftPlayerScore++;
}
//Stops the ball from moving after missing the paddle
bMovementX = 0;
bMovementY = 0;
}
//Reverses the movement
bMovementX = -bMovementX;
}
//Goal boundaries changes between games
switch(mode)
{
//Tennis goal boundaries
case kTennisMode:
upperGoal = kTennisGoalUpper;
lowerGoal = kTennisGoalLower;
break;
//Handball Goal boundaries
case kHandballMode:
upperGoal = kHandballGoalUpper;
lowerGoal = kHandballGoalLower;
break;
//Hockey goal boundaries
case kHockeyMode:
upperGoal = kHockeyGoalUpper;
lowerGoal = kHockeyGoalLower;
break;
}
//Is the ball at the left side of the screen?
if( positionX <= kScreenLeft)
{
//Is the ball within the goal boundaries?
if( positionY > upperGoal && positionY + kBallHeight < lowerGoal )
{
//Is the mode not handball?
if( mode != kHandballMode && bMovementX != 0)
{
//Then increase right player score
rightPlayerScore++;
}
//Is the mode not handball?
if( mode != kHandballMode)
{
//Then stop the ball from moving
bMovementX = 0;
bMovementY = 0;
}
}
//Yes, reverse direction and increase right's score.
bMovementX = -bMovementX;
}
// Collisions for the bottom
if( positionY >= kScreenBottom - kBallWidth)
{
bMovementY = -bMovementY;
}
//Collision detection for the top
if( positionY <= kScreenTop)
{
bMovementY = -bMovementY;
}
//Is the ball at the same row as the paddle?
if( mode != kHandballMode){
if( positionX == paddleX)
{
//Is it at the same position as the paddle?
if( positionY >= lPaddleY - kBallHeight/2 && positionY <= lPaddleY + kPaddleHeight - kBallHeight/2)
{
//Yes so change direction
bMovementX = -bMovementX;
}
}
}
//Is the ball at the same row as the paddle?
if( positionX == kScreenRight - paddleX)
{
//Is it at the same position as the paddle?
if( positionY >= rPaddleY - kBallHeight/2 && positionY <= rPaddleY + kPaddleHeight - kBallHeight/2)
{
//Yes so change direction
bMovementX = -bMovementX;
if(mode == kHandballMode)
{
rightPlayerScore++;
}
}
}
//Ball Drawing code for use on the screen
drawBall( ballX, ballY, kBallWidth, kBallHeight, WHITE);
} //void ballUpdate
////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////
//Code for drawing the basic shape of the paddle
void drawPaddle(int x, int y, int width, int height, byte color)
{
for(int i=0;i<=height;i++)
{
display.drawLine(x,y+i,x+width,y+i,color);
}
//delayMicroseconds(50);
} //void drawPaddle
////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////
//Code for drawing the basic shape of the ball
void drawBall(int x, int y, int width, int height, byte color)
{
for(int i=0;i<width;i++)
{
display.drawLine(x,y+i,x+width,y+i,color);
}
//delayMicroseconds(50);
}//void drawBall
////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////
//Joystick code for use by the paddles
void getJoysticks()
{
Wire.requestFrom(0x22,6);
int data[4];
for(int i=0;i<4;i++)
{
data[i]=Wire.read();
}
byte lsb=Wire.read();
byte buttons=~Wire.read();
leftButton=buttons&4;
rightButton=buttons&8;
for(int i=0;i<4;i++)
{
data[i]<<=2;
data[i]|= ((lsb>>(i*2))&3);
data[i]-=511;
}
RX=data[0];
RY=-data[1];
LX=-data[2];
LY=data[3];
}//void getJoysticks
Comments