Créer Flappy Bird avec Pygame

PythonPythonBeginner
Pratiquer maintenant

This tutorial is from open-source community. Access the source code

💡 Ce tutoriel est traduit par l'IA à partir de la version anglaise. Pour voir la version originale, vous pouvez cliquer ici

Introduction

Dans ce projet, nous allons décomposer le code pour créer un jeu Flappy Bird simple en utilisant la bibliothèque Pygame en étapes gérables. En suivant ces étapes, vous apprendrez à construire le jeu progressivement. Chaque étape comprendra une brève explication, des blocs de code et des commentaires pour vous aider à comprendre et à implémenter le jeu. Commençons!

👀 Aperçu

Aperçu du jeu Flappy Bird

🎯 Tâches

Dans ce projet, vous apprendrez :

  • Comment configurer les fichiers du projet pour le jeu Flappy Bird
  • Comment afficher l'animation d'accueil du jeu
  • Comment implémenter la logique principale du jeu Flappy Bird
  • Comment afficher l'écran de fin de partie lorsque le joueur perd
  • Comment définir des fonctions d'aide pour le jeu

🏆 Réalisations

Après avoir terminé ce projet, vous serez capable de :

  • Utiliser la bibliothèque Pygame pour créer des jeux
  • Comprendre les concepts de développement de jeux tels que les boucles de jeu, les collisions et l'animation

Créer les fichiers du projet

Tout d'abord, nous allons créer les fichiers du projet pour le jeu Flappy Bird.

cd ~/projet
touch flappy.py
sudo pip install pygame

Dans cette étape, nous allons configurer la structure de base du projet et importer les bibliothèques nécessaires. Nous allons également définir quelques constantes et charger les actifs de jeu initiaux.

from itertools import cycle
import random
import sys
import pygame
from pygame.locals import *

FPS = 30
SCREENWIDTH = 288
SCREENHEIGHT = 512
PIPEGAPSIZE = 100  ## espace entre la partie supérieure et la partie inférieure du tuyau
BASEY = SCREENHEIGHT * 0.79
## dictionnaires d'images et de hitmask
IMAGES, HITMASKS = {}, {}

## liste de tous les joueurs possibles (tuple de 3 positions du battement d'ailes)
PLAYERS_LIST = (
    ## oiseau rouge
    (
        "data/sprites/redbird-upflap.png",
        "data/sprites/redbird-midflap.png",
        "data/sprites/redbird-downflap.png",
    ),
    ## oiseau bleu
    (
        "data/sprites/bluebird-upflap.png",
        "data/sprites/bluebird-midflap.png",
        "data/sprites/bluebird-downflap.png",
    ),
    ## oiseau jaune
    (
        "data/sprites/yellowbird-upflap.png",
        "data/sprites/yellowbird-midflap.png",
        "data/sprites/yellowbird-downflap.png",
    ),
)

## liste d'arrière-plans
BACKGROUNDS_LIST = (
    "data/sprites/background-day.png",
    "data/sprites/background-night.png",
)

## liste de tuyaux
PIPES_LIST = (
    "data/sprites/pipe-green.png",
    "data/sprites/pipe-red.png",
)
  • Nous importons les bibliothèques nécessaires pour le jeu, y compris pygame pour créer le jeu, random pour générer des éléments aléatoires, sys pour les fonctions liées au système et pygame.locals pour les constantes de touches.
  • Nous initialisons Pygame avec pygame.init().
  • Des constantes telles que FPS, SCREENWIDTH, SCREENHEIGHT, PIPEGAPSIZE et BASEY sont définies pour configurer les dimensions et la vitesse du jeu.
  • Nous créons des dictionnaires vides (IMAGES et HITMASKS) pour stocker les actifs de jeu.
  • Des listes d'actifs de joueurs, d'arrière-plans et de tuyaux sont définies à l'aide de chemins de fichiers.
✨ Vérifier la solution et pratiquer

Afficher l'animation d'accueil

Dans cette étape, nous allons créer l'animation de l'écran d'accueil du jeu Flappy Bird.

