1. Introduction

Pour mon projet P, j’ai décidé de faire (avec le Raspberry pi) un jeu classique de la programmation, le Pong. D’après Wikipédia : le Pong est un des premiers jeux vidéo d'arcade et le premier jeu vidéo d'arcade de sport. Il a été imaginé par l'Américain Nolan Bushnell et développé par Allan Alcorn, et la société Atari le commercialise à partir de 1972 1. Les raisons pour lesquelles j’ai choisi de faire ce projet sont le fait que c’est un jeu simple, qui en principe ne devait pas prendre trop de temps à être programmé et le fait de programmer un jeu me semblait intéressant.

2. Matériel et méthodes

2.1 Matériel

  • Un Raspberry Pi 3
  • Un module Pi Wedge pour Raspberry
  • Breadboard
  • 4x Boutons poussoir avec 4 branches (1x Bleu, 1x Jaune, 1x Vert, 1x Rouge)
  • 10x Câbles

2.2 Méthode

Pour faire ce projet, j’ai commencé par la partie de software, ensuite, j’ai fait la partie Hardware.

2.2.1 Programmation

Pour programmer le jeu Pong, il faut suivre certaines étapes : la création d’une fenêtre, la création des objets, le mouvement des objets et enfin l’interaction entre les objets.

2.2.1.1 Création d’une fenêtre

Les étapes pour créer une fenêtre sont les suivantes : « import pygame » cette ligne du code nous permet d’importer la librairie pygame qui est utilisée pour la création des jeux de vidéos. Ensuite, on doit démarrer pygame à l’aide de cette ligne « pygame.init() ». Après cela, on doit créer la fenêtre à l’aide de la ligne suivante : « ECRAN = pygame.display.set_mode(dimensions) » . On doit définir les dimensions de la fenêtre, pour cela, on peut définir des variables ayant les valeurs des dimensions. De cette façon, on doit juste tenir compte du nom de la variable, sans avoir besoin de se rappeler d’une valeur donnée. Ensuite, il faut créer une boucle principale ou boucle du jeu. Cette boucle va nous permettre de créer une fenêtre qui va rester ouverte, sans cette boucle, la fenêtre se ferme subitement. Pour créer cette boucle, on définit une fonction « main() ». Dans cette fonction, on utilise un booléen et la condition « while », cela va faire que la boucle se répète tant que le booléen vrai. Dans cette boucle créée par la condition « while », on va aussi définir d'événements. Ces événements vont nous permettre de fermer la fenêtre. Par exemple : @@ running = True

   while running:
       # Evènements permettant de fermer la fenêtre
       for event in pygame.event.get():
           if event.type == pygame.QUIT: # En appuyant sur le X au coin supérieur
               running = False
               break
   pygame.quit()

@@

2.2.1.2 Création des objets

Pour créer des objets sur pygame, on utilise une classe. Dans cette classe, on va donner des attributs aux objets, les attributs sont des fonctions qui définissent les objets. Le jeu Pong consiste de deux joueurs et d'une balle, donc il fallait créer ces trois objets. Pour créer un joueur, on crée une classe Player(), dans cette classe on va définir une fonction permettant de définir l’objet lui-même (sa forme et sa position dans la fenêtre), c’est le cas de la fonction « init(self, x, y, width, height) » Il faut savoir qu’à part le mot « self », on aurait pu donner d’autres nom aux autres valeurs, par exemple : « init(self, position_x, position_y, largeur, hauteur) » Ensuite, il faut assigner self à chacune des valeurs. @@

  # Fonction permettant de définir un objet
   def init(self, x, y, width, height):
       self.x = x
       self.y = y
       self.width = width
       self.height = height

