Arduino 10 : Arduino Pong 2

Arduino ProgrammingAs 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();
}

Leave a Reply

Your email address will not be published. Required fields are marked *