Ping-Pong-Spiel

In diesem Projekt erfahren Sie, wie Sie ein Ping-Pong-Spiel auf Arduino erstellen. Sie können mit zwei Spielern spielen. Jeder Spieler hat zwei Tasten: eine zum Bewegen des Schlägers nach oben und eine zum Bewegen nach unten.

Ein Punkt wird erzielt, wenn der Gegner den Ball nicht zurückschlägt. Der erste Spieler, der 10 Punkte erzielt, gewinnt.

Schwierigkeit:

Notwendiges Material

Sehen wir uns nun die für das Projekt benötigten Materialien an:

Ein Arduino Uno Board

Ein 16×2 Flüssigkristalldisplay (LCD)

4 Drucktasten

5 1-Kiloohm-Widerstände
Überbrückungskabel

Projektdiagramm

Projektsimulation

Hier ist die Simulation des Projekts auf Tinkercad:

Projektprogramm

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

#define RS 12
#define EN 11
#define D4 6
#define D5 7
#define D6 8
#define D7 9


#define Player_1_moveDownButton 10   
#define Player_1_moveUpButton 5     
#define Player_2_moveDownButton 2   
#define Player_2_moveUpButton 3     

#define Player_1 1
#define Player_2 2

#define DiagonalballUpdateTime 21
#define HorizontalballUpdateTime 15

#define Start_X 35
#define Start_Y 7

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

void(* resetFunc) (void) = 0;       
volatile boolean x_Up = true;
volatile boolean x_Down = true;

LiquidCrystal lcd(RS, EN, D4, D5, D6, D7);   


class Paddle 
{
  public:
  uint8_t PaddleColArray[16] = {0,0,0,0,0,4,4,4,0,0,0,0,0,0,0,0};
  
  uint8_t PaddlePos = 6;     
  uint8_t Score = 0;         

  void MovePaddleUp()   
  {
    
    if(PaddlePos != 1)
    {
      PaddlePos--;
      PaddleColArray[PaddlePos+2]=0;
      PaddleColArray[PaddlePos-1]=4;
    }
  }

  
  void MovePaddleDown()
  {
    if(PaddlePos != 14)
    {
      PaddlePos++;
      PaddleColArray[PaddlePos-2]=0;
      PaddleColArray[PaddlePos+1]=4;
    }
  }

  
  void PrintPaddles(uint8_t Player_Num)
  {
    if(Player_Num == 2)
    {
      
      lcd.createChar(0, PaddleColArray);
      lcd.createChar(1, PaddleColArray+8);

      lcd.setCursor(14, 0);
      lcd.write(byte(0));
      
      lcd.setCursor(14, 1);
      lcd.write(byte(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));
    }
  }
};


Paddle p1, p2;

class Print_Game
{
  public:

  
  void Start_Game()
  {
    lcd.print(F("   PING PONG "));
    
    
    lcd.setCursor(0, 1);
    lcd.print(F("Appuyez sur un bouton ! "));

    
    uint8_t p1_UpButState = 0;
    uint8_t p1_DownButState = 0;
    uint8_t p2_UpButState = 0;
    uint8_t p2_DownButState = 0;
    
    // On attend qu'un bouton soit appuyé
    while(!(Button_Pressed))
    {
      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();        
  }

  
  void Print_Score()
  {
    
    lcd.clear();
    lcd.print(F("Joueur_1  Joueur_2"));
    lcd.setCursor(3 ,1);
    lcd.print(p1.Score);
    lcd.setCursor(12 ,1);
    lcd.print(p2.Score);

    
    delay(2000);

    // On efface l'écran pour continuer le jeu
    lcd.clear();
  }

  // On affiche le gagnant
  void Print_Winner(int Player_Num)
  {
    lcd.setCursor(0 ,0);
    lcd.print(F("   PERDU   "));
    lcd.setCursor(1, 1);
    lcd.print(F("Joueur "));
    lcd.print(Player_Num);
    lcd.setCursor(11 ,1);
    lcd.print(F("a gagne"));

    // Le texte reste pendant 5 secondes
    delay(5000);

    // On reintialise le jeu
    resetFunc();
  }
};

Print_Game g;

class Ball // Fonction pour la balle
{
  private:

  //Pour reintialiser la balle et les raquettes qaund un point est marqué Flag to reset the ball and paddles when a point is scored
  uint8_t Point_Scored = 0;
  
  uint8_t ballYDir = 0;       
  uint8_t ballXDir = -1;    

  // La localisation de la balle
  uint8_t ballY = Start_Y;
  uint8_t ballX = Start_X;
  
  uint8_t ballCharArray[8] = {0, 0, 0, 0, 0, 0, 0, 16};

  public:

  void GenerateBallArray();

  void PrintBall();

  void UpdateBall(uint8_t , uint8_t);