def showWelcomeAnimation():
    """Affiche l'animation de l'écran d'accueil du Flappy Bird"""
    ## indice du joueur à afficher à l'écran
    playerIndex = 0
    playerIndexGen = cycle([0, 1, 2, 1])
    ## itérateur utilisé pour changer playerIndex après chaque 5ème itération
    loopIter = 0

    playerx = int(SCREENWIDTH * 0.2)
    playery = int((SCREENHEIGHT - IMAGES["player"][0].get_height()) / 2)

    messagex = int((SCREENWIDTH - IMAGES["message"].get_width()) / 2)
    messagey = int(SCREENHEIGHT * 0.12)

    basex = 0
    ## montant par lequel la base peut maximum se déplacer vers la gauche
    baseShift = IMAGES["base"].get_width() - IMAGES["background"].get_width()

    ## mouvement vertical du joueur sur l'écran d'accueil
    playerShmVals = {"val": 0, "dir": 1}

    while True:
        for event in pygame.event.get():
            if event.type == QUIT or (event.type == KEYDOWN and event.key == K_ESCAPE):
                pygame.quit()
                sys.exit()
            if event.type == KEYDOWN and (event.key == K_SPACE or event.key == K_UP):
                ## jouer le premier son de battement d'ailes et retourner les valeurs pour mainGame
                return {
                    "playery": playery + playerShmVals["val"],
                    "basex": basex,
                    "playerIndexGen": playerIndexGen,
                }

        ## ajuster playery, playerIndex, basex
        if (loopIter + 1) % 5 == 0:
            playerIndex = next(playerIndexGen)
        loopIter = (loopIter + 1) % 30
        basex = -((-basex + 4) % baseShift)
        playerShm(playerShmVals)

        ## dessiner les sprites
        SCREEN.blit(IMAGES["background"], (0, 0))
        SCREEN.blit(
            IMAGES["player"][playerIndex], (playerx, playery + playerShmVals["val"])
        )
        SCREEN.blit(IMAGES["message"], (messagex, messagey))
        SCREEN.blit(IMAGES["base"], (basex, BASEY))

        pygame.display.update()
        FPSCLOCK.tick(FPS)
  • Nous définissons la fonction showWelcomeAnimation responsable de l'affichage de l'animation de l'écran d'accueil.
  • La fonction configure des variables pour l'animation et gère l'entrée utilisateur pour démarrer le jeu.
  • Elle utilise une boucle pour mettre à jour les images de l'animation et vérifier l'entrée utilisateur pour commencer le jeu.
  • L'animation comprend l'oiseau battant des ailes et un message affiché à l'écran.
  • La fonction pygame.display.update() est utilisée pour mettre à jour l'affichage, et FPSCLOCK.tick(FPS) contrôle le taux de rafraîchissement.
✨ Vérifier la solution et pratiquer

Logique principale du jeu

Dans cette étape, nous allons implémenter la logique principale du jeu Flappy Bird.

