Note

Bonjour et bienvenue dans la communauté Facebook des passionnés de SunFounder Raspberry Pi, Arduino et ESP32 ! Plongez plus profondément dans l’univers du Raspberry Pi, de l’Arduino et de l’ESP32 avec d’autres passionnés.

Pourquoi nous rejoindre ?

  • Support d’experts : Résolvez les problèmes post-achat et les défis techniques grâce à l’aide de notre communauté et de notre équipe.

  • Apprenez & Partagez : Échangez des astuces et des tutoriels pour améliorer vos compétences.

  • Aperçus exclusifs : Accédez en avant-première aux nouvelles annonces de produits et aux avant-goûts.

  • Réductions spéciales : Profitez de remises exclusives sur nos derniers produits.

  • Promotions festives et cadeaux : Participez à nos tirages au sort et à nos promotions de saison.

👉 Prêt à explorer et à créer avec nous ? Cliquez sur [Ici] et rejoignez-nous dès aujourd’hui !

3.1.14 JEU – Not Not

Introduction

Dans cette leçon, nous allons créer un dispositif de jeu amusant, que nous appelons « Not Not ».

Pendant le jeu, la matrice de points affichera aléatoirement une flèche. Votre objectif est d’appuyer sur le bouton opposé à la direction de la flèche dans un temps limité. Si le temps est écoulé, ou si vous appuyez sur le bouton correspondant à la même direction que la flèche, vous perdez.

Ce jeu est idéal pour entraîner votre pensée inversée. Alors, on essaie ?

Composants nécessaires

Pour ce projet, nous aurons besoin des composants suivants :

../_images/3.1.14_game_not_not_list.png

Schéma de câblage

T-Board Name

physical

wiringPi

BCM

GPIO17

Pin 11

0

17

GPIO18

Pin 12

1

18

GPIO27

Pin 13

2

27

GPIO20

Pin 38

28

20

GPIO26

Pin 37

25

26

../_images/3.1.14_game_not_not_schematic.png

Procédures expérimentales

Étape 1 : Construisez le circuit.

../_images/3.1.14_game_not_not_circuit.png

Étape 2 : Ouvrez le fichier du code.

cd ~/davinci-kit-for-raspberry-pi/python-pi5

Étape 3 : Exécutez le programme.

sudo python3 3.1.14_MotionControl.py

Après le démarrage du programme, une flèche pointant vers la gauche ou la droite apparaît sur la matrice de points. Vous devez appuyer sur le bouton dans la direction opposée à la flèche dans un temps limité. Ensuite, le symbole "" s’affichera sur la matrice de points. Si le temps est écoulé ou si vous appuyez sur le bouton correspondant à la même direction que la flèche, vous êtes éliminé et la matrice affichera "x". Vous pouvez également ajouter deux nouveaux boutons ou les remplacer par des touches de joystick pour les directions haut, bas, gauche et droite afin d’augmenter la difficulté du jeu.

Avertissement

Si vous recevez le message d’erreur RuntimeError: Cannot determine SOC peripheral base address, veuillez vous référer à Si gpiozero ne fonctionne pas.

Code

Note

Vous pouvez Modifier/Réinitialiser/Copier/Exécuter/Arrêter le code ci-dessous. Mais avant cela, vous devez vous rendre dans le chemin source, tel que davinci-kit-for-raspberry-pi/python-pi5. Après avoir modifié le code, vous pouvez l’exécuter directement pour voir le résultat.

#!/usr/bin/env python3
from gpiozero import OutputDevice, Button
import time
import threading
import random

# Broches GPIO pour le registre à décalage 74HC595
SDI = OutputDevice(17)   # Entrée de données série
RCLK = OutputDevice(18)  # Horloge du registre
SRCLK = OutputDevice(27) # Horloge du registre à décalage

# Broches GPIO pour les boutons
AButtonPin = Button(20)  # Bouton A
BButtonPin = Button(26)  # Bouton B

# Initialisation des variables de jeu
timerPlay = 0
timerCheck = 0
waypoint = "NULL"
stage = "NULL"

# Glyphes des flèches pour l'affichage sur la matrice LED
arrow = {
    "right": [0xFF, 0xEF, 0xDF, 0x81, 0xDF, 0xEF, 0xFF, 0xFF],
    "left": [0xFF, 0xF7, 0xFB, 0x81, 0xFB, 0xF7, 0xFF, 0xFF]
}

# Glyphes de retour pour les réponses correctes/incorrectes
check = {
    "wrong": [0xFF, 0xBB, 0xD7, 0xEF, 0xD7, 0xBB, 0xFF, 0xFF],
    "right": [0xFF, 0xFF, 0xF7, 0xEB, 0xDF, 0xBF, 0xFF, 0xFF]
}

