Dans ce projet on va voir comment faire un jeu de ping pong sur arduino. Vous pouvez jouer à deux joueurs. Chaque joueur à deux boutons, un pour déplacer la raquette vers le haut et l’autre vers le bas.
Un point est marqué quand l’adversaire n’a pas réussi à renvoyer la balle. Le premier joueur qui marque 10 points gagne.
On va maintenant le matériel nécessaire pour le projet :
Voici la simulation du projet sur tinkercad :
#include <LiquidCrystal.h>
#include <avr/interrupt.h>
// On assigne les pins pour l'écran
#define RS 12
#define EN 11
#define D4 6
#define D5 7
#define D6 8
#define D7 9
// On définit les boutons qui font bouger les raquettes
#define Player_1_moveDownButton 10 // Bouton du bas pour le joueur 1
#define Player_1_moveUpButton 5 // Bouton du haut pour le joueur 1
#define Player_2_moveDownButton 2 // Bouton du bas pour le joueur 2
#define Player_2_moveUpButton 3 // Bouton du haut pour le joueur 1
// On assigne le numéro à chaque joueur
#define Player_1 1
#define Player_2 2
// Temps d'attente entre deux mise à jour de la balle
#define DiagonalballUpdateTime 21
#define HorizontalballUpdateTime 15
//Départ de la position de la balle
#define Start_X 35
#define Start_Y 7
#define Button_Pressed (p1_UpButState | p1_DownButState | p2_UpButState | p2_DownButState)
void(* resetFunc) (void) = 0;
// Variable globale pour les interruptions
volatile boolean x_Up = true;
volatile boolean x_Down = true;
LiquidCrystal lcd(RS, EN, D4, D5, D6, D7); // On initialise l'écran LCD
class Paddle // Class pour la raquette
{
public:
// On stock la valeur de chaque ligne contenant la raquette
uint8_t PaddleColArray[16] = {0,0,0,0,0,4,4,4,0,0,0,0,0,0,0,0};
uint8_t PaddlePos = 6; // On enregistre le centre de la raquette comme référence pour la faire bouger
uint8_t Score = 0; // Score de chaque joueur
void MovePaddleUp() // Quand le joueur appuie sur le bouton haut
{
//On vérifie que la raquette ne sorte pas de l'écran
if(PaddlePos != 1)
{
PaddlePos--;
PaddleColArray[PaddlePos+2]=0;
PaddleColArray[PaddlePos-1]=4;
}
}
// Quand le joueur appuie sur le bouton du bas
void MovePaddleDown()
{
if(PaddlePos != 14)
{
PaddlePos++;
PaddleColArray[PaddlePos-2]=0;
PaddleColArray[PaddlePos+1]=4;
}
}
// Afficher les raquettes avec le numéro des joueurs
void PrintPaddles(uint8_t Player_Num)
{
if(Player_Num == 2)
{
// Chaque joueur a un unique numéro à afficher
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));
}
}
};
// On créer les deux raquettes
Paddle p1, p2;
class Print_Game
{
public:
// On affiche le texte quand le jeu démarre
void Start_Game()
{
lcd.print(F(" PING PONG "));
//On se met sur la deuxième ligne
lcd.setCursor(0, 1);
lcd.print(F("Appuyez sur un bouton ! "));
// Variable poru récupérer si un bouton est appuyé
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(); //On efface l'écran pour commencer le jeu
}
// On affiche le score de chaque joueur
void Print_Score()
{
// On efface l'écran LCD pour afficher les scores
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);
// Les scores s'affcihent pendant 2 secondes
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;
}
}
Lorsque la balle atteint un bord de l’écran celle-ci est renvoyé comme avec un mur et ne passe pas de l’autre côté de l’écran.