def mainGame(movementInfo):
    score = playerIndex = loopIter = 0
    playerIndexGen = movementInfo["playerIndexGen"]
    playerx, playery = int(SCREENWIDTH * 0.2), movementInfo["playery"]

    basex = movementInfo["basex"]
    baseShift = IMAGES["base"].get_width() - IMAGES["background"].get_width()

    ## obtenir 2 nouveaux tuyaux pour ajouter à la liste upperPipes lowerPipes
    newPipe1 = getRandomPipe()
    newPipe2 = getRandomPipe()

    ## liste des tuyaux supérieurs
    upperPipes = [
        {"x": SCREENWIDTH + 200, "y": newPipe1[0]["y"]},
        {"x": SCREENWIDTH + 200 + (SCREENWIDTH / 2), "y": newPipe2[0]["y"]},
    ]

    ## liste des tuyaux inférieurs
    lowerPipes = [
        {"x": SCREENWIDTH + 200, "y": newPipe1[1]["y"]},
        {"x": SCREENWIDTH + 200 + (SCREENWIDTH / 2), "y": newPipe2[1]["y"]},
    ]

    dt = FPSCLOCK.tick(FPS) / 1000
    pipeVelX = -128 * dt

    ## vitesse du joueur, vitesse maximale, accélération vers le bas, accélération lors du battement d'ailes
    playerVelY = -9  ## vitesse du joueur le long de Y, par défaut la même que playerFlapped
    playerMaxVelY = 10  ## vitesse maximale le long de Y, vitesse maximale de descente
    playerMinVelY = -8  ## vitesse minimale le long de Y, vitesse maximale d'ascension
    playerAccY = 1  ## accélération vers le bas du joueur
    playerRot = 45  ## rotation du joueur
    playerVelRot = 3  ## vitesse angulaire
    playerRotThr = 20  ## seuil de rotation
    playerFlapAcc = -9  ## vitesse du joueur lors du battement d'ailes
    playerFlapped = False  ## True lorsque le joueur bat des ailes

    while True:
        for event in pygame.event.get():
            if event.type == QUIT or (event.type == KEYDOWN and event.key == K_ESCAPE):
                pygame.quit()
                sys.exit()
            if event.type == KEYDOWN and (event.key == K_SPACE or event.key == K_UP):
                if playery > -2 * IMAGES["player"][0].get_height():
                    playerVelY = playerFlapAcc
                    playerFlapped = True

        ## vérifier s'il y a eu un crash ici
        crashTest = checkCrash(
            {"x": playerx, "y": playery, "index": playerIndex}, upperPipes, lowerPipes
        )
        if crashTest[0]:
            return {
                "y": playery,
                "groundCrash": crashTest[1],
                "basex": basex,
                "upperPipes": upperPipes,
                "lowerPipes": lowerPipes,
                "score": score,
                "playerVelY": playerVelY,
                "playerRot": playerRot,
            }

        ## vérifier le score
        playerMidPos = playerx + IMAGES["player"][0].get_width() / 2
        for pipe in upperPipes:
            pipeMidPos = pipe["x"] + IMAGES["pipe"][0].get_width() / 2
            if pipeMidPos <= playerMidPos < pipeMidPos + 4:
                score += 1

        ## changement de playerIndex basex
        if (loopIter + 1) % 3 == 0:
            playerIndex = next(playerIndexGen)
        loopIter = (loopIter + 1) % 30
        basex = -((-basex + 100) % baseShift)

        ## tourner le joueur
        if playerRot > -90:
            playerRot -= playerVelRot

        ## mouvement du joueur
        if playerVelY < playerMaxVelY and not playerFlapped:
            playerVelY += playerAccY
        if playerFlapped:
            playerFlapped = False

            ## plus de rotation pour couvrir le seuil (calculé dans la rotation visible)
            playerRot = 45

        playerHeight = IMAGES["player"][playerIndex].get_height()
        playery += min(playerVelY, BASEY - playery - playerHeight)

        ## déplacer les tuyaux vers la gauche
        for uPipe, lPipe in zip(upperPipes, lowerPipes):
            uPipe["x"] += pipeVelX
            lPipe["x"] += pipeVelX

        ## ajouter un nouveau tuyau lorsque le premier tuyau est sur le point de toucher la gauche de l'écran
        if 3 > len(upperPipes) > 0 and 0 < upperPipes[0]["x"] < 5:
            newPipe = getRandomPipe()
            upperPipes.append(newPipe[0])
            lowerPipes.append(newPipe[1])

        ## supprimer le premier tuyau s'il est hors de l'écran
        if len(upperPipes) > 0 and upperPipes[0]["x"] < -IMAGES["pipe"][0].get_width():
            upperPipes.pop(0)
            lowerPipes.pop(0)

        ## dessiner les sprites
        SCREEN.blit(IMAGES["background"], (0, 0))

        for uPipe, lPipe in zip(upperPipes, lowerPipes):
            SCREEN.blit(IMAGES["pipe"][0], (uPipe["x"], uPipe["y"]))
            SCREEN.blit(IMAGES["pipe"][1], (lPipe["x"], lPipe["y"]))

        SCREEN.blit(IMAGES["base"], (basex, BASEY))
        ## afficher le score pour que le joueur recouvre le score
        showScore(score)

        ## La rotation du joueur a un seuil
        visibleRot = playerRotThr
        if playerRot <= playerRotThr:
            visibleRot = playerRot

        playerSurface = pygame.transform.rotate(
            IMAGES["player"][playerIndex], visibleRot
        )
        SCREEN.blit(playerSurface, (playerx, playery))

        pygame.display.update()
        FPSCLOCK.tick(FPS)
  • Nous définissons la fonction mainGame, qui contient la logique principale du jeu Flappy Bird.
  • La fonction gère l'entrée utilisateur, met à jour l'état du jeu, vérifie les collisions et suit le score.
  • La boucle de jeu tourne en continu, mettant à jour l'affichage et la logique du jeu.
  • Les contrôles du joueur sont gérés via des événements clavier (espace ou flèche du haut).
  • La fonction vérifie également les collisions avec les tuyaux et le sol, met à jour le score et gère l'animation de l'oiseau.
  • La boucle de jeu continue jusqu'à ce que le joueur ait un crash ou quitte le jeu.