  void AwardAPoint();
};


void Ball :: GenerateBallArray() // Generation de la balle
{
  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() // On affiche la balle
{
   uint8_t LCDCol = ballX / 5;
  uint8_t LCDRow = (ballY <= 7) ? 0 : 1;
  lcd.createChar(4, ballCharArray);
  lcd.setCursor(LCDCol,LCDRow);
  lcd.write(byte(4));
}

// Mise à jour de la position de la balle
void Ball :: UpdateBall(uint8_t P1_PaddlePos, uint8_t P2_PaddlePos)
{
  // Petite pause avant la mise à jour
  // La balle à la même vitesse quand cela va en diagonale
  if(ballYDir)          // Quand la balle va en diagonale 
  {
    delay(DiagonalballUpdateTime);
  }
  else                  // Quand la balle va à l'horizontale
  {
    delay(HorizontalballUpdateTime);
  }

  //Calculer la prochaine position de la balle
  
  //Si la balle sort de la raquette
  if((ballX <= 6) || (ballX >= 73))
  {
    AwardAPoint();
  }

 // Si la balle touche la raquette du joueur 2
  else if(ballX == 72)
  {
    // si la balle touche au milieu de la raquette
    if(ballY == P2_PaddlePos)
    {
      ballXDir = -1;
    }

    // Si la balle touche le bas de la raquette
    else if(ballY == (P2_PaddlePos + 1))
    {
      ballXDir = -1;
      if(ballY == 15)    // Si la balle touche le bas de la raquette au bout de l'écran
      {
        ballYDir = -1;
      }
      else
      {
        ballYDir = 1;
      }
    }

    // Si la balle touche la haut de la raquette du joueur 2
    else if(ballY == (P2_PaddlePos - 1)){
      ballXDir = -1;
      if(ballY == 0)    // Si la balle touche le haut de la raquette et le haut de l'écran
      {
        ballYDir = 1;
      }
      else
      {
        ballYDir = -1;
      }
    }
  }

  //si la balle est au milieu de la raquette
  else if(ballX == 7)
  {
    if(ballY == P1_PaddlePos)
    {
      ballXDir = 1;
    }
        // Si la balle touche le bas de la raquette
    else if(ballY == (P1_PaddlePos + 1)){
      ballXDir = 1;
      if(ballY == 15)   // Si la balle touche le bas de la raquette au bout de l'écran
      {
        ballYDir = -1;
      }
      else
      {
        ballYDir = 1;
      }
    }

     // Si la balle touche la haut de la raquette du joueur 1
    else if(ballY == (P1_PaddlePos - 1))
    { 
      ballXDir = 1;
      if(ballY == 0)    // Si la balle touche le haut de la raquette et le haut de l'écran
      {
        ballYDir = 1;
      }
      else
      {
        ballYDir = -1;
      }
    }
  }

  // Si la balle touche un bord de l'écran
  else if((ballY == 0) || (ballY == 15))
  {
    ballYDir *= -1;
  }
    
  //On reintialise la ballee t les raquettes si un point a été gagné 
  if(Point_Scored == 1)
  {
    // On reintialise la balle
    ballX = Start_X;
    ballY = Start_Y;
    ballXDir *= -1;
    ballYDir = 0;

    // On reintialise les raquettes
    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;    // On reintialise le score
  }

  //  On efface la balle de sa position initiale
  uint8_t LCDCol = ballX / 5;
  uint8_t LCDRow = (ballY <= 7) ? 0 : 1;
  lcd.setCursor(LCDCol, LCDRow);
  lcd.print(" ");
  
  // On change la position de la balle basé sur les directions de X et Y  
  ballX += ballXDir;
  ballY += ballYDir;

  GenerateBallArray();

  // On affiche la balle après avoir calculé son nouvel emplacement
  PrintBall();
}

// On augmente le score des joueurs une fois un point marqué
void Ball :: AwardAPoint()
{
  if(ballX <= 8)    // Quand le joueur 2 marque un point 
  {
    p2.Score++;
  }
    
  else              // Quand le joueur 1 marque un point 
  {
    p1.Score++;
  }
  
  // Quand un des joueurs arrivent à 10 points
  
  if(p1.Score == 10)            // Si le joueur 1 a atteint 10 points
  {
    g.Print_Winner(Player_1);   //On affiche joueur 1 a gagné
  }

  else if(p2.Score == 10)       // Si le joueur 2 a atteint 10 points
  {
    g.Print_Winner(Player_2);   //On affiche joueur 2 a gagné
  }

   // On affiche le score des deux joueurs
  g.Print_Score();
    
  Point_Scored = 1;        
}

Ball b;


void setup()
{
  lcd.begin(16, 2); // On initialize l'écran LCD
  
  // On initalize les boutons
  DDRD &= ~(1<<Player_1_moveUpButton);
  DDRD &= ~(1<<Player_1_moveDownButton);
  DDRD &= ~(1<<Player_2_moveUpButton);
  DDRD &= ~(1<<Player_2_moveDownButton);
  
  g.Start_Game();
  
  // On affiche les raquettes
  p1.PrintPaddles(Player_1);
  p2.PrintPaddles(Player_2);
  
  // On affiche la balle sur l'écran LCD
  b.PrintBall();


  PCMSK2 |= (1 << PCINT21);
  PCICR |= (1 << PCIE2);

  PCMSK0 |= (1 << PCINT2);
  PCICR |= (1 << PCIE0);

  sei();
  
  attachInterrupt(digitalPinToInterrupt(Player_2_moveDownButton), P2_Move_Down, RISING);
  attachInterrupt(digitalPinToInterrupt(Player_2_moveUpButton), P2_Move_Up, RISING);
}


void loop()
{
  //On affiche les deux raquettes
  p1.PrintPaddles(Player_1);
  p2.PrintPaddles(Player_2);
  
  // Mise à jour de la position de la balle
  b.UpdateBall(p1.PaddlePos, p2.PaddlePos);
}

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

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

ISR(PCINT2_vect)
{
  if(x_Up)
  {
    p1.MovePaddleUp();
    x_Up = false;
  }
  else
  {
    x_Up = true;
  }
}

ISR(PCINT0_vect)
{
  if(x_Down)
  {
    p1.MovePaddleDown();
    x_Down = false;
  }
  else
  {
    x_Down = true;
  }
}

Bildschirmrandverwaltung

Wenn der Ball eine Kante des Bildschirms erreicht, wird er wie eine Wand zurückgespielt und gelangt nicht auf die andere Seite des Bildschirms.