Pour recréer cette variante du classique jeu d'arcade "Pong", j'ai utilisé, en plus de la blue pill, un écran OLED I2C SH1106 et un potentiomètre. J'ai choisi la blue pill, mais il ne devrait pas être très difficile de modifier le sketch afin de l'utiliser sur une autre carte supportée par l'IDE Arduino. De plus, grâce à la bibliothèque u8g2, le sketch peut être modifié pour utilisation avec un autre modèle d'écran monochrome.
Déroulement du jeu
L'utilisateur contrôle la "raquette" de gauche au moyen du potentiomètre. La raquette de droite est contrôlée par notre programme.
Afin de programmer la carte blue pill avec l'IDE Arduino, les fichiers relatifs aux cartes STM32 doivent avoir préalablement été installés au moyen du gestionnaire de fichier (voir ce précédent billet pour des instructions détaillées).
De plus, notre sketch utilisera la bibliothèque u8g2 pour contrôler l'écran OLED. Cette bibliothèque peut être installée grâce au gestionnaire de bibliothèques de l'IDE.
Circuit
Puisque l'écran OLED SH1106 est un périphérique I2C, il est branché à la carte blue pill de la façon suivante:
- Broche GND de l'écran OLED: broche GND de la blue pill
- Broche VCC de l'écran OLED: broche 3.3 de la blue pill
- Broche SCL de l'écran OLED: broche B6 de la blue pill
- Broche SDA de l'écran OLED: broche B7 de la blue pill
Le potentiomètre est branché à la broche B0 de la blue pill, de façon à faire varier la tension entre 0 et 3,3 V.
Sketch
La routine "hasard" (ligne 36) est appelée chaque fois que la balle est remise en jeu (au tout début, et chaque fois qu'un point a été compté). Elle génère, de façon aléatoire, la hauteur initiale de la balle sur l'écran, et la vitesse à laquelle la balle se déplace (2 ou 3 pixels par cycle vers la gauche ou vers la droite, 2 ou 3 pixels par cycle vers le haut ou vers le bas). La raquette de l'adversaire est initialement placée à une hauteur similaire à celle de la balle, mais avec une imprécision aléatoire (ligne 41).
J'ai réglé la résolution du convertisseur analogique-numérique de la blue pill à 6 bits (ligne 59): le potentiomètre génère ainsi une valeur pouvant varier entre 0 et 63, ce qui correspond parfaitement aux 64 pixels de l'écran.
La raquette de l'adversaire tente de se maintenir à la même hauteur que la balle, mais il y parvient avec un taux de succès de 90% (ligne 75): l'adversaire n'est donc pas infaillible et le joueur peut gagner s'il réussit plusieurs coups consécutifs.
Dans sa version actuelle, le jeu ne s'arrête jamais!
-
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
/********************************************* | |
STM32 Pong OLED | |
Jeu de pong sur STM32 Blue Pill et OLED. | |
Plus d'infos: | |
https://electroniqueamateur.blogspot.com/2020/03/on-joue-pong-avec-la-stm32-blue-pill.html | |
**********************************************/ | |
#include <U8g2lib.h> | |
#include <Wire.h> | |
// écran OLED i2c 128X64 pixels: | |
U8G2_SH1106_128X64_NONAME_F_HW_I2C u8g2(U8G2_R0, /* reset=*/ U8X8_PIN_NONE); | |
const int AnalogPin = PB0; // broche du potentiomètre | |
// chaque raquette a 16 pixels de longueur: | |
const int demiRaquette = 8; | |
int longueurRaquette = 2 * demiRaquette; | |
int balleposx, balleposy; // position de la balle | |
int balledirx, ballediry; // vitesse de la balle | |
int posAdversaire; // position verticale de la raquette de l'adversaire | |
int pointageJoueur = 0; | |
int pointageAdversaire = 0; | |
/* | |
À chaque remise en jeu, la balle part du milieu de l'écran, | |
mais à une position verticale aléatoire, et avec un mouvement | |
de direction aléatoire. | |
*/ | |
void hasard() { | |
balleposx = 64; | |
balleposy = random(15, 50); | |
// adversaire placé environ à la même hauteur que la balle: | |
posAdversaire = balleposy + random(17) - 8; | |
balledirx = random(2, 4); | |
if (random(2)) | |
{ | |
balledirx = -balledirx; | |
} | |
ballediry = random(2, 4); | |
if (random(2)) | |
{ | |
ballediry = -ballediry; | |
} | |
// petite pause pour indiquer qu'un point a été compté | |
delay(1000); | |
} | |
void setup() { | |
u8g2.begin(); | |
analogReadResolution(6); // 64 positions possibles | |
u8g2.setFont(u8g2_font_t0_12_tn); | |
randomSeed(analogRead(PB0)); | |
hasard(); | |
} | |
void loop() { | |
int pot = analogRead(AnalogPin); | |
u8g2.clearBuffer(); | |
// raquette du joueur positionnée à la valeur du potentiomètre | |
u8g2.drawBox(2, pot - demiRaquette, 4, longueurRaquette); | |
// raquette de l'adversaire repositionnée 9 fois sur 10 | |
if (random(10)) { | |
if (posAdversaire > balleposy) { | |
posAdversaire = posAdversaire - abs(ballediry); | |
} | |
else if (posAdversaire < balleposy) { | |
posAdversaire = posAdversaire + abs(ballediry); | |
} | |
} | |
// on calcule une nouvelle position pour la balle | |
balleposx = balleposx + balledirx; | |
balleposy = balleposy + ballediry; | |
// est-ce que la balle frappe le mur du haut ou le mur du bas? | |
if ((balleposy < 4) || (balleposy > 60)) { | |
ballediry = -ballediry; | |
} | |
// est-ce que la balle frappe la raquette du joueur? | |
if ((balleposx < 10) && (abs(balleposy - pot) < demiRaquette)) { | |
balledirx = -balledirx; | |
balleposx = balleposx + 1; | |
} | |
// est-ce que la balle frappe la raquette de l'adversaire? | |
if ((balleposx > 118) && (abs(balleposy - posAdversaire) < demiRaquette)) { | |
balledirx = -balledirx; | |
balleposx = balleposx - 1; | |
} | |
// est-ce que l'adversaire a compté un point? | |
if (balleposx < 5) { | |
hasard(); | |
pointageAdversaire = pointageAdversaire + 1; | |
} | |
// est-ce que le joueur a compté un point? | |
if (balleposx > 122) { | |
hasard(); | |
pointageJoueur = pointageJoueur + 1; | |
} | |
// raquette de l'adversaire | |
u8g2.drawBox(122, posAdversaire - demiRaquette, 4, longueurRaquette); | |
// balle | |
u8g2.drawDisc(balleposx, balleposy, 3); | |
// pointage | |
u8g2.setCursor(30, 62); | |
u8g2.print(pointageJoueur); | |
u8g2.setCursor(80, 62); | |
u8g2.print(pointageAdversaire); | |
u8g2.sendBuffer(); | |
} |
Yves Pelletier (Twitter, Facebook)
Aucun commentaire:
Enregistrer un commentaire