✨ Vérifier la solution et pratiquer

Afficher l'écran de fin de partie

Dans cette étape, nous allons créer l'écran de fin de partie qui apparaît lorsque le joueur perd.

def showGameOverScreen(crashInfo):
    """Fait tomber le joueur et affiche l'image de fin de partie"""
    score = crashInfo["score"]
    playerx = SCREENWIDTH * 0.2
    playery = crashInfo["y"]
    playerHeight = IMAGES["player"][0].get_height()
    playerVelY = crashInfo["playerVelY"]
    playerAccY = 2
    playerRot = crashInfo["playerRot"]
    playerVelRot = 7

    basex = crashInfo["basex"]

    upperPipes, lowerPipes = crashInfo["upperPipes"], crashInfo["lowerPipes"]

    while True:
        for event in pygame.event.get():
            if event.type == QUIT or (event.type == KEYDOWN and event.key == K_ESCAPE):
                pygame.quit()
                sys.exit()
            if event.type == KEYDOWN and (event.key == K_SPACE or event.key == K_UP):
                if playery + playerHeight >= BASEY - 1:
                    return

        ## Décalage vertical du joueur
        if playery + playerHeight < BASEY - 1:
            playery += min(playerVelY, BASEY - playery - playerHeight)

        ## Changement de vitesse du joueur
        if playerVelY < 15:
            playerVelY += playerAccY

        ## Tourner seulement lorsqu'il y a eu une collision avec un tuyau
        if not crashInfo["groundCrash"]:
            if playerRot > -90:
                playerRot -= playerVelRot

        ## Dessiner les sprites
        SCREEN.blit(IMAGES["background"], (0, 0))

        for uPipe, lPipe in zip(upperPipes, lowerPipes):
            SCREEN.blit(IMAGES["pipe"][0], (uPipe["x"], uPipe["y"]))
            SCREEN.blit(IMAGES["pipe"][1], (lPipe["x"], lPipe["y"]))

        SCREEN.blit(IMAGES["base"], (basex, BASEY))
        showScore(score)

        playerSurface = pygame.transform.rotate(IMAGES["player"][1], playerRot)
        SCREEN.blit(playerSurface, (playerx, playery))
        SCREEN.blit(IMAGES["gameover"], (50, 180))

        FPSCLOCK.tick(FPS)
        pygame.display.update()
  • La fonction showGameOverScreen affiche l'écran de fin de partie lorsque le joueur perd.
  • Elle affiche le score final du joueur, joue des effets sonores et attend que le joueur appuie sur la barre d'espace ou la flèche du haut pour redémarrer le jeu.
  • L'animation comprend l'oiseau tombant sur le sol et le message de fin de partie affiché à l'écran.
✨ Vérifier la solution et pratiquer

Définir des fonctions d'aide

Dans cette étape, nous définissons des fonctions d'aide utilisées dans le jeu.

def playerShm(playerShm):
    """Fait osciller la valeur de playerShm['val'] entre 8 et -8"""
    if abs(playerShm["val"]) == 8:
        playerShm["dir"] *= -1

    if playerShm["dir"] == 1:
        playerShm["val"] += 1
    else:
        playerShm["val"] -= 1


def getRandomPipe():
    """Renvoie un tuyau généré aléatoirement"""
    ## y de l'espace entre le tuyau supérieur et le tuyau inférieur
    gapY = random.randrange(0, int(BASEY * 0.6 - PIPEGAPSIZE))
    gapY += int(BASEY * 0.2)
    pipeHeight = IMAGES["pipe"][0].get_height()
    pipeX = SCREENWIDTH + 10

    return [
        {"x": pipeX, "y": gapY - pipeHeight},  ## tuyau supérieur
        {"x": pipeX, "y": gapY + PIPEGAPSIZE},  ## tuyau inférieur
    ]


def showScore(score):
    """Affiche le score au centre de l'écran"""
    scoreDigits = [int(x) for x in list(str(score))]
    totalWidth = 0  ## largeur totale de tous les chiffres à afficher

    for digit in scoreDigits:
        totalWidth += IMAGES["numbers"][digit].get_width()

    Xoffset = (SCREENWIDTH - totalWidth) / 2

    for digit in scoreDigits:
        SCREEN.blit(IMAGES["numbers"][digit], (Xoffset, SCREENHEIGHT * 0.1))
        Xoffset += IMAGES["numbers"][digit].get_width()


