Arduino Ping Pong LCD Game Made Simple on Tinkercad

Arduino Ping Pong

Gaming is good way to learn coding. It makes learning an easy and fun path to learn, Arduino coding is no exception. That’s why we learn Arduino through making cool games.

Arduino UNO From Banggood 👇👇👇👇
https://www.banggood.in/custlink/vvDHa0trIB

This post is about the famous game the ping pong you can start making it on Arduino and 16×2 LCD and here is a simple simulation on Tinkercad.

First, we see the circuit components:

  • Arduino UNO From Banggood
  • 16×2 LCD Banggood
  • Reference Resistor 1 K Ohm
  • Resistor
  • Jumper wires

Circuit Connections:

Arduino UNO is connected to 16×2 LCD through four pins of data enable pin and register select connected to 11 and 12 read write contrast v node connected to ground vcc and ground of LCD connected to vcc and ground of Arduino the backlight led connected to positive and the negative terminal of backlight led connected to ground through one kilo ohm resistor and here are four push buttons used to control the game pull down to the ground through one kilo ohm resistor

Arduino UNO From Banggood 👇👇👇👇

https://www.banggood.in/custlink/vvDHa0trIB

Arduino Code:

We start by including the lcd library then defining the lcd pins then the push button pins and define the variables here is the class of the function of the pedal that used to control the function of the pedals.

Arduino UNO From Banggood 👇👇👇👇

https://www.banggood.in/custlink/vvDHa0trIB

\


#include <LiquidCrystal.h>
#include <avr/interrupt.h>

//----------------------------------------MACROS------------------------------------------

// Assigning LCD PINS
#define RS 12
#define EN 11
#define D4 6
#define D5 7
#define D6 8
#define D7 9

// Buttons that move the paddle
#define Player_1_moveDownButton 10   // Player 1's Down Button is connected to digital pin 4
#define Player_1_moveUpButton 5     // Player 1's Down Button is connected to digital pin 4
#define Player_2_moveDownButton 2   // Player 2's Down Button is connected to digital pin 4
#define Player_2_moveUpButton 3     // Player 2's Down Button is connected to digital pin 4

// Assigning Player Numbers
#define Player_1 1
#define Player_2 2

// Delay for updating the ball
#define DiagonalballUpdateTime 21
#define HorizontalballUpdateTime 15

//Starting Position of the Ball
#define Start_X 35
#define Start_Y 7

#define Button_Pressed (p1_UpButState | p1_DownButState | p2_UpButState | p2_DownButState)

void(* resetFunc) (void) = 0;       //declaring reset function at address 0

// Global Variables for Pin Change Interrupt Service Routine
volatile boolean x_Up = true;
volatile boolean x_Down = true;

LiquidCrystal lcd(RS, EN, D4, D5, D6, D7);    // Creating Object of type Liquid Crystal

//------------------------------------PADDLE CLASS------------------------------------------

class Paddle
{
  public:
  // Storing Value of each row of the Character Column for the Player's Paddle
  uint8_t PaddleColArray[16] = {0,0,0,0,0,4,4,4,0,0,0,0,0,0,0,0};
  
  uint8_t PaddlePos = 6;     // Recording Paddles's middle position as reference to move it
  uint8_t Score = 0;         // Score of each Player

  // When the Player presses the "UP" Button
  void MovePaddleUp()
  {
    // Make sure the paddle doesn't go off the board
    if(PaddlePos != 1)
    {
      PaddlePos--;
      PaddleColArray[PaddlePos+2]=0;
      PaddleColArray[PaddlePos-1]=4;
    }
  }

  // When the Player presses the "DOWN" Button
  void MovePaddleDown()
  {
    if(PaddlePos != 14)
    {
      PaddlePos++;
      PaddleColArray[PaddlePos-2]=0;
      PaddleColArray[PaddlePos+1]=4;
    }
  }

