As we continue making Pong with an Arduino, I now create the code to make the ball handle collision detection, movement and way much more. We are building Pong on a constrained piece of hardware using raw binary bits and clever algorithms. Like before I’ll live code everything with very little preparation so that you can see my thinking process. All of the heavily commented code follows below.
If you like videos like this, consider donating $1, or simply turn off Ad Blocking software. Either helps me to afford components and the books I need to make these free tutorials.
Code from the Tutorial
#include <LiquidCrystal.h>
LiquidCrystal lcd(12, 11, 5, 4, 3, 2);
// ---------- NEW ----------
// Stores the whole gameBoard
byte gameBoard[16][80] = {};
// Delay for updating the ball
int ballUpdateTime = 100;
// Ball starts off going horizontal
byte ballXDir = 0;
// Call starts off going left
byte ballYDir = -1;
// Starting ball x/y for storing in gameBoard
byte ballX = 7;
byte ballY = 35;
// Temporarily stores character created for drawing
// the ball before drawing to LCD
byte ballCharArray[8] = {};
// Stores scores
byte playerScore = 0;
byte aiScore = 0;
// Holds middle pixel for each paddle
// Hit above go up / below go down / center go straight
byte aiPaddlePos = 8;
byte myPaddlePos = 8;
// Ball steps
// 1. Ball starts moving to the right
// 2. With each move
// A. Delete previous space
// B. Light new space
// C. Check if paddle is in new space
// i. Check if ball hits PaddlePos, above PaddlePos, or below Paddle Pos
// ii. Change ballXDir & ballYDir accordingly
// a. PaddlePos : -XDir, YDir = 0
// b. PaddlePos - 1 : -XDir, YDir - 1
// c. PaddlePos + 1 : -XDir, YDir + 1
// iii. Clear old ball
// D. Check if top wall is hit
// i. XDir not changed, YDir - 1
// ii. Clear old ball
// E. Check if bottom wall is hit
// i. XDir not changed, YDir + 1
// ii. Clear old ball
// D. Check if passes paddle
// i. Change score
// ii. Play sound
// iii. Draw ball in center of board
// iv. Clear old ball
// v. YDir = 0, XDir = -XDir
int GetLEDRowValue(byte ledRow, byte maxColumn){
// The starting column to create
int minColumn = maxColumn - 4;
// Stores the base 10 value representing the binary
// value which defines what lights to turn on
int ledValue = 0;
// Multiplies values by 16, 8, 4, 2, 1
int multiplier = 1;
// Cycle through binary values while multiplying
// to create the base 10 value
for(int i = maxColumn; i >= minColumn; i--){
ledValue += (gameBoard[ledRow][i] * multiplier);
multiplier *= 2;
}
return ledValue;
}
// Generate the 8 values that make up the character to draw
void GenerateBallArray(){
// The max column to use when forming the character using
// data in the gameBoard array
byte maxCol = ((ballY / 5) * 5) + 4;
byte minCol = maxCol - 4;
// 0 for top LCD row and 8 for bottom
byte startRow = (ballX <= 7) ? 0 : 8;
// Get last row value
byte endRow = startRow + 8;
// Get values in gameBoard and create new array with
// just the balls character array
if(startRow == 0){
for(int i = startRow; i < endRow; i++){
ballCharArray[i] = GetLEDRowValue(i, maxCol);
}
} else {
for(int i = startRow; i < endRow; i++){
ballCharArray[i-8] = GetLEDRowValue(i, maxCol);
}
}
}
byte charNum = 0;
void PrintBall(){
// Calculate the column we will draw in
byte LCDCol = ballY / 5;
// Either the top or bottom row
byte LCDRow = (ballX <= 7) ? 0 : 1;
// Character number to associate with the character
charNum = ballY / 5;
/*
for(int i = 0; i < 8; i++){
Serial.print(ballCharArray[i]);
Serial.print(" ");
}
Serial.println("\n");
*/
// Assign array to the charNum
lcd.createChar(charNum, ballCharArray);
// Move the cursor into position
lcd.setCursor(LCDCol,LCDRow);
/*
Serial.print("charNum ");
Serial.println(charNum);
Serial.print("Printing to Column ");
Serial.println(LCDCol);
Serial.print("Printing to Row ");
Serial.println(LCDRow);
*/
// Draw the character
lcd.write(byte(charNum));
}
// Start at X: 7 Y: 35
void SetupBall(){
// Send ball in opposite direction
ballYDir *= -1;
// Put ball on the gameboard
gameBoard[7][35] = true;
}
void AwardAPoint(){
if(ballY <= 8){
playerScore++;
} else {
aiScore++;
}
delay(100);
// Send ball toward other player
ballYDir *= -1;
}
void UpdateBall(){
// Short wait before update
delay(ballUpdateTime);
if((ballY <= 8) || (ballY >= 71)){
AwardAPoint();
} else if((ballX == 0) || (ballX == 15)){
// Hit top or bottom of screen and change Y Direction
ballXDir *= -1;
} else if((ballY == 69) && (ballX == myPaddlePos)){
// If hit players paddle in middle
Serial.println("MIDDLE\n");
ballYDir *= -1;
} else if((ballY == 69) && (ballX == (myPaddlePos + 1))){
// If hit players paddle on bottom
Serial.println("BOTTOM\n");
ballYDir *= -1;
ballXDir = 1;
} else if((ballY == 69) && (ballX == (myPaddlePos - 1))){
// If hit players paddle on top
Serial.println("TOP\n");
ballYDir *= -1;
ballXDir = -1;
}
// Delete last ball position and add new 1 to the gameboard
gameBoard[ballX][ballY] = false;
// Increase ball direction based on direction set on X & Y
ballX += ballXDir;
ballY += ballYDir;
// Set new position as true
gameBoard[ballX][ballY] = true;
// Create the array for the ball character
GenerateBallArray();
// Clears whole LCD
lcd.clear();
PrintBall();
}
void setup() {
Serial.begin(9600);
lcd.begin(16, 2);
SetupBall();
GenerateBallArray();
PrintBall();
}
void loop() {
UpdateBall();
}