def checkCrash(player, upperPipes, lowerPipes):
    """Renvoie True si le joueur entre en collision avec la base ou les tuyaux."""
    pi = player["index"]
    player["w"] = IMAGES["player"][0].get_width()
    player["h"] = IMAGES["player"][0].get_height()

    ## si le joueur heurte le sol
    if player["y"] + player["h"] >= BASEY - 1:
        return [True, True]
    else:
        playerRect = pygame.Rect(player["x"], player["y"], player["w"], player["h"])
        pipeW = IMAGES["pipe"][0].get_width()
        pipeH = IMAGES["pipe"][0].get_height()

        for uPipe, lPipe in zip(upperPipes, lowerPipes):
            ## rectangles des tuyaux supérieur et inférieur
            uPipeRect = pygame.Rect(uPipe["x"], uPipe["y"], pipeW, pipeH)
            lPipeRect = pygame.Rect(lPipe["x"], lPipe["y"], pipeW, pipeH)

            ## masques de collision du joueur et des tuyaux supérieur/inferieur
            pHitMask = HITMASKS["player"][pi]
            uHitmask = HITMASKS["pipe"][0]
            lHitmask = HITMASKS["pipe"][1]

            ## si l'oiseau a heurté le tuyau supérieur ou inférieur
            uCollide = pixelCollision(playerRect, uPipeRect, pHitMask, uHitmask)
            lCollide = pixelCollision(playerRect, lPipeRect, pHitMask, lHitmask)

            if uCollide or lCollide:
                return [True, False]

    return [False, False]


def pixelCollision(rect1, rect2, hitmask1, hitmask2):
    """Vérifie si deux objets entrent en collision et pas seulement leurs rectangles"""
    rect = rect1.clip(rect2)

    if rect.width == 0 or rect.height == 0:
        return False

    x1, y1 = rect.x - rect1.x, rect.y - rect1.y
    x2, y2 = rect.x - rect2.x, rect.y - rect2.y

    for x in range(rect.width):
        for y in range(rect.height):
            if hitmask1[x1 + x][y1 + y] and hitmask2[x2 + x][y2 + y]:
                return True
    return False


def getHitmask(image):
    """Renvoie un masque de collision à partir de l'alpha d'une image."""
    mask = []
    for x in range(image.get_width()):
        mask.append([])
        for y in range(image.get_height()):
            mask[x].append(bool(image.get_at((x, y))[3]))
    return mask
  • La fonction playerShm fait osciller la valeur de playerShm["val"] entre 8 et -8. Elle est utilisée pour déplacer l'oiseau vers le haut et le bas sur l'écran d'accueil.
  • La fonction getRandomPipe renvoie un tuyau généré aléatoirement. Elle génère un espace aléatoire entre le tuyau supérieur et le tuyau inférieur.
  • La fonction showScore affiche le score au centre de l'écran. Elle utilise la liste IMAGES["numbers"] pour afficher le score.
  • La fonction checkCrash renvoie True si le joueur entre en collision avec le sol ou les tuyaux. Elle utilise la fonction pixelCollision pour vérifier les collisions.
  • La fonction pixelCollision vérifie si deux objets entrent en collision et pas seulement leurs rectangles. Elle utilise la fonction getHitmask pour obtenir le masque de collision du joueur et des tuyaux.
  • La fonction getHitmask renvoie un masque de collision à partir de l'alpha d'une image. Elle utilise la fonction image.get_at pour obtenir la valeur alpha de chaque pixel de l'image.
✨ Vérifier la solution et pratiquer

La fonction principale

Dans cette étape, nous définissons la fonction principale qui initialise le jeu et démarre la boucle de jeu.

def main():
    global SCREEN, FPSCLOCK
    pygame.init()
    FPSCLOCK = pygame.time.Clock()
    SCREEN = pygame.display.set_mode((SCREENWIDTH, SCREENHEIGHT))
    pygame.display.set_caption("Flappy Bird")

    ## sprites pour afficher le score
    IMAGES["numbers"] = (
        pygame.image.load("data/sprites/0.png").convert_alpha(),
        pygame.image.load("data/sprites/1.png").convert_alpha(),
        pygame.image.load("data/sprites/2.png").convert_alpha(),
        pygame.image.load("data/sprites/3.png").convert_alpha(),
        pygame.image.load("data/sprites/4.png").convert_alpha(),
        pygame.image.load("data/sprites/5.png").convert_alpha(),
        pygame.image.load("data/sprites/6.png").convert_alpha(),
        pygame.image.load("data/sprites/7.png").convert_alpha(),
        pygame.image.load("data/sprites/8.png").convert_alpha(),
        pygame.image.load("data/sprites/9.png").convert_alpha(),
    )

    ## sprite pour l'écran de fin de partie
    IMAGES["gameover"] = pygame.image.load("data/sprites/gameover.png").convert_alpha()
    ## sprite pour le message de l'écran d'accueil
    IMAGES["message"] = pygame.image.load("data/sprites/message.png").convert_alpha()
    ## sprite pour la base (sol)
    IMAGES["base"] = pygame.image.load("data/sprites/base.png").convert_alpha()

    while True:
        ## sélectionner des sprites d'arrière-plan aléatoires
        randBg = random.randint(0, len(BACKGROUNDS_LIST) - 1)
        IMAGES["background"] = pygame.image.load(BACKGROUNDS_LIST[randBg]).convert()

        ## sélectionner des sprites de joueur aléatoires
        randPlayer = random.randint(0, len(PLAYERS_LIST) - 1)
        IMAGES["player"] = (
            pygame.image.load(PLAYERS_LIST[randPlayer][0]).convert_alpha(),
            pygame.image.load(PLAYERS_LIST[randPlayer][1]).convert_alpha(),
            pygame.image.load(PLAYERS_LIST[randPlayer][2]).convert_alpha(),
        )

        ## sélectionner des sprites de tuyau aléatoires
        pipeindex = random.randint(0, len(PIPES_LIST) - 1)
        IMAGES["pipe"] = (
            pygame.transform.flip(
                pygame.image.load(PIPES_LIST[pipeindex]).convert_alpha(), False, True
            ),
            pygame.image.load(PIPES_LIST[pipeindex]).convert_alpha(),
        )

        ## masque de collision pour les tuyaux
        HITMASKS["pipe"] = (
            getHitmask(IMAGES["pipe"][0]),
            getHitmask(IMAGES["pipe"][1]),
        )

        ## masque de collision pour le joueur
        HITMASKS["player"] = (
            getHitmask(IMAGES["player"][0]),
            getHitmask(IMAGES["player"][1]),
            getHitmask(IMAGES["player"][2]),
        )

        movementInfo = showWelcomeAnimation()
        crashInfo = mainGame(movementInfo)
        showGameOverScreen(crashInfo)
  • La fonction main initialise le jeu, configure l'affichage et démarre la boucle de jeu.
  • Elle charge les ressources du jeu, y compris les images, et sélectionne aléatoirement les sprites d'arrière-plan, de joueur et de tuyau.
  • La boucle de jeu gère l'animation d'accueil, le jeu principal et l'écran de fin de partie.
  • Le jeu continue de boucler jusqu'à ce que le joueur quitte le jeu ou ferme la fenêtre.
✨ Vérifier la solution et pratiquer

Exécuter le jeu

Dans cette étape, nous allons exécuter le jeu Flappy Bird.

if __name__ == "__main__":
    main()
  • La condition __name__ == "__main__" vérifie si le module actuel est exécuté seul ou importé par un autre module.
  • Si le module actuel est exécuté seul, la fonction main est appelée pour démarrer le jeu.

Une fois que vous avez terminé toutes les étapes, vous pouvez exécuter le jeu Flappy Bird en utilisant la commande suivante :

cd ~/projet
python flappy.py
Capture d'écran du jeu Flappy Bird
✨ Vérifier la solution et pratiquer

Sommaire

Dans ce projet, nous avons divisé le code du jeu Flappy Bird en plusieurs étapes et fourni des explications pour chaque étape. Vous avez appris à créer une structure de base de jeu, à gérer l'entrée du joueur, à mettre à jour l'état du jeu, à vérifier les collisions et à afficher les écrans de jeu. Vous pouvez maintenant exécuter et jouer au jeu Flappy Bird à l'aide du code fourni. Amusez-vous bien dans votre parcours de développement de jeux!