  // Printing Paddles using each Player's Number
  void PrintPaddles(uint8_t Player_Num)
  {
    if(Player_Num == 2)
    {
      // Each character must have a unique character ID and array to print
      lcd.createChar(0, PaddleColArray);
      lcd.createChar(1, PaddleColArray+8);

      // Move cursor to 15th character on the 1st row
      lcd.setCursor(14, 0);
      lcd.write(byte(0));

      // Move cursor to 15th character on the 1st row
      lcd.setCursor(14, 1);
      lcd.write(byte(1));
    }
  
    //if(Player_Num == 1)
    else
    {
      lcd.createChar(2, PaddleColArray);
      lcd.createChar(3, PaddleColArray+8);
  
      lcd.setCursor(1, 0);
      lcd.write(byte(2));

      lcd.setCursor(1, 1);
      lcd.write(byte(3));
    }
  }
};

// Creating Objects of Class "Paddle"
Paddle p1, p2;

//------------------------------------PRINT_GAME CLASS------------------------------------------

class Print_Game
{
  public:

  // Printing texts when the game starts
  void Start_Game()
  {
    lcd.print(F("   PING PONG "));
    
    //Set the Cursor at the starting of 2nd row
    lcd.setCursor(0, 1);
    lcd.print(F("PRESS A BUTTON"));

    // Variables to record the state of Push-Buttons
    uint8_t p1_UpButState = 0;
    uint8_t p1_DownButState = 0;
    uint8_t p2_UpButState = 0;
    uint8_t p2_DownButState = 0;
    
    // Waiting till any one of the button is pressed
    while(!(Button_Pressed))
    {
      // Low Level Code for digitalread() function for each input pin
      p1_UpButState = ((PIND & (1 << Player_1_moveUpButton)) );
      p1_DownButState = ((PINB & (1 << (Player_1_moveDownButton-8))) );
      p2_UpButState = ((PIND & (1 << Player_2_moveUpButton)) );
      p2_DownButState = ((PIND & (1 << Player_2_moveDownButton)) );
    }
    lcd.clear();        //Clearing LCD to start the game
  }

  // Printing score each time a player scores
  void Print_Score()
  {
    // Clearing the LCD to print scores
    lcd.clear();
    lcd.print(F("PLAYER1  PLAYER2"));
    lcd.setCursor(3 ,1);
    lcd.print(p1.Score);
    lcd.setCursor(12 ,1);
    lcd.print(p2.Score);

    // Scores remain on the display for 2 seconds
    delay(2000);

    // Clearing the display to continue the game
    lcd.clear();
  }

  // Printing the Winner on Display
  void Print_Winner(int Player_Num)
  {
    lcd.setCursor(0 ,0);
    lcd.print(F("   GAME  OVER   "));
    lcd.setCursor(1, 1);
    lcd.print(F("PLAYER "));
    lcd.print(Player_Num);
    lcd.setCursor(11 ,1);
    lcd.print(F("WINS"));

    // Text remains on screen for 5 seconds
    delay(5000);

    // Resetting the Game
    resetFunc();
  }
};

// Creating an Object of Print_Game Class
Print_Game g;

//------------------------------------BALL CLASS------------------------------------------

class Ball
{
  private:

  // Flag to reset the ball and paddles when a point is scored
  uint8_t Point_Scored = 0;
  
  // X and Y Direction Components of the Ball
  uint8_t ballYDir = 0;       // Ball starts off going horizontal
  uint8_t ballXDir = -1;      // Call starts off going left

  // Location of the Ball
  uint8_t ballY = Start_Y;
  uint8_t ballX = Start_X;

  // Row values of Character of the LCD in which the Ball is present
  uint8_t ballCharArray[8] = {0, 0, 0, 0, 0, 0, 0, 16};

  public:

  // Declaring member fucntions defined outside of class
  void GenerateBallArray();

  void PrintBall();

  void UpdateBall(uint8_t , uint8_t);

  void AwardAPoint();
};

//----------------------------Member Fuctions of Class BALL---------------------------------

// Generate the 8 values that make up the character to draw
void Ball :: GenerateBallArray()
{
  for(uint8_t i=0; i<8; i++)
    {
    if(i == (ballY % 8))
    {
      ballCharArray[i] = 2 << (4 - (ballX % 5));
    }
    else
    {
      ballCharArray[i] = 0;
    }
  }
}

