ENIB 2024 : GNOP : Différence entre versions
(→Outil et matériel) |
(→Code Arduino) |
||
Ligne 32 : | Ligne 32 : | ||
/* | /* | ||
Code de fonctionnement pour le jeu PONG. | Code de fonctionnement pour le jeu PONG. | ||
− | Par les petits | + | Par les petits debrouillards. |
*/ | */ | ||
Ligne 38 : | Ligne 38 : | ||
// Definition des couleurs | // Definition des couleurs | ||
− | const CRGB NOIR = CRGB(0, 0, 0 ); | + | const CRGB NOIR = CRGB(0, 0, 0 ); //Création d'une constante de type CRGB de couleur noire |
− | const CRGB BLANC = CRGB(255, 255, 255); | + | const CRGB BLANC = CRGB(255, 255, 255); //Création d'une constante de type CRGB de couleur blanche |
− | const CRGB ROUGE = CRGB(255, 0, 0 ); | + | const CRGB ROUGE = CRGB(255, 0, 0 ); //Création d'une constante de type CRGB de couleur rouge |
− | const CRGB VERT = CRGB(0, 255, 0 ); | + | const CRGB VERT = CRGB(0, 255, 0 ); //Création d'une constante de type CRGB de couleur verte |
− | const CRGB BLEU = CRGB(0, 0, 255); | + | const CRGB BLEU = CRGB(0, 0, 255); //Création d'une constante de type CRGB de couleur bleue |
− | const CRGB JAUNE = CRGB(255, 255, 0 ); | + | const CRGB JAUNE = CRGB(255, 255, 0 ); //Création d'une constante de type CRGB de couleur jaune |
− | const CRGB ROSE = CRGB(255, 0, 255); | + | const CRGB ROSE = CRGB(255, 0, 255); //Création d'une constante de type CRGB de couleur rose |
− | const CRGB CYAN = CRGB(0, 255, 255); | + | const CRGB CYAN = CRGB(0, 255, 255); //Création d'une constante de type CRGB de couleur cyan |
// Definition des notes de musique | // Definition des notes de musique | ||
− | const int | + | const int PONG_SOUND = 250; //380 |
+ | const int LOOSE_SOUND = 180; | ||
/****************************** | /****************************** | ||
− | + | PARAMETRES DE BRANCHEMENTS | |
******************************/ | ******************************/ | ||
const int LED_PIN = 8; // Numero de branchement de la bande de LEDs | const int LED_PIN = 8; // Numero de branchement de la bande de LEDs | ||
− | const int BUTTON1_PIN = | + | const int BUTTON1_PIN = 2; // Numero de branchement du premier bouton |
− | const int BUTTON2_PIN = | + | const int BUTTON2_PIN = 4; // Numero de branchement du deuxieme bouton |
− | const int HAUT_PARLEUR_PIN = | + | const int HAUT_PARLEUR_PIN = 5; // Numero de branchement du haut parleur (optionnel) |
− | / | + | /*********************** |
− | + | PARAMETRES GENERAUX | |
− | + | ***********************/ | |
− | const int NUM_LEDS = | + | const int NUM_LEDS = 60; // Nombre de LEDs sur la bande |
− | const float | + | const CRGB PLAYER1_COLOR = VERT; // Couleur Player 1 |
− | const float ACCELERATION = | + | const CRGB PLAYER2_COLOR = ROUGE; // Couleur Player 2 |
− | const int HIT_ZONE = | + | |
− | const int MAX_SCORE = 3; | + | |
+ | /*********************** | ||
+ | PARAMETRES PONG | ||
+ | ***********************/ | ||
+ | const float BALL_SPEED = 0.5; // Vitesse de la balle | ||
+ | const float ACCELERATION = 9; // Accélération de la balle a chaque tir, si ACCELERATION = 10 on augmente la vitesse de 10 pourcent a chaque tir | ||
+ | const int HIT_ZONE = 8; // Nombre de LED pendant lesquelles on peut renvoyer la balle | ||
+ | const int MAX_SCORE = 3; // Score maximum avant la fin du jeu | ||
− | |||
− | |||
const CRGB BALL_COLOR = BLANC; // Couleur de la balle | const CRGB BALL_COLOR = BLANC; // Couleur de la balle | ||
+ | |||
+ | /******************************* | ||
+ | PARAMETRES TIR A LA CORDE | ||
+ | *******************************/ | ||
+ | const float INCREMENT = 0.05; // De combien avance la corde lorsqu'on appuie sur le bouton | ||
+ | const float ROPE_SMOOTH = 1.0; // Paramètre servant a lisser la position de la corde | ||
+ | |||
+ | const float WAVE_LENGTH = 0.2; // Taille des ondes | ||
+ | const float WAVE_SPEED = 1.0; // Vitesse des ondes | ||
− | |||
− | |||
+ | /****************************** | ||
+ | VARIABLES GENERALES | ||
+ | ******************************/ | ||
// Players | // Players | ||
enum Player | enum Player | ||
Ligne 90 : | Ligne 106 : | ||
{ | { | ||
START, | START, | ||
− | + | PONG, // Mode pong | |
+ | TAC // Mode tir a la corde | ||
}; | }; | ||
+ | |||
+ | unsigned long lastMillis = 0; | ||
+ | unsigned long beginTimer = 0; | ||
+ | unsigned int counter = 0; | ||
+ | |||
+ | Player player = PLAYER1; // Prochain joueur a appuyer sur son bouton | ||
+ | Player lastWinner = PERSONNE; | ||
+ | |||
+ | CRGB leds[NUM_LEDS]; | ||
GameState gameState = START; | GameState gameState = START; | ||
− | // | + | /********************************************** |
+ | FONCTIONS DE FONCTIONNEMENT GENERALES | ||
+ | **********************************************/ | ||
// Fonction permettant de changer la couleur d'une LED relativement au player 1 ou 2 | // Fonction permettant de changer la couleur d'une LED relativement au player 1 ou 2 | ||
Ligne 116 : | Ligne 144 : | ||
} | } | ||
− | / | + | /********************************************* |
− | + | VARIABLES DE FONCTIONNEMENT DU PONG | |
− | float ballSpeed = | + | *********************************************/ |
+ | float ballSpeed = BALL_SPEED; // Vitesse de la balle | ||
float ballPosition = 1; // Position de la balle sur la bande de led (Si ballPosition = 0, la balle est devant le player 1. Si ballPosition = 1, la balle est devant le player 2) | float ballPosition = 1; // Position de la balle sur la bande de led (Si ballPosition = 0, la balle est devant le player 1. Si ballPosition = 1, la balle est devant le player 2) | ||
int player1Score = 0; // Score du player 1 | int player1Score = 0; // Score du player 1 | ||
int player2Score = 0; // Score du player 2 | int player2Score = 0; // Score du player 2 | ||
− | |||
− | |||
− | |||
− | |||
− | |||
− | + | /********************************************** | |
− | // Fonction servant | + | FONCTIONS DE FONCTIONNEMENT DU PONG |
+ | **********************************************/ | ||
+ | // Fonction servant a afficher les scores | ||
void showScore() | void showScore() | ||
{ | { | ||
Ligne 182 : | Ligne 208 : | ||
} | } | ||
− | // Si la partie est | + | // Si la partie est terminee on va a l'affichage de fin |
if (player1Score == MAX_SCORE || player2Score == MAX_SCORE) | if (player1Score == MAX_SCORE || player2Score == MAX_SCORE) | ||
{ | { | ||
Ligne 192 : | Ligne 218 : | ||
// On reinitialise la vitesse | // On reinitialise la vitesse | ||
− | ballSpeed = | + | ballSpeed = BALL_SPEED; |
// On reinitialise les leds | // On reinitialise les leds | ||
Ligne 200 : | Ligne 226 : | ||
else | else | ||
{ | { | ||
− | gameState = | + | gameState = PONG; |
− | ballSpeed = | + | ballSpeed = BALL_SPEED; |
+ | } | ||
+ | } | ||
+ | |||
+ | |||
+ | /*************************************** | ||
+ | FONCTION PRINCIPALE DU PONG | ||
+ | ***************************************/ | ||
+ | void pong_loop() | ||
+ | { | ||
+ | // Calcul du temps ecoule entre deux boucles | ||
+ | unsigned long currentMillis = millis(); | ||
+ | |||
+ | // On calcule le numero de la LED allumee | ||
+ | int ballLed = ballPosition * NUM_LEDS; | ||
+ | |||
+ | // On s'assure que la position de la balle ne depasse pas la taille de la bande de LED | ||
+ | ballLed = min(ballLed, NUM_LEDS - 1); | ||
+ | |||
+ | // On regarde qui est en train de jouer | ||
+ | if (player == PLAYER1) | ||
+ | { | ||
+ | // On regarde si le player a appuye sur son bouton et si le delai de debut de jeu est passe | ||
+ | if (digitalRead(BUTTON1_PIN) == LOW && currentMillis - beginTimer > 500) | ||
+ | { | ||
+ | // Si la balle est hors de la zone de tir, l'autre player marque un point | ||
+ | if (ballLed >= HIT_ZONE) | ||
+ | { | ||
+ | player2Score += 1; | ||
+ | lastWinner = PLAYER2; | ||
+ | ballPosition = 0; | ||
+ | |||
+ | // On joue le son de defaite | ||
+ | tone(HAUT_PARLEUR_PIN, LOOSE_SOUND, 800); | ||
+ | |||
+ | // On passe en mode affichage des scores | ||
+ | showScore(); | ||
+ | |||
+ | // C'est a l'autre player de jouer | ||
+ | player = PLAYER2; | ||
+ | |||
+ | // Actualisation de la variable lastMillis | ||
+ | lastMillis = millis(); | ||
+ | } | ||
+ | else | ||
+ | { | ||
+ | // On accelere la balle | ||
+ | ballSpeed *= 1.0 + ACCELERATION / 100; | ||
+ | |||
+ | // C'est a l'autre player de jouer | ||
+ | player = PLAYER2; | ||
+ | |||
+ | // On joue la note de musique | ||
+ | tone(HAUT_PARLEUR_PIN, PONG_SOUND, 100); | ||
+ | } | ||
+ | |||
+ | return; | ||
+ | } | ||
+ | |||
+ | // On fait avancer la balle | ||
+ | ballPosition -= ballSpeed * (currentMillis - lastMillis) * 0.001f; | ||
+ | |||
+ | // On regarde si la balle est sortie de la zone | ||
+ | if (ballPosition < 0.0f) | ||
+ | { | ||
+ | // Si oui le player 2 marque un point | ||
+ | player2Score += 1; | ||
+ | lastWinner = PLAYER2; | ||
+ | ballPosition = 0; | ||
+ | |||
+ | // On joue le son de defaite | ||
+ | tone(HAUT_PARLEUR_PIN, LOOSE_SOUND, 800); | ||
+ | |||
+ | // On passe en mode affichage des scores | ||
+ | showScore(); | ||
+ | |||
+ | // C'est a l'autre player de jouer | ||
+ | player = PLAYER2; | ||
+ | |||
+ | // Actualisation de la variable lastMillis | ||
+ | lastMillis = millis(); | ||
+ | return; | ||
+ | } | ||
+ | } | ||
+ | else // player == PLAYER2 | ||
+ | { | ||
+ | // On regarde si le player a appuye sur son bouton et si le delai de debut de jeu est passe | ||
+ | if (digitalRead(BUTTON2_PIN) == LOW && currentMillis - beginTimer > 500) | ||
+ | { | ||
+ | // Si la balle est hors de la zone de tir, l'autre player marque un point | ||
+ | if (ballLed < NUM_LEDS - HIT_ZONE) | ||
+ | { | ||
+ | player1Score += 1; | ||
+ | lastWinner = PLAYER1; | ||
+ | ballPosition = 1; | ||
+ | |||
+ | // On joue le son de defaite | ||
+ | tone(HAUT_PARLEUR_PIN, LOOSE_SOUND, 800); | ||
+ | |||
+ | // On passe en mode affichage des scores | ||
+ | showScore(); | ||
+ | |||
+ | // C'est a l'autre player de jouer | ||
+ | player = PLAYER1; | ||
+ | |||
+ | // Actualisation de la variable lastMillis | ||
+ | lastMillis = millis(); | ||
+ | } | ||
+ | else | ||
+ | { | ||
+ | // On accelere la balle | ||
+ | ballSpeed *= 1.1; | ||
+ | |||
+ | // C'est a l'autre player de jouer | ||
+ | player = PLAYER1; | ||
+ | |||
+ | // On joue la note de musique | ||
+ | tone(HAUT_PARLEUR_PIN, PONG_SOUND, 100); | ||
+ | } | ||
+ | |||
+ | return; | ||
+ | } | ||
+ | |||
+ | // On fait avancer la balle dans l'autre sens | ||
+ | ballPosition += ballSpeed * (currentMillis - lastMillis) * 0.001f; | ||
+ | |||
+ | // On regarde si la balle est sortie de la zone | ||
+ | if (ballPosition >= 1) | ||
+ | { | ||
+ | // Si oui le player 1 marque un point | ||
+ | player1Score += 1; | ||
+ | lastWinner = PLAYER1; | ||
+ | ballPosition = 1; | ||
+ | |||
+ | // On joue le son de defaite | ||
+ | tone(HAUT_PARLEUR_PIN, LOOSE_SOUND, 800); | ||
+ | |||
+ | // On passe en mode affichage des scores | ||
+ | showScore(); | ||
+ | // C'est a l'autre player de jouer | ||
+ | player = PLAYER1; | ||
+ | |||
+ | // Actualisation de la variable lastMillis | ||
+ | lastMillis = millis(); | ||
+ | return; | ||
+ | } | ||
+ | } | ||
+ | |||
+ | ///// AFFICHAGE BANDE DE LEDs ///// | ||
+ | // Premierement on efface toutes les couleurs precedentes | ||
+ | FastLED.clear(); | ||
+ | |||
+ | // Ensuite on allume faiblement les LEDs correspondant a la zone de chaque cote | ||
+ | for (int i = 0; i < HIT_ZONE; i++) | ||
+ | { | ||
+ | // On allume de chaque cote | ||
+ | ledColor(PLAYER1, i, PLAYER1_COLOR / 10); // On divise la couleur par 10 pour la rendre 10 fois moins puissante | ||
+ | ledColor(PLAYER2, i, PLAYER2_COLOR / 10); | ||
+ | } | ||
+ | |||
+ | // Ensuite on allume faiblement les LEDs correspondant aux scores | ||
+ | // Pour le player 1 | ||
+ | for (int i = 0; i < player1Score; i++) | ||
+ | { | ||
+ | ledColor(PLAYER1, NUM_LEDS / 2 - (i + 1), PLAYER1_COLOR / 15); | ||
+ | } | ||
+ | // Pour le player 2 | ||
+ | for (int i = 0; i < player2Score; i++) | ||
+ | { | ||
+ | ledColor(PLAYER2, NUM_LEDS / 2 - (i + 1), PLAYER2_COLOR / 15); | ||
+ | } | ||
+ | |||
+ | // Ensuite on actualise la position de la balle | ||
+ | // On donne la couleur de la led en fonction de si la balle est dans la zone d'un player ou non | ||
+ | |||
+ | // Si la balle est dans le camp d'un des player, elle est rouge. | ||
+ | if (ballLed < HIT_ZONE || ballLed >= NUM_LEDS - HIT_ZONE) | ||
+ | { | ||
+ | ledColor(ballLed, ROUGE); | ||
+ | } | ||
+ | // Si elle en est proche, elle est jaune | ||
+ | else if (ballLed < 2 * HIT_ZONE || ballLed >= NUM_LEDS - 2 * HIT_ZONE) | ||
+ | { | ||
+ | ledColor(ballLed, JAUNE); | ||
+ | } | ||
+ | // Sinon la balle a sa couleur par defaut | ||
+ | else | ||
+ | { | ||
+ | ledColor(ballLed, BALL_COLOR); | ||
} | } | ||
+ | |||
+ | // On envoie la couleur des leds a la bande de leds | ||
+ | FastLED.show(); | ||
+ | |||
+ | // On actualise la variable lastMillis pour la boucle suivante | ||
+ | lastMillis = currentMillis; | ||
+ | } | ||
+ | |||
+ | |||
+ | |||
+ | /******************************************************* | ||
+ | VARIABLES DE FONCTIONNEMENT DU TIR A LA CORDE | ||
+ | *******************************************************/ | ||
+ | float ropePosition = 0.5; // Position non lissee de la corde | ||
+ | float displayedRopePosition = 0.5; // Position lissee de la corde | ||
+ | bool player1Pushed = false; | ||
+ | bool player2Pushed = false; | ||
+ | |||
+ | float player1Waves[10] = { 0 }; | ||
+ | float player2Waves[10] = { 0 }; | ||
+ | |||
+ | /*********************************************** | ||
+ | FONCTION PRINCIPALE DU TIR A LA CORDE | ||
+ | ***********************************************/ | ||
+ | void tac_loop() | ||
+ | { | ||
+ | // Calcul des temps | ||
+ | unsigned long ms = millis(); | ||
+ | float dt = (ms - lastMillis) * 0.001f; | ||
+ | lastMillis = ms; | ||
+ | |||
+ | // Si moins d'une seconde s'est ecoule depuis le debut, on ne fait rien (pour eviter d'appuyer sans faire expres) | ||
+ | if (ms - beginTimer < 1000) | ||
+ | return; | ||
+ | |||
+ | bool player1Push = !digitalRead(BUTTON1_PIN); | ||
+ | bool player2Push = !digitalRead(BUTTON2_PIN); | ||
+ | |||
+ | if (player1Push && !player1Pushed) | ||
+ | { | ||
+ | // On incremente la position de la corde | ||
+ | ropePosition += INCREMENT; | ||
+ | |||
+ | // On lance une nouvelle onde | ||
+ | for (int i = 0; i < 10; i++) | ||
+ | { | ||
+ | if (player1Waves[i] == 0.0f) | ||
+ | { | ||
+ | //player1Waves[i] = WAVE_SPEED * dt; | ||
+ | player1Waves[i] = 0.01f; | ||
+ | break; | ||
+ | } | ||
+ | } | ||
+ | } | ||
+ | if (player2Push && !player2Pushed) | ||
+ | { | ||
+ | // On incremente la position de la corde | ||
+ | ropePosition -= INCREMENT; | ||
+ | |||
+ | // On lance une nouvelle onde | ||
+ | for (int i = 0; i < 10; i++) | ||
+ | { | ||
+ | if (player2Waves[i] == 0.0f) | ||
+ | { | ||
+ | player2Waves[i] = 0.99f; | ||
+ | break; | ||
+ | } | ||
+ | } | ||
+ | } | ||
+ | |||
+ | // Memorisation des etats des boutons | ||
+ | player1Pushed = player1Push; | ||
+ | player2Pushed = player2Push; | ||
+ | |||
+ | |||
+ | |||
+ | |||
+ | // On calcule la position lissee de la corde (displayedRopePosition correspond a la variable ropePosition avec un lissage supplementaire) | ||
+ | displayedRopePosition += (ropePosition - displayedRopePosition) * dt / ROPE_SMOOTH; | ||
+ | |||
+ | // On regarde si player 1 a gagne | ||
+ | if (displayedRopePosition >= 1.0f) | ||
+ | { | ||
+ | lastWinner = PLAYER1; | ||
+ | gameState = START; | ||
+ | beginTimer = millis(); | ||
+ | |||
+ | ropePosition = 0.5f; | ||
+ | displayedRopePosition = 0.5f; | ||
+ | |||
+ | FastLED.clear(); | ||
+ | return; | ||
+ | } | ||
+ | // On regarde si player 2 a gagne | ||
+ | else if (displayedRopePosition <= 0.0f) | ||
+ | { | ||
+ | lastWinner = PLAYER2; | ||
+ | gameState = START; | ||
+ | beginTimer = millis(); | ||
+ | |||
+ | ropePosition = 0.5f; | ||
+ | displayedRopePosition = 0.5f; | ||
+ | |||
+ | FastLED.clear(); | ||
+ | return; | ||
+ | } | ||
+ | |||
+ | // On propage les ondes | ||
+ | float dx = WAVE_SPEED * dt; | ||
+ | |||
+ | for (int wave = 0; wave < 10; wave++) | ||
+ | { | ||
+ | // Ondes player 1 | ||
+ | if (player1Waves[wave] != 0.0f) | ||
+ | { | ||
+ | player1Waves[wave] += dx; | ||
+ | |||
+ | // Si toute l'onde depasse le point de la corde, on l'arrete | ||
+ | if (player1Waves[wave] - WAVE_LENGTH > displayedRopePosition) | ||
+ | { | ||
+ | player1Waves[wave] = 0.0f; | ||
+ | } | ||
+ | } | ||
+ | |||
+ | // Ondes player 2 | ||
+ | if (player2Waves[wave] != 0.0f) | ||
+ | { | ||
+ | player2Waves[wave] -= dx; | ||
+ | |||
+ | // Si toute l'onde depasse le point de la corde, on l'arrete | ||
+ | if (player2Waves[wave] + WAVE_LENGTH < displayedRopePosition) | ||
+ | { | ||
+ | player2Waves[wave] = 0.0f; | ||
+ | } | ||
+ | } | ||
+ | } | ||
+ | |||
+ | // On actualise la bande de leds | ||
+ | float ledLuminosity; | ||
+ | float ledPosition; | ||
+ | |||
+ | FastLED.clear(); | ||
+ | |||
+ | // On commence par calculer le numero de la led correspondant a la position de la corde | ||
+ | int ropeLedPosition = NUM_LEDS * displayedRopePosition; | ||
+ | |||
+ | // On dessine la zone du player 1 | ||
+ | // Pour cela on calcule la luminosite led par led | ||
+ | for (int led = 0; led < ropeLedPosition; led++) | ||
+ | { | ||
+ | // La luminosite minimale d'une led est 10% de la luminosite max | ||
+ | ledLuminosity = 0.1f; | ||
+ | ledPosition = float(led) / float(NUM_LEDS); | ||
+ | |||
+ | // Pour chaque onde, on ajoute la luminosite a la led | ||
+ | for (int wave = 0; wave < 10; wave++) | ||
+ | { | ||
+ | // Si l'onde n'est pas active ou si l'onde est avant la led, on n'ajoute aucune luminosite | ||
+ | if (player1Waves[wave] == 0.0f || player1Waves[wave] < ledPosition) | ||
+ | continue; | ||
+ | |||
+ | // On ajoute de la luminosite de facon decroissante. plus l'onde est loin, moins on ajoute de luminosite. | ||
+ | ledLuminosity += max(0.0f, 0.9f - (player1Waves[wave] - ledPosition) / WAVE_LENGTH); | ||
+ | } | ||
+ | // La valeur maximale de luminosite est 1.0 | ||
+ | if (ledLuminosity > 2.0f) | ||
+ | ledLuminosity = 2.0f; | ||
+ | |||
+ | // On actualise la valeur de luminosite de la led | ||
+ | ledColor(led, PLAYER1_COLOR / int(2.0f / ledLuminosity) / 5); | ||
+ | } | ||
+ | |||
+ | // On dessine la zone du player 2 | ||
+ | for (int led = ropeLedPosition + 1; led < NUM_LEDS; led++) | ||
+ | { | ||
+ | // La luminosite minimale d'une led est 10% de la luminosite max | ||
+ | ledLuminosity = 0.1f; | ||
+ | ledPosition = float(led) / float(NUM_LEDS); | ||
+ | |||
+ | // Pour chaque onde, on ajoute la luminosite a la led | ||
+ | for (int wave = 0; wave < 10; wave++) | ||
+ | { | ||
+ | // Si l'onde n'est pas active ou si l'onde est avant la led, on n'ajoute aucune luminosite | ||
+ | if (player2Waves[wave] == 0.0f || ledPosition > player2Waves[wave]) | ||
+ | continue; | ||
+ | |||
+ | // On ajoute de la luminosite de facon decroissante. plus l'onde est loin, moins on ajoute de luminosite. | ||
+ | ledLuminosity += max(0.0f, 0.9f - (player2Waves[wave] - ledPosition) / WAVE_LENGTH); | ||
+ | } | ||
+ | // La valeur maximale de luminosite est 1.0 | ||
+ | if (ledLuminosity > 2.0f) | ||
+ | ledLuminosity = 2.0f; | ||
+ | |||
+ | // On actualise la valeur de luminosite de la led | ||
+ | ledColor(led, PLAYER2_COLOR / int(1.0f / ledLuminosity) / 5); | ||
+ | } | ||
+ | |||
+ | // On actualise la led correspondant a la position de la corde | ||
+ | ledColor(ropeLedPosition, BLANC); | ||
+ | FastLED.show(); | ||
} | } | ||
Ligne 211 : | Ligne 625 : | ||
****************************************************************/ | ****************************************************************/ | ||
void setup() { | void setup() { | ||
− | |||
// Initialisation des LEDs | // Initialisation des LEDs | ||
FastLED.addLeds<WS2812B, LED_PIN, GRB>(leds, NUM_LEDS); | FastLED.addLeds<WS2812B, LED_PIN, GRB>(leds, NUM_LEDS); | ||
Ligne 240 : | Ligne 653 : | ||
// On envoie les changements a la bande de leds | // On envoie les changements a la bande de leds | ||
FastLED.show(); | FastLED.show(); | ||
+ | |||
+ | // Initialisation du temps d'allumage | ||
+ | beginTimer = millis(); | ||
} | } | ||
Ligne 262 : | Ligne 678 : | ||
} | } | ||
− | // On regarde si un des boutons est appuye | + | // On regarde si un temps minimim s'est ecoule et si un des boutons est appuye |
− | if (digitalRead(BUTTON1_PIN) == LOW || digitalRead(BUTTON2_PIN) == LOW) | + | if (millis() - beginTimer > 1000 && (digitalRead(BUTTON1_PIN) == LOW || digitalRead(BUTTON2_PIN) == LOW)) |
{ | { | ||
− | + | unsigned long pushBegin = millis(); | |
− | |||
− | + | while (digitalRead(BUTTON1_PIN) == LOW || digitalRead(BUTTON2_PIN) == LOW); | |
− | |||
− | + | if (millis() - pushBegin < 1000) | |
− | |||
− | |||
− | |||
− | |||
− | |||
− | |||
− | |||
− | |||
− | |||
− | |||
− | |||
− | |||
− | |||
− | |||
− | |||
− | |||
− | |||
− | |||
− | |||
{ | { | ||
− | + | gameState = PONG; | |
− | |||
− | |||
− | |||
− | |||
− | |||
− | |||
− | |||
− | |||
− | |||
− | |||
− | |||
− | |||
− | |||
− | |||
− | |||
− | |||
− | |||
− | |||
− | |||
− | |||
− | |||
− | |||
− | |||
− | |||
− | |||
− | |||
− | |||
− | |||
} | } | ||
− | + | else | |
− | |||
− | |||
− | |||
− | |||
− | |||
{ | { | ||
− | + | gameState = TAC; | |
− | |||
− | |||
− | |||
− | |||
− | |||
− | |||
− | |||
− | |||
− | |||
− | |||
− | |||
− | |||
− | |||
} | } | ||
− | |||
− | |||
− | |||
− | |||
− | |||
− | |||
− | |||
− | |||
− | |||
− | |||
− | |||
− | |||
− | + | // On joue la note de musique | |
− | + | tone(HAUT_PARLEUR_PIN, PONG_SOUND, 100); | |
− | + | // Initialisation de la varable lastMillis | |
− | + | lastMillis = millis(); | |
− | + | // Initialisation du temps de debut de jeu | |
− | + | beginTimer = millis(); | |
− | |||
− | |||
− | |||
− | |||
− | |||
− | |||
− | |||
− | |||
− | |||
− | |||
− | |||
− | |||
− | |||
− | |||
− | |||
− | |||
− | |||
− | |||
− | |||
− | |||
− | |||
− | |||
− | |||
− | |||
− | |||
− | |||
− | |||
− | |||
− | |||
− | |||
− | |||
− | |||
− | |||
} | } | ||
− | + | break; | |
− | |||
− | |||
− | + | case PONG: | |
− | + | pong_loop(); | |
− | + | break; | |
− | |||
− | |||
− | |||
− | |||
− | + | case TAC: | |
− | + | tac_loop(); | |
− | |||
− | |||
− | |||
− | |||
− | |||
− | |||
− | |||
− | |||
− | |||
− | |||
− | |||
− | |||
− | |||
− | |||
− | |||
− | |||
− | |||
− | |||
− | |||
− | |||
− | |||
− | |||
− | |||
− | |||
− | |||
− | |||
− | |||
− | |||
− | |||
− | |||
− | |||
− | |||
− | |||
− | |||
break; | break; | ||
} | } | ||
} | } | ||
− | |||
− | |||
==Étapes de fabrication== | ==Étapes de fabrication== |
Version du 25 janvier 2024 à 10:46
Titre de la fiche expérience :
Sommaire
Description
Le projet est de fabriquer un jeu ludique à l'aide d'une carte Arduino, notre jeu est le D1-Pong. Inspiré du sport du tennis de table, il reprend le même principe mais avec des LEDS, la balle se déplace sur les LEDS et dès qu'elle arrive à une extrémité, le joueur en question appuie sur son bouton pour que la balle parte dans l'autre sens et ainsi de suite.
Voici notre équipe :
Photos
Outil et matériel
Pour fabriquer ce jeu, nous avons eut besoin de :
- Carte Arduino Nano
- 2 boutons d'arcade
- Carton
- Fils électriques
- Interupteur
- Batterie
- Ruban de LEDS
- Haut-parleur
Nous nous sommes servis de plusieurs outils :
- Fer à souder
- Pistolet à colle
- Cutter
Fichiers à joindre
On mettra une vidéo
Code Arduino
<syntaxhighlight lang="Arduino" line> /*
Code de fonctionnement pour le jeu PONG. Par les petits debrouillards.
- /
- include <FastLED.h>
// Definition des couleurs const CRGB NOIR = CRGB(0, 0, 0 ); //Création d'une constante de type CRGB de couleur noire const CRGB BLANC = CRGB(255, 255, 255); //Création d'une constante de type CRGB de couleur blanche const CRGB ROUGE = CRGB(255, 0, 0 ); //Création d'une constante de type CRGB de couleur rouge const CRGB VERT = CRGB(0, 255, 0 ); //Création d'une constante de type CRGB de couleur verte const CRGB BLEU = CRGB(0, 0, 255); //Création d'une constante de type CRGB de couleur bleue const CRGB JAUNE = CRGB(255, 255, 0 ); //Création d'une constante de type CRGB de couleur jaune const CRGB ROSE = CRGB(255, 0, 255); //Création d'une constante de type CRGB de couleur rose const CRGB CYAN = CRGB(0, 255, 255); //Création d'une constante de type CRGB de couleur cyan
// Definition des notes de musique const int PONG_SOUND = 250; //380 const int LOOSE_SOUND = 180;
/******************************
PARAMETRES DE BRANCHEMENTS ******************************/
const int LED_PIN = 8; // Numero de branchement de la bande de LEDs const int BUTTON1_PIN = 2; // Numero de branchement du premier bouton const int BUTTON2_PIN = 4; // Numero de branchement du deuxieme bouton const int HAUT_PARLEUR_PIN = 5; // Numero de branchement du haut parleur (optionnel)
/***********************
PARAMETRES GENERAUX ***********************/
const int NUM_LEDS = 60; // Nombre de LEDs sur la bande const CRGB PLAYER1_COLOR = VERT; // Couleur Player 1 const CRGB PLAYER2_COLOR = ROUGE; // Couleur Player 2
/***********************
PARAMETRES PONG ***********************/
const float BALL_SPEED = 0.5; // Vitesse de la balle const float ACCELERATION = 9; // Accélération de la balle a chaque tir, si ACCELERATION = 10 on augmente la vitesse de 10 pourcent a chaque tir const int HIT_ZONE = 8; // Nombre de LED pendant lesquelles on peut renvoyer la balle const int MAX_SCORE = 3; // Score maximum avant la fin du jeu
const CRGB BALL_COLOR = BLANC; // Couleur de la balle
/*******************************
PARAMETRES TIR A LA CORDE *******************************/
const float INCREMENT = 0.05; // De combien avance la corde lorsqu'on appuie sur le bouton const float ROPE_SMOOTH = 1.0; // Paramètre servant a lisser la position de la corde
const float WAVE_LENGTH = 0.2; // Taille des ondes const float WAVE_SPEED = 1.0; // Vitesse des ondes
/******************************
VARIABLES GENERALES ******************************/
// Players enum Player {
PERSONNE, PLAYER1, PLAYER2
};
// Etats du jeu enum GameState {
START, PONG, // Mode pong TAC // Mode tir a la corde
};
unsigned long lastMillis = 0; unsigned long beginTimer = 0; unsigned int counter = 0;
Player player = PLAYER1; // Prochain joueur a appuyer sur son bouton Player lastWinner = PERSONNE;
CRGB leds[NUM_LEDS]; GameState gameState = START;
/**********************************************
FONCTIONS DE FONCTIONNEMENT GENERALES **********************************************/
// Fonction permettant de changer la couleur d'une LED relativement au player 1 ou 2 void ledColor(Player player, int pos, CRGB color) {
if (player == PLAYER1) { leds[pos] = color; } else // player == PLAYER2 { leds[NUM_LEDS - pos - 1] = color; }
}
// Fonction permettant de changer la couleur d'une LED void ledColor(int pos, CRGB color) {
leds[pos] = color;
}
/*********************************************
VARIABLES DE FONCTIONNEMENT DU PONG *********************************************/
float ballSpeed = BALL_SPEED; // Vitesse de la balle float ballPosition = 1; // Position de la balle sur la bande de led (Si ballPosition = 0, la balle est devant le player 1. Si ballPosition = 1, la balle est devant le player 2) int player1Score = 0; // Score du player 1 int player2Score = 0; // Score du player 2
/**********************************************
FONCTIONS DE FONCTIONNEMENT DU PONG **********************************************/
// Fonction servant a afficher les scores void showScore() {
// On commence par effacer toutes les couleurs de led FastLED.clear();
// On allume le nombre de led correspondant au score du player 1 for (int i = 0; i < player1Score; i++) { ledColor(PLAYER1, NUM_LEDS / 2 - (i + 1), PLAYER1_COLOR); }
// On allume le nombre de led correspondant au score du player 2 for (int i = 0; i < player2Score; i++) { ledColor(PLAYER2, NUM_LEDS / 2 - (i + 1), PLAYER2_COLOR); }
// On envoie les nouvelles couleurs a la bande de led FastLED.show();
// On fait clignotter trois fois if (lastWinner == PLAYER1) { for (int i = 0; i < 3; i++) { // On eteint la derniere LED pendant 0.5s delay(500); ledColor(PLAYER1, NUM_LEDS / 2 - player1Score, NOIR); FastLED.show();
// On allume la derniere LED pendant 0.5s delay(500); ledColor(PLAYER1, NUM_LEDS / 2 - player1Score, PLAYER1_COLOR); FastLED.show(); } } else // lastWinner == PLAYER2 { for (int i = 0; i < 3; i++) { // On eteint la derniere LED pendant 0.5s delay(500); ledColor(PLAYER2, NUM_LEDS / 2 - player2Score, NOIR); FastLED.show();
// On allume la derniere LED pendant 0.5s delay(500); ledColor(PLAYER2, NUM_LEDS / 2 - player2Score, PLAYER2_COLOR); FastLED.show(); } }
// Si la partie est terminee on va a l'affichage de fin if (player1Score == MAX_SCORE || player2Score == MAX_SCORE) { gameState = START;
// On reinitialise les scores player1Score = 0; player2Score = 0;
// On reinitialise la vitesse ballSpeed = BALL_SPEED;
// On reinitialise les leds FastLED.clear(); } // Sinon on reprend le jeu else { gameState = PONG; ballSpeed = BALL_SPEED; }
}
/***************************************
FONCTION PRINCIPALE DU PONG ***************************************/
void pong_loop() {
// Calcul du temps ecoule entre deux boucles unsigned long currentMillis = millis();
// On calcule le numero de la LED allumee int ballLed = ballPosition * NUM_LEDS;
// On s'assure que la position de la balle ne depasse pas la taille de la bande de LED ballLed = min(ballLed, NUM_LEDS - 1);
// On regarde qui est en train de jouer if (player == PLAYER1) { // On regarde si le player a appuye sur son bouton et si le delai de debut de jeu est passe if (digitalRead(BUTTON1_PIN) == LOW && currentMillis - beginTimer > 500) { // Si la balle est hors de la zone de tir, l'autre player marque un point if (ballLed >= HIT_ZONE) { player2Score += 1; lastWinner = PLAYER2; ballPosition = 0;
// On joue le son de defaite tone(HAUT_PARLEUR_PIN, LOOSE_SOUND, 800);
// On passe en mode affichage des scores showScore();
// C'est a l'autre player de jouer player = PLAYER2;
// Actualisation de la variable lastMillis lastMillis = millis(); } else { // On accelere la balle ballSpeed *= 1.0 + ACCELERATION / 100;
// C'est a l'autre player de jouer player = PLAYER2;
// On joue la note de musique tone(HAUT_PARLEUR_PIN, PONG_SOUND, 100); }
return; }
// On fait avancer la balle ballPosition -= ballSpeed * (currentMillis - lastMillis) * 0.001f;
// On regarde si la balle est sortie de la zone if (ballPosition < 0.0f) { // Si oui le player 2 marque un point player2Score += 1; lastWinner = PLAYER2; ballPosition = 0;
// On joue le son de defaite tone(HAUT_PARLEUR_PIN, LOOSE_SOUND, 800);
// On passe en mode affichage des scores showScore();
// C'est a l'autre player de jouer player = PLAYER2;
// Actualisation de la variable lastMillis lastMillis = millis(); return; } } else // player == PLAYER2 { // On regarde si le player a appuye sur son bouton et si le delai de debut de jeu est passe if (digitalRead(BUTTON2_PIN) == LOW && currentMillis - beginTimer > 500) { // Si la balle est hors de la zone de tir, l'autre player marque un point if (ballLed < NUM_LEDS - HIT_ZONE) { player1Score += 1; lastWinner = PLAYER1; ballPosition = 1;
// On joue le son de defaite tone(HAUT_PARLEUR_PIN, LOOSE_SOUND, 800);
// On passe en mode affichage des scores showScore();
// C'est a l'autre player de jouer player = PLAYER1;
// Actualisation de la variable lastMillis lastMillis = millis(); } else { // On accelere la balle ballSpeed *= 1.1;
// C'est a l'autre player de jouer player = PLAYER1;
// On joue la note de musique tone(HAUT_PARLEUR_PIN, PONG_SOUND, 100); }
return; }
// On fait avancer la balle dans l'autre sens ballPosition += ballSpeed * (currentMillis - lastMillis) * 0.001f;
// On regarde si la balle est sortie de la zone if (ballPosition >= 1) { // Si oui le player 1 marque un point player1Score += 1; lastWinner = PLAYER1; ballPosition = 1;
// On joue le son de defaite tone(HAUT_PARLEUR_PIN, LOOSE_SOUND, 800);
// On passe en mode affichage des scores showScore(); // C'est a l'autre player de jouer player = PLAYER1;
// Actualisation de la variable lastMillis lastMillis = millis(); return; } }
///// AFFICHAGE BANDE DE LEDs ///// // Premierement on efface toutes les couleurs precedentes FastLED.clear();
// Ensuite on allume faiblement les LEDs correspondant a la zone de chaque cote for (int i = 0; i < HIT_ZONE; i++) { // On allume de chaque cote ledColor(PLAYER1, i, PLAYER1_COLOR / 10); // On divise la couleur par 10 pour la rendre 10 fois moins puissante ledColor(PLAYER2, i, PLAYER2_COLOR / 10); }
// Ensuite on allume faiblement les LEDs correspondant aux scores // Pour le player 1 for (int i = 0; i < player1Score; i++) { ledColor(PLAYER1, NUM_LEDS / 2 - (i + 1), PLAYER1_COLOR / 15); } // Pour le player 2 for (int i = 0; i < player2Score; i++) { ledColor(PLAYER2, NUM_LEDS / 2 - (i + 1), PLAYER2_COLOR / 15); }
// Ensuite on actualise la position de la balle // On donne la couleur de la led en fonction de si la balle est dans la zone d'un player ou non
// Si la balle est dans le camp d'un des player, elle est rouge. if (ballLed < HIT_ZONE || ballLed >= NUM_LEDS - HIT_ZONE) { ledColor(ballLed, ROUGE); } // Si elle en est proche, elle est jaune else if (ballLed < 2 * HIT_ZONE || ballLed >= NUM_LEDS - 2 * HIT_ZONE) { ledColor(ballLed, JAUNE); } // Sinon la balle a sa couleur par defaut else { ledColor(ballLed, BALL_COLOR); }
// On envoie la couleur des leds a la bande de leds FastLED.show();
// On actualise la variable lastMillis pour la boucle suivante lastMillis = currentMillis;
}
/*******************************************************
VARIABLES DE FONCTIONNEMENT DU TIR A LA CORDE *******************************************************/
float ropePosition = 0.5; // Position non lissee de la corde float displayedRopePosition = 0.5; // Position lissee de la corde bool player1Pushed = false; bool player2Pushed = false;
float player1Waves[10] = { 0 }; float player2Waves[10] = { 0 };
/***********************************************
FONCTION PRINCIPALE DU TIR A LA CORDE ***********************************************/
void tac_loop() {
// Calcul des temps unsigned long ms = millis(); float dt = (ms - lastMillis) * 0.001f; lastMillis = ms;
// Si moins d'une seconde s'est ecoule depuis le debut, on ne fait rien (pour eviter d'appuyer sans faire expres) if (ms - beginTimer < 1000) return;
bool player1Push = !digitalRead(BUTTON1_PIN); bool player2Push = !digitalRead(BUTTON2_PIN);
if (player1Push && !player1Pushed) { // On incremente la position de la corde ropePosition += INCREMENT;
// On lance une nouvelle onde for (int i = 0; i < 10; i++) { if (player1Waves[i] == 0.0f) { //player1Waves[i] = WAVE_SPEED * dt; player1Waves[i] = 0.01f; break; } } } if (player2Push && !player2Pushed) { // On incremente la position de la corde ropePosition -= INCREMENT;
// On lance une nouvelle onde for (int i = 0; i < 10; i++) { if (player2Waves[i] == 0.0f) { player2Waves[i] = 0.99f; break; } } }
// Memorisation des etats des boutons player1Pushed = player1Push; player2Pushed = player2Push;
// On calcule la position lissee de la corde (displayedRopePosition correspond a la variable ropePosition avec un lissage supplementaire) displayedRopePosition += (ropePosition - displayedRopePosition) * dt / ROPE_SMOOTH;
// On regarde si player 1 a gagne if (displayedRopePosition >= 1.0f) { lastWinner = PLAYER1; gameState = START; beginTimer = millis();
ropePosition = 0.5f; displayedRopePosition = 0.5f;
FastLED.clear(); return; } // On regarde si player 2 a gagne else if (displayedRopePosition <= 0.0f) { lastWinner = PLAYER2; gameState = START; beginTimer = millis();
ropePosition = 0.5f; displayedRopePosition = 0.5f;
FastLED.clear(); return; }
// On propage les ondes float dx = WAVE_SPEED * dt;
for (int wave = 0; wave < 10; wave++) { // Ondes player 1 if (player1Waves[wave] != 0.0f) { player1Waves[wave] += dx;
// Si toute l'onde depasse le point de la corde, on l'arrete if (player1Waves[wave] - WAVE_LENGTH > displayedRopePosition) { player1Waves[wave] = 0.0f; } }
// Ondes player 2 if (player2Waves[wave] != 0.0f) { player2Waves[wave] -= dx;
// Si toute l'onde depasse le point de la corde, on l'arrete if (player2Waves[wave] + WAVE_LENGTH < displayedRopePosition) { player2Waves[wave] = 0.0f; } } }
// On actualise la bande de leds float ledLuminosity; float ledPosition;
FastLED.clear();
// On commence par calculer le numero de la led correspondant a la position de la corde int ropeLedPosition = NUM_LEDS * displayedRopePosition;
// On dessine la zone du player 1 // Pour cela on calcule la luminosite led par led for (int led = 0; led < ropeLedPosition; led++) { // La luminosite minimale d'une led est 10% de la luminosite max ledLuminosity = 0.1f; ledPosition = float(led) / float(NUM_LEDS);
// Pour chaque onde, on ajoute la luminosite a la led for (int wave = 0; wave < 10; wave++) { // Si l'onde n'est pas active ou si l'onde est avant la led, on n'ajoute aucune luminosite if (player1Waves[wave] == 0.0f || player1Waves[wave] < ledPosition) continue;
// On ajoute de la luminosite de facon decroissante. plus l'onde est loin, moins on ajoute de luminosite. ledLuminosity += max(0.0f, 0.9f - (player1Waves[wave] - ledPosition) / WAVE_LENGTH); } // La valeur maximale de luminosite est 1.0 if (ledLuminosity > 2.0f) ledLuminosity = 2.0f;
// On actualise la valeur de luminosite de la led ledColor(led, PLAYER1_COLOR / int(2.0f / ledLuminosity) / 5); }
// On dessine la zone du player 2 for (int led = ropeLedPosition + 1; led < NUM_LEDS; led++) { // La luminosite minimale d'une led est 10% de la luminosite max ledLuminosity = 0.1f; ledPosition = float(led) / float(NUM_LEDS);
// Pour chaque onde, on ajoute la luminosite a la led for (int wave = 0; wave < 10; wave++) { // Si l'onde n'est pas active ou si l'onde est avant la led, on n'ajoute aucune luminosite if (player2Waves[wave] == 0.0f || ledPosition > player2Waves[wave]) continue;
// On ajoute de la luminosite de facon decroissante. plus l'onde est loin, moins on ajoute de luminosite. ledLuminosity += max(0.0f, 0.9f - (player2Waves[wave] - ledPosition) / WAVE_LENGTH); } // La valeur maximale de luminosite est 1.0 if (ledLuminosity > 2.0f) ledLuminosity = 2.0f;
// On actualise la valeur de luminosite de la led ledColor(led, PLAYER2_COLOR / int(1.0f / ledLuminosity) / 5); }
// On actualise la led correspondant a la position de la corde ledColor(ropeLedPosition, BLANC); FastLED.show();
}
/****************************************************************
Cette fonction s'execute une fois lorsque la carte s'allume. ****************************************************************/
void setup() {
// Initialisation des LEDs FastLED.addLeds<WS2812B, LED_PIN, GRB>(leds, NUM_LEDS);
// Initialisations des boutons pinMode(BUTTON1_PIN, INPUT_PULLUP); pinMode(BUTTON2_PIN, INPUT_PULLUP);
// Initialisation du haut parleur pinMode(HAUT_PARLEUR_PIN, OUTPUT);
// COULEUR DES LEDS EN DEBUT DE PARTIE FastLED.clear(); // Couleurs player 1 ledColor(PLAYER1, 0, PLAYER1_COLOR); ledColor(PLAYER1, 1, PLAYER1_COLOR); ledColor(PLAYER1, 2, PLAYER1_COLOR); ledColor(PLAYER1, 3, PLAYER1_COLOR); ledColor(PLAYER1, 4, PLAYER1_COLOR);
// Couleurs player 2 ledColor(PLAYER2, 0, PLAYER2_COLOR); ledColor(PLAYER2, 1, PLAYER2_COLOR); ledColor(PLAYER2, 2, PLAYER2_COLOR); ledColor(PLAYER2, 3, PLAYER2_COLOR); ledColor(PLAYER2, 4, PLAYER2_COLOR);
// On envoie les changements a la bande de leds FastLED.show();
// Initialisation du temps d'allumage beginTimer = millis();
}
/*************************************************************
Cette fonction s'execute en continue tout au long du jeu. *************************************************************/
void loop() {
switch (gameState) { case START: // Si un player a gagne, on affiche l'arc en ciel du cote du vainqueur if (lastWinner == PLAYER1) { fill_rainbow(leds, NUM_LEDS / 2, counter++, 7); FastLED.show(); } else if (lastWinner == PLAYER2) { fill_rainbow(leds + NUM_LEDS / 2, NUM_LEDS / 2, counter++, 7); FastLED.show(); }
// On regarde si un temps minimim s'est ecoule et si un des boutons est appuye if (millis() - beginTimer > 1000 && (digitalRead(BUTTON1_PIN) == LOW || digitalRead(BUTTON2_PIN) == LOW)) { unsigned long pushBegin = millis();
while (digitalRead(BUTTON1_PIN) == LOW || digitalRead(BUTTON2_PIN) == LOW);
if (millis() - pushBegin < 1000) { gameState = PONG; } else { gameState = TAC; }
// On joue la note de musique tone(HAUT_PARLEUR_PIN, PONG_SOUND, 100);
// Initialisation de la varable lastMillis lastMillis = millis();
// Initialisation du temps de debut de jeu beginTimer = millis(); }
break;
case PONG: pong_loop(); break;
case TAC: tac_loop(); break; }
}
Étapes de fabrication
Voici toutes les étapes de fabrication de notre projet :
Étape 1
Tout d'abord on se rend compte du matériel qu'on nous donne et on regarde les différentes étapes à suivre sur le Wikidébrouillard
Étape 2
On soude des fils électriques au ruban de LEDS et sur les pattes des interrupteurs
Étape 2-1
En même temps, on construit le support sur lequel le jeu va être joué
Étape 3
On soude aussi les broches de support sur la carte Arduino
Étape 4
On réalise le câblage sur la plaquette qui nous a été fourni pour pouvoir tester le code
Étape 5
Puis on commence à mettre tous nos composants sur la maquette réelle
Étape 6
Ensuite, le jeu peut être utilisé !