@@ Ensuite, on attribue à ce joueur la fonction « draw(self, ECRAN) » , cette fonction nous permettra de dessiner l’objet sur la fenêtre. Pour dessiner le rectangle qui va représenter le joueur, on utilise la ligne de code suivante : « pygame.draw.rect(ECRAN, self.COULEUR, (self.x, self.y, self.width, self.height)). Le partie « COULEUR », dans le mot « self.COULEUR », représente la couleur RGB qui est appliquée sur le rectangle. Ensuite, on attribue à ce joueur la fonction « move(self, up=True) », cette fonction va nous permettre d’additionner ou de soustraire une valeur, à la position y du joueur, à chaque fois que la boucle se répète. Cela va donner un effet de mouvement. La valeur qu’on ajoute à la position peut être définie comme une variable (par exemple : « VEL » ). En fin, pour que le joueur puisse être reconnu plus tard par la fonction draw(), on associe la classe Player à un jouer donné en lui donnant comme argument sa position sur la fenêtre et ses dimensions par exemple : @@

   left_player = Player(10, HAUTEUR_ECRAN/2 - HAUTEUR_RECT/2, LARGEUR_RECT, HAUTEUR_RECT)

@@ Pour créer la balle, on utilise presque le même processus, sauf qu’on doit tenir compte des différences suivantes :

  • la fonction « init() » ne tient pas compte des valeurs width et height, mais de la valeur radius, car il s’agit d’un objet circulaire. On ajoute aussi dans cette fonction les deux paramètres suivants : « self.x_vel = self.MAX_VEL » et « self.y_vel = 0 ». Ces paramètres vont nous permettre plus tard de définir le mouvement de la balle au début de la partie.
  • la ligne « pygame.draw.rect() » devient « pygame.draw.circle() »,
  • la balle se déplace sur x et sur y (puisqu’elle déplace «librement », il n’y a pas besoin de définir de conditions, au contraire du cas des joueurs.)
  • On rajoute la fonction « reset() » qui va nous permettre de remettre la balle à sa position par défaut, lorsqu’on marque un point, et aussi à changer le sens de sa vitesse finale sur l’axe x et à immobiliser la balle sur l’axe y.

Ensuite, on va définir une fonction « draw(ECRAN, players, balle) » permettant de dessiner les objets sur la fenêtre, lorsqu’on appelle la fonction dans la boucle principale. Dans « draw() », on va définir les objets qu’on va dessiner. Premièrement, on commence par « ECRAN.fill(NOIR) cela va nous permettre d’avoir un background de couloir noir. Ensuite, on indique à la fonction « draw() » qu’il y a plusieurs joueurs à dessiner, pour cela on utilise la ligne du code suivant : @@

# Dessiner autant de joueurs que l'on annonce dans la boucle principale

for player in players:

       player.draw(ECRAN)

@@ Ensuite, on dessine la ligne qui se situe au milieu du terrain (le filet) : @@

  # Dessiner un trait discontinu formé d'une succession de petits traits.
   for i in range(10, HAUTEUR_ECRAN, HAUTEUR_ECRAN//20):
       if i % 2 == 1:
           continue
       pygame.draw.rect(ECRAN, BLANC, (LARGEUR_ECRAN//2-5, i, 10, HAUTEUR_ECRAN//25))

@@ On dessine la balle : « balle.draw() » (il faut suivre un ordre de rédaction à fin que les objets ne soient pas juxtaposées). Et on finit avec « pygame.display.update() » cela va nous permettre de mettre à jour l’écran lorsque la boucle se répète.

2.2.1.3 Mouvement des objets

Dans ce jeu, on a deux types de mouvements différents. D’une part, on a le mouvement vertical effectué par les joueurs, et d’autre part le mouvement effectué sur les deux axes du plan par la balle. Pour bien comprendre ces mouvements, commençons par le premier.
Mouvement des joueurs
Le mouvement des joueurs se produit du haut en bas. Pour décrire ce mouvement, on a besoin d’une fonction « mouvement_player() » permettant de déplacer le joueur vers le haut et vers le bas lorsque des boutons différents sont pressés (Les boutons sont bien évidemment placés sur la Breadboard). Les arguments de cette fonction vont être les noms que l’on a attribués aux boutons (chaque bouton est associé à un pin différant.) et les noms des joueurs (Tous les arguments doivent être séparés par des virgules.) le mouvement se produit grâce à deux conditions la première s’assurant que le bouton est pressé et la deuxième que le jouer ne dépasse pas les limites de la fenêtre. Ensuite, si ces conditions sont rempliées, on appelle la fonction « move() » pour le joueur donné (Cette fonction a été attribuée au joueur dans la « class Player :). Pour finir, on va appeler cette fonction dans la boucle principale. Exemple pour le joueur de gauche : @@ def mouvement_player(button_r1, button_r2, left_player):

   if button_r1.is_pressed and left_player.y - left_player.VEL >= 0:
       left_player.move(up=True)
   if button_r2.is_pressed and left_player.y + left_player.VEL + left_player.height <= HAUTEUR_ECRAN:
       left_player.move(up=False)

@@

Mouvement de la balle
La balle se déplace « librement », c’est-à-dire qu’elle n’est pas contrôlée par des boutons. Son mouvement est déjà prédéterminé dans la « class Balle ». Cependant, pour que la balle puisse se déplacer sur la fenêtre, on doit appeler la fonction dans la boucle principale « balle.move() »

2.2.1.4 l’interaction entre les objets

Dans ce jeu, les objets interagissent sous forme de collision. Il y a deux types de collisions dans le jeu Pong : les collisions entre la balle et les parties supérieur/inférieur, et entre la balle et les joueurs. Ces deux types de fonctions vont être définies dans une fonction « Collision » cette fonction prend comme arguments la balle et les joueurs.

Collisions entre la balle et les parties supérieur/inférieur
Pour définir ce type de collision, on a besoin de connaître la position de la balle par rapport aux parties supérieur/inférieur. Si la balle touche la partie supérieur ou inférieur, la vitesse de la balle sur l’axe y doit changer de sens. Par exemple : @@

   # Collisions entre la balle et les parties supérieur et inférieur
   if balle.y + balle.radius >= HAUTEUR_ECRAN:
       balle.y_vel *= -1
   elif balle.y - balle.radius <= 0:
       balle.y_vel *= -1

@@

Collisions entre la balle et les joueurs
Pour ce type de collision, la méthode devient un peu plus complexe. On doit tenir compte de la vitesse de la balle sur l’axe x. (Cela permet de savoir si la balle va taper le joueur de la gauche ou de la droite.) La position de la balle et des joueurs (pour savoir s’ils vont entrer en collision) et le fait que la vitesse de la balle doit changer de sens et augmenter (jusqu’à ce qu’elle atteigne sa vitesse max au coin du rectangle) au fur et mesure qu’elle impacte un point éloigné du centre du rectangle représentant le joueur.
Pour avoir cette information, il faut placer des conditions. La condition pour savoir si la balle va vers la gauche ou la droite est la suivante : « if ball.x_vel < 0 : (la balle va vers la gauche.) …. else : (elle va vers la droite.) » La condition pour savoir sa position par rapport au joueur est la suivante : @@

   # Collisions entre la balle et les joueurs
   if balle.x_vel < 0:
       if balle.y >= left_player.y and balle.y <= left_player.y + left_player.height:
           if balle.x - balle.radius <= left_player.x + left_player.width:
               balle.x_vel *= -1
   else: #idem pour le joueur de la droite

@@ Si ces deux conditions sont remplies, le résultat est le suivant : « balle.x_vel *=-1» cela va changer le sens de la vitesse sur l’axe X. Enfin, pour la dernière condition permettant de graduer la vitesse de la balle, on va déclarer 3 variables : le milieu du rectangle (milieu_y), la difference sur l’axe y entre le milieu du rectangle et la position de la balle, qu’on va nommer difference_en_y, cela va permettre de savoir la distance de la balle par rapport au centre du rectangle. Et enfin le facteur de réduction, qu’on va nommer « facteur_reduction », cette variable indique de quelle façon on doit diviser la « difference_en_y »pourque la valeur de la vitesse soit égale à la valeur maximale lorsqu’elle touche les coins du rectangle. @@

   # Collisions entre la balle et les joueurs
   if balle.x_vel < 0:
  1. bla ... bla ...
               milieu_y = left_player.y + left_player.height / 2
               difference_en_y = milieu_y - balle.y
               facteur_reduction = (left_player.height / 2)/balle.MAX_VEL
               y_vel = difference_en_y / facteur_reduction
               balle.y_vel = -1*y_vel
   else:  #idem pour la droite

@@

2.2.1.5 Titre et icône de la fenêtre

Pour donner un titre à la fenêtre, on utilise la ligne de code suivante : « pygame.display.set_caption("Pong") » Et pour l’icône, il nous faut télécharger une image sur Internet de 32 pixels et utiliser les deux lignes de codes suivantes : ICONE = pygame.image.load('nom de l’mage.png') pygame.display.set_icon(ICONE)

2.2.1.6 Scores :

On utilise le code suivant : @@

  1. Configuration des scores
       if balle.x < 0:
           right_score += 1
           balle.reset()
       elif balle.x > LARGEUR_ECRAN:
           left_score +=1
           balle.reset()
       won = False
       if left_score >= WINNING_SCORE:
           won = True
           won_texte = "Left Player WON!"
       elif right_score >= WINNING_SCORE:
           won = True
           won_texte = "Right Player WON!"
       if won:
           texte = SCORE_FONT_1.render(won_texte, 1, BLANC) 
           #SCORE_FONT_1 indique la police et sa taille, elle est déclarée au début du scripte
           ECRAN.blit(texte, (LARGEUR_ECRAN//2 - texte.get_width()//2, \\
               HAUTEUR_ECRAN//2 - texte.get_height()//2 - 8))
           pygame.display.update()
           pygame.time.delay(5000)
           balle.reset()
           left_score = 0
           right_score = 0

@@

2.2.2 Hardware

Utiliser 4 boutons et 10 câbles.
Associer le premier bouton au pin 4, le deuxième au pin 6, le troisième au pin 26 et le quatrième au pin 24.
Schéma : de l’installation d’un boutonCircuit Circuit bouton

3. Résultats

Le programme du jeu Pong fonctionne et on peut contrôler les joueurs avec des boutons à l’aide d’un Raspberry Pi. Je suis donc satisfait des résultats.

4. Discussion

Le fait d’apprendre à programmer un jeu était intéressent. Les notions informatiques pour programmer ce jeu sont assez simples, toutefois, cela permet de voir et d'apprendre des choses intéressantes comme c’est le cas de la programmation orientée (Superficiellement).
(Pour exécuter le jeu sur le terminal du Raspberry Pi Katla, il faut utiliser python3.5 et non python tout seul)
Un point négatif de programmer sur python dans le Raspberry, est le fait que le logiciel n’est pas mis à jour et du coup certains aspects change, par exemple sur python 3.10.4 on peut utiliser « f"{valeur numérique} » afin qu’un nombre devienne une chaîne de caractère. Cependant, sur python 3.5, c’est une faute de syntaxe, et il faut écrire « str(valeur numérique) ».

5. Conclusion

C’était intéressant de faire ce projet, cela m’a permis d’apprendre des choses et de voir la programmation d’une autre façon.

Références

  1. Wikipédia, Pong, https://fr.wikipedia.org/wiki/Pong
  2. gpiozero, Image circuit bouton, https://gpiozero.readthedocs.io/en/v1.1.0/recipes.html
  3. gpiozero, Documentation, https://gpiozero.readthedocs.io/en/stable/
  4. Tech With Tim, Make Pong With Python!, https://www.youtube.com/watch?v=vVGTZlnnX3U
  5. Flaticon, Pong, https://www.flaticon.com/search?word=pong