void Ball :: PrintBall()
{
  // Calculate the column we will draw in
  uint8_t LCDCol = ballX / 5;

  // Either the top or bottom row
  uint8_t LCDRow = (ballY <= 7) ? 0 : 1;

  // Assign array to the charNum
  lcd.createChar(4, ballCharArray);

  // Move the cursor into position
  lcd.setCursor(LCDCol,LCDRow);

  // Draw the character
  lcd.write(byte(4));
}

// Updating the Ball's Position
void Ball :: UpdateBall(uint8_t P1_PaddlePos, uint8_t P2_PaddlePos)
{
  // Short wait before update
  // Handling different delays so that
  // ball has same speed when it's going diagonally
  if(ballYDir)          // When it's going Diagonally
  {
    delay(DiagonalballUpdateTime);
  }
  else                  // When it's going Horizontally
  {
    delay(HorizontalballUpdateTime);
  }

  //-------------CALCULATING BALL'S NEXT POSITION------------
  
  //If the Ball goes off-board
  if((ballX <= 6) || (ballX >= 73))
  {
    AwardAPoint();
  }

  //If Ball is at the Player 2 edge
  else if(ballX == 72)
  {
    // IF THE BALL STRIKES THE MIDDLE POSITION OF THE PADDLE
    if(ballY == P2_PaddlePos)
    {
      ballXDir = -1;
    }

    // IF THE BALL STRIKES THE BOTTOM POSITION OF THE PADDLE
    else if(ballY == (P2_PaddlePos + 1))
    {
      ballXDir = -1;
      if(ballY == 15)    // If the ball strikes the paddle at the bottom corner of the display
      {
        ballYDir = -1;
      }
      else
      {
        ballYDir = 1;
      }
    }

    // IF THE BALL STRIKES THE TOP POSITION OF THE PADDLE
    else if(ballY == (P2_PaddlePos - 1)){
      ballXDir = -1;
      if(ballY == 0)    // If the ball strikes the paddle at the upper corner of the display
      {
        ballYDir = 1;
      }
      else
      {
        ballYDir = -1;
      }
    }
  }

  //If Ball is at the Player 1 edge
  else if(ballX == 7)
  {
    // IF THE BALL STRIKES THE MIDDLE POSITION OF THE PADDLE
    if(ballY == P1_PaddlePos)
    {
      ballXDir = 1;
    }

    // IF THE BALL STRIKES THE BOTTOM POSITION OF THE PADDLE
    else if(ballY == (P1_PaddlePos + 1)){
      ballXDir = 1;
      if(ballY == 15)    // If the ball strikes the paddle at the bottom corner of the 
      {
        ballYDir = -1;
      }
      else
      {
        ballYDir = 1;
      }
    }

    // IF THE BALL STRIKES THE TOP POSITION OF THE PADDLE
    else if(ballY == (P1_PaddlePos - 1))
    { 
      ballXDir = 1;
      if(ballY == 0)    // If the ball strikes the paddle at the upper corner of the display
      {
        ballYDir = 1;
      }
      else
      {
        ballYDir = -1;
      }
    }
  }

  // If the Ball Hit top or bottom of display then change Y Direction
  else if((ballY == 0) || (ballY == 15))
  {
    ballYDir *= -1;
  }
    
  //Resetting the Ball and Paddles IF a point is scored
  if(Point_Scored == 1)
  {
    // Resetting the Ball
    ballX = Start_X;
    ballY = Start_Y;
    ballXDir *= -1;
    ballYDir = 0;

    // Resetting the Paddles
    p1.PaddlePos = 6;
    p2.PaddlePos = 6;
    
    for(uint8_t i=0; i<16; i++)
    {
      if((i==5) || (i==6) || (i==7))
      {
        p1.PaddleColArray[i] = 4;
        p2.PaddleColArray[i] = 4;
      }
      else
      {
        p1.PaddleColArray[i] = 0;
        p2.PaddleColArray[i] = 0;
      }
    }
    
    Point_Scored = 0;         //Resetting the Point_Scored Flag
  }

  //  Delete Ball from it's Current Location
  uint8_t LCDCol = ballX / 5;
  uint8_t LCDRow = (ballY <= 7) ? 0 : 1;
  lcd.setCursor(LCDCol, LCDRow);
  lcd.print(" ");
  
  // Change ball's position based on direction set on X & Y
  ballX += ballXDir;
  ballY += ballYDir;

  // Create the array for the ball character
  GenerateBallArray();

  // Printing ball after calculating the new location
  PrintBall();
}

// Increase player's score each time one scores
void Ball :: AwardAPoint()
{
  if(ballX <= 8)    // When Player 2 Scores
  {
    p2.Score++;
  }
    
  else              // When Player 2 Scores
  {
    p1.Score++;
  }
  
  // When one of the player reaches the winning score of 10
  
  if(p1.Score == 10)            // If Player 1 reaches 10 first
  {
    g.Print_Winner(Player_1);   // Printing Player 1 as Winner
  }

  else if(p2.Score == 10)       // If Player 2 reaches 10 first
  {
    g.Print_Winner(Player_2);   // Printing Player 2 as Winner
  }

   // Printing the Score of both players
  g.Print_Score();
    
  Point_Scored = 1;        // Setting the Flag to reset the locations of Paddles and Ball
}

// Creating an Object of class BALL
Ball b;

//---------------------------------------SETUP---------------------------------------------

void setup()
{
  // The display has 2 rows with 16 characters per row
  lcd.begin(16, 2);
  
  // Setup buttons so we can receive input
  DDRD &= ~(1<<Player_1_moveUpButton);
  // Low level version of the statement: pinMode( Player_1_moveUpButton, INPUT);
  // Similarly, setting all other pins to input
  DDRD &= ~(1<<Player_1_moveDownButton);
  DDRD &= ~(1<<Player_2_moveUpButton);
  DDRD &= ~(1<<Player_2_moveDownButton);
  
  // Printing the first statement
  g.Start_Game();
  
  // Print paddles on LCD
  p1.PrintPaddles(Player_1);
  p2.PrintPaddles(Player_2);
  
  // Print Ball on LCD
  b.PrintBall();

  // Enabling Pin Change Interrup for Player 1's "UP" Button
  PCMSK2 |= (1 << PCINT21);
  PCICR |= (1 << PCIE2);

  // Enabling Pin Change Interrup for Player 1's "DOWN" Button
  PCMSK0 |= (1 << PCINT2);
  PCICR |= (1 << PCIE0);

  // Enabling Global interrupts
  sei();
  
  //Player 2 will input through Interrupts
  attachInterrupt(digitalPinToInterrupt(Player_2_moveDownButton), P2_Move_Down, RISING);
  attachInterrupt(digitalPinToInterrupt(Player_2_moveUpButton), P2_Move_Up, RISING);
}

//---------------------------------------LOOP---------------------------------------------

void loop()
{
  //Printing Both Paddles
  p1.PrintPaddles(Player_1);
  p2.PrintPaddles(Player_2);
  
  //Updating Ball's Position
  b.UpdateBall(p1.PaddlePos, p2.PaddlePos);
}

//-----INTERRUPT SERVICE ROUTINES----FOR PLAYER 2--------------

void P2_Move_Down()
{
  p2.MovePaddleDown();
}

void P2_Move_Up()
{
  p2.MovePaddleUp();
}

//----PIN CHANGE INTERRUPT SERVICE ROUTINES----FOR PLAYER 1-----

// Interrupt Routine for Player 1 "UP" Button
ISR(PCINT2_vect)
{
  // The if statements make sure that interrupts does something
  // only on rising edge of the clock
  if(x_Up)
  {
    p1.MovePaddleUp();
    x_Up = false;
  }
  else
  {
    x_Up = true;
  }
}

// Interrupt Routine for Player 1 "DOWN" Button
ISR(PCINT0_vect)
{
  // The if statements make sure that interrupts does something
  // only on rising edge of the clock
  if(x_Down)
  {
    p1.MovePaddleDown();
    x_Down = false;
  }
  else
  {
    x_Down = true;
  }
}

Arduino Ping Pong Simulation

Arduino Ping Pong on Tinkercad. Here is the Simulation. You can try it right here.

Leave a Comment

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

Scroll to Top