def hc595_shift(dat):
    """ Shift data to the 74HC595 shift register. """
    for i in range(8):
        SDI.value = 0x80 & (dat << i)
        SRCLK.on()
        SRCLK.off()

def display(glyphCode):
    """ Display a glyph on the LED matrix. """
    for i in range(0, 8):
        hc595_shift(glyphCode[i])
        hc595_shift(0x80 >> i)
        RCLK.on()
        RCLK.off()

def creatGlyph():
    """ Create a new glyph for the game and start the play timer. """
    global waypoint, stage, timerPlay
    waypoint = random.choice(list(arrow.keys()))
    stage = "PLAY"
    timerPlay = threading.Timer(2.0, timeOut)
    timerPlay.start()

def checkPoint(inputKey):
    """ Check player's input and update game state. """
    global waypoint, stage, timerCheck
    if inputKey == "empty" or inputKey == waypoint:
        waypoint = "wrong"
    else:
        waypoint = "right"
    timerPlay.cancel()
    stage = "CHECK"
    timerCheck = threading.Timer(1.0, creatGlyph)
    timerCheck.start()

def timeOut():
    """ Handle game timeout scenario. """
    checkPoint("empty")

def getKey():
    """ Detect button press and trigger checkpoint. """
    if AButtonPin.is_pressed and not BButtonPin.is_pressed:
        checkPoint("right")
    elif not AButtonPin.is_pressed and BButtonPin.is_pressed:
        checkPoint("left")

def main():
    """ Main game loop. """
    creatGlyph()
    while True:
        if stage == "PLAY":
            display(arrow[waypoint])
            getKey()
        elif stage == "CHECK":
            display(check[waypoint])

def destroy():
    """ Clean up resources on program exit. """
    global timerPlay, timerCheck
    timerPlay.cancel()  # Annule le minuteur de jeu
    timerCheck.cancel()  # Annule le minuteur de vérification

# Exécute le jeu et gère l'interruption clavier pour une sortie propre
try:
    main()
except KeyboardInterrupt:
    destroy()

Explication du Code

Basé sur la leçon 1.1.6 Matrice LED, cette activité ajoute 2 boutons pour créer un dispositif de jeu amusant. Si vous n’êtes pas encore très familier avec la matrice LED, veuillez vous référer à 1.1.6 Matrice de LED.

  1. Le code commence par importer les bibliothèques nécessaires. gpiozero est utilisé pour interagir avec les broches GPIO, comme les boutons et les dispositifs de sortie. time permet d’ajouter des délais, threading permet d’exécuter plusieurs tâches simultanément, et random est utile pour introduire de l’aléatoire dans le projet.

    #!/usr/bin/env python3
    from gpiozero import OutputDevice, Button
    import time
    import threading
    import random
    
  2. Initialisation des broches GPIO pour le registre à décalage (SDI, RCLK, SRCLK) et pour les boutons (AButtonPin, BButtonPin). Le registre à décalage permet de contrôler plusieurs LED avec moins de broches GPIO, ce qui est essentiel pour l’affichage sur la matrice LED.

    # Broches GPIO pour le registre à décalage 74HC595
    SDI = OutputDevice(17)   # Entrée de données série
    RCLK = OutputDevice(18)  # Horloge du registre
    SRCLK = OutputDevice(27) # Horloge du registre à décalage
    
    # Broches GPIO pour les boutons
    AButtonPin = Button(20)  # Bouton A
    BButtonPin = Button(26)  # Bouton B
    
  3. Initialisation des variables utilisées dans la logique du jeu, telles que les minuteurs et les indicateurs d’état du jeu.

    # Initialisation des variables de jeu
    timerPlay = 0
    timerCheck = 0
    waypoint = "NULL"
    stage = "NULL"
    
  4. Définit les motifs binaires pour afficher les flèches et les retours (correct/incorrect) sur la matrice LED. Chaque élément du tableau représente une ligne de la matrice LED, où 1 et 0 correspondent respectivement à une LED allumée ou éteinte.

    # Glyphes de flèches pour l'affichage sur la matrice LED
    arrow = {
        "right": [0xFF, 0xEF, 0xDF, 0x81, 0xDF, 0xEF, 0xFF, 0xFF],
        "left": [0xFF, 0xF7, 0xFB, 0x81, 0xFB, 0xF7, 0xFF, 0xFF]
    }
    
    # Glyphes de retour pour les réponses correctes/incorrectes
    check = {
        "wrong": [0xFF, 0xBB, 0xD7, 0xEF, 0xD7, 0xBB, 0xFF, 0xFF],
        "right": [0xFF, 0xFF, 0xF7, 0xEB, 0xDF, 0xBF, 0xFF, 0xFF]
    }
    
  5. Cette fonction transfère un octet de données vers le registre à décalage 74HC595. Elle parcourt chaque bit de l’octet dat, règle la broche SDI sur haut ou bas en conséquence, et bascule la broche SRCLK pour déplacer le bit dans le registre.

    def hc595_shift(dat):
        """ Shift data to the 74HC595 shift register. """
        for i in range(8):
            SDI.value = 0x80 & (dat << i)
            SRCLK.on()
            SRCLK.off()
    
  6. Cette fonction affiche un glyphe sur la matrice LED. Elle envoie chaque ligne du glyphe (représentée par glyphCode) et l’adresse de la ligne au registre à décalage en utilisant hc595_shift, puis bascule la broche RCLK pour mettre à jour l’affichage.

    def display(glyphCode):
        """ Display a glyph on the LED matrix. """
        for i in range(0, 8):
            hc595_shift(glyphCode[i])
            hc595_shift(0x80 >> i)
            RCLK.on()
            RCLK.off()
    
  7. Cette fonction sélectionne aléatoirement un glyphe du dictionnaire arrow, démarre le minuteur de jeu et définit l’état du jeu à « PLAY ». threading.Timer est utilisé pour le contrôle du temps dans le jeu.

    def creatGlyph():
        """ Create a new glyph for the game and start the play timer. """
        global waypoint, stage, timerPlay
        waypoint = random.choice(list(arrow.keys()))
        stage = "PLAY"
        timerPlay = threading.Timer(2.0, timeOut)
        timerPlay.start()
    
  8. Cette fonction vérifie l’entrée du joueur par rapport au glyphe actuel. Si l’entrée est correcte, elle règle le waypoint sur « right », sinon sur « wrong ». Elle annule ensuite le minuteur de jeu actuel et démarre un nouveau minuteur pour le prochain glyphe.

    def checkPoint(inputKey):
        """ Check player's input and update game state. """
        global waypoint, stage, timerCheck
        if inputKey == "empty" or inputKey == waypoint:
            waypoint = "wrong"
        else:
            waypoint = "right"
        timerPlay.cancel()
        stage = "CHECK"
        timerCheck = threading.Timer(1.0, creatGlyph)
        timerCheck.start()
    
  9. Cette fonction est appelée lorsque le temps imparti est écoulé. Elle invoque checkPoint avec « empty » pour indiquer qu’aucun bouton n’a été pressé à temps.

    def timeOut():
        """ Handle game timeout scenario. """
        checkPoint("empty")
    
  10. Cette fonction vérifie l’état des boutons. Si AButtonPin est pressé (et BButtonPin ne l’est pas), elle appelle checkPoint avec « right ». Si BButtonPin est pressé (et AButtonPin ne l’est pas), elle appelle checkPoint avec « left ».

    def getKey():
        """ Detect button press and trigger checkpoint. """
        if AButtonPin.is_pressed and not BButtonPin.is_pressed:
            checkPoint("right")
        elif not AButtonPin.is_pressed and BButtonPin.is_pressed:
            checkPoint("left")
    
  11. La fonction main contrôle le déroulement du jeu. Elle commence par créer un glyphe, puis vérifie continuellement l’état du jeu. Si l’état est « PLAY », elle affiche le glyphe actuel et vérifie les pressions de bouton. Si l’état est « CHECK », elle affiche le retour d’information en fonction de l’action du joueur.

    def main():
        """ Main game loop. """
        creatGlyph()
        while True:
            if stage == "PLAY":
                display(arrow[waypoint])
                getKey()
            elif stage == "CHECK":
                display(check[waypoint])
    
  12. Cette fonction annule tous les minuteurs en cours lors de la sortie du programme, assurant ainsi une fermeture propre.

    def destroy():
        """ Clean up resources on program exit. """
        global timerPlay, timerCheck
        timerPlay.cancel()  # Annule le minuteur de jeu
        timerCheck.cancel()  # Annule le minuteur de vérification
    
  13. Le jeu est exécuté dans un bloc try. Si une KeyboardInterrupt (comme en appuyant sur Ctrl+C) se produit, l’exception est interceptée et destroy est appelé pour nettoyer avant de quitter.

    # Exécute le jeu et gère l'interruption clavier pour une sortie propre
    try:
        main()
    except KeyboardInterrupt:
        destroy()