En une phraseJ'ai programmé un petit jeu d'air-hockey qui tourne sur une carte électronique, sans ordinateur. Le jeu ci-dessus est le même programme, traduit pour le navigateur : mêmes constantes, même physique.
01L'objectif
Développer une application embarquée complète sur une carte STM32 : lire des entrées analogiques (joysticks), gérer un affichage graphique (écran TFT 320 × 240 en SPI) et faire tourner une boucle de jeu fluide à environ 60 images par seconde.
02Ma contribution, étape par étape
-
01Prise en main
Tester chaque périphérique
J'ai d'abord écrit de petits programmes de test : afficher sur l'écran, lire les axes des joysticks, détecter l'appui sur leur bouton. J'ai ajouté une zone morte pour que le joueur ne bouge pas tout seul quand le joystick est au repos.
⌁ Lecture d'un axe// CAN 12 bits : 0..4095, centre ≈ 2048 int lireAxe(int pin) { int v = analogRead(pin) - 2048; if (abs(v) < 300) return 0; // zone morte return v / 500; // vitesse de -4 à +4 pixels/image }
-
02Affichage
Le terrain et le score
J'ai dessiné le terrain avec des constantes (largeur, hauteur des buts, rayon des palets) pour pouvoir tout régler facilement. Pour éviter le scintillement, je n'efface et ne redessine que ce qui bouge.
-
03Physique
Rebonds, effets et accélération
C'est la partie dont je suis le plus fier. À chaque contact, je calcule la direction entre le centre du palet et la balle pour un rebond miroir. Si le joueur frappe à contre-sens, il renvoie la balle plus fort. Après dix échanges, la balle accélère progressivement.
⌁ Collision balle / paletfloat d = sqrt(d2), nx = dx / d, ny = dy / d; // normale au contact b.x = j.x + nx * dmin; b.y = j.y + ny * dmin; // sortir la balle du palet float vn = b.vx * nx + b.vy * ny; if (vn >= 0) return; // la balle s'éloigne déjà b.vx -= 2 * vn * nx; b.vy -= 2 * vn * ny; // rebond miroir if (j.vx * vxEntree + j.vy * vyEntree < 0) { // frappe à contre-sens b.vx += j.vx * FORCE_JOUEUR; b.vy += j.vy * FORCE_JOUEUR; } if (++b.echanges > ECHANGES_ACCEL) b.vitesse = fminf(VITESSE_MAX, VITESSE_BASE + (b.echanges - ECHANGES_ACCEL) * ACCEL_PAR_ECHANGE); fixerVitesse(b); // la norme de la vitesse est imposée
-
04Méthode
Outils et suivi de version
J'ai travaillé avec PlatformIO, le débogueur ST-Link et Git : une dizaine de commits retracent l'avancement, des tests de l'écran jusqu'au jeu complet. La documentation du code est générée avec Doxygen.
03Une difficulté, et comment je l'ai résolue
Le problème : la balle « collée »
Quand la balle entrait un peu dans le palet entre deux images, la collision était détectée plusieurs fois de suite : la balle tremblait ou restait coincée.
La solution
À chaque contact, je replace la balle juste à l'extérieur du palet et je ne la fais rebondir que si elle se dirige vraiment vers le joueur. J'impose aussi la norme de la vitesse : le joueur change la direction, pas la vitesse, ce qui garde le jeu contrôlable.
04Ce que j'ai appris
Compétences techniques
- Programmer un microcontrôleur STM32 en C/C++
- Lire des entrées analogiques (CAN 12 bits) et piloter un écran SPI
- Écrire une boucle temps réel et un peu de géométrie vectorielle
- Utiliser PlatformIO, un débogueur et Git
Compétences transversales
- Avancer par petites étapes testées
- Régler un système par essais (vitesse, accélération)
- Penser à l'utilisateur : un jeu doit être agréable, pas seulement fonctionner