Créer un jeu Aliens avec Pygame

PythonBeginner
Pratiquer maintenant

Introduction

Dans ce projet, nous vous guiderons tout au long du processus de création d'un jeu simple appelé "Aliens" à l'aide de la bibliothèque Pygame. Le jeu consiste à défendre contre les envahisseurs d'aliens en les abattant. Nous allons diviser le processus de développement en plusieurs étapes, depuis la configuration des fichiers du projet jusqu'à l'exécution du jeu terminé.

Pygame est une bibliothèque populaire pour créer des jeux 2D en Python. Elle fournit des fonctions pour gérer les graphiques, le son et l'entrée utilisateur, ce qui en fait un excellent choix pour les débutants intéressés par le développement de jeux.

👀 Aperçu

Aliens Game

Ce jeu est révisé à partir des exemples Pygame.

🎯 Tâches

Dans ce projet, vous allez apprendre :

  • Comment configurer la structure initiale du projet et charger les ressources nécessaires telles que des images et des sons.
  • Comment définir les classes pour le personnage du joueur et les envahisseurs d'aliens.
  • Comment créer des classes supplémentaires pour gérer les explosions, les tirs du joueur, les bombes des aliens et le score du jeu.
  • Comment initialiser le jeu, charger les ressources et configurer la fenêtre du jeu.
  • Comment implémenter la boucle principale du jeu, gérer l'entrée de l'utilisateur, mettre à jour les entités du jeu, gérer les collisions et dessiner la scène du jeu.

🏆 Réalisations

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

  • Utiliser la bibliothèque Pygame pour développer un jeu 2D.
  • Charger et afficher des images dans Pygame.
  • Gérer l'entrée de l'utilisateur et contrôler le mouvement du joueur.
  • Créer et mettre à jour les entités du jeu à l'aide de classes de sprites.
  • Gérer les collisions entre les entités du jeu.
  • Dessiner la scène du jeu et mettre à jour l'écran.
  • Jouer des effets sonores et de la musique dans le jeu.
  • Implémenter une boucle principale du jeu pour gérer la logique du jeu.

Créer les fichiers du projet

Dans cette étape, nous allons configurer la structure initiale du projet et charger les ressources nécessaires telles que des images et des sons.

Créez un nouveau fichier appelé aliens.py et ajoutez le code suivant :

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

Ensuite, ouvrez le fichier dans votre éditeur de texte favori et ajoutez le code suivant :

import os
import random
from typing import List

## importe les modules de base de Pygame
import pygame as pg

## voir si nous pouvons charger plus que l'image standard BMP
if not pg.image.get_extended():
    raise SystemExit("Désolé, le module d'image étendu est requis")


## constantes du jeu
MAX_SHOTS = 2  ## nombre maximum de balles du joueur à l'écran
ALIEN_ODDS = 22  ## chances qu'un nouvel alien apparaisse
BOMB_ODDS = 60  ## chances qu'une nouvelle bombe tombe
ALIEN_RELOAD = 12  ## images entre les nouveaux aliens
SCREENRECT = pg.Rect(0, 0, 640, 480)
SCORE = 0

main_dir = os.path.split(os.path.abspath(__file__))[0]


def load_image(file):
    """charge une image, la prépare pour le jeu"""
    file = os.path.join(main_dir, "data", file)
    try:
        surface = pg.image.load(file)
    except pg.error:
        raise SystemExit(f'Impossible de charger l\'image "{file}" {pg.get_error()}')
    return surface.convert()


def load_sound(file):
    """car Pygame peut être compilé sans le module mixer."""
    if not pg.mixer:
        return None
    file = os.path.join(main_dir, "data", file)
    try:
        sound = pg.mixer.Sound(file)
        return sound
    except pg.error:
        print(f"Avertissement, impossible de charger, {file}")
    return None

Dans cette étape, nous importons les modules nécessaires, définissons les constantes du jeu et créons des fonctions pour charger les images et les effets sonores. Nous configurons également le répertoire principal du projet.

Dans def load_image et def load_sound, nous utilisons la fonction os.path.join pour joindre le répertoire principal du projet au répertoire data, où nous stockerons nos ressources de jeu. Nous créerons ce répertoire dans l'étape suivante.

✨ Vérifier la solution et pratiquer

Définir les classes de joueur et d'alien

Maintenant, nous allons définir les classes pour les personnages de notre jeu : Joueur et Alien. La classe Joueur représente le personnage du joueur, tandis que la classe Alien représente les envahisseurs d'aliens.

#... (code de l'étape 1)

## Définir la classe Joueur
class Joueur(pg.sprite.Sprite):
    """Représenter le joueur sous forme d'un véhicule du type buggy lunaire."""

    vitesse = 10
    rebond = 24
    décalage_armure = -11
    images: List[pg.Surface] = []

    def __init__(self, *groups):
        pg.sprite.Sprite.__init__(self, *groups)
        self.image = self.images[0]
        self.rect = self.image.get_rect(midbottom=SCREENRECT.midbottom)
        self.recharge = 0
        self.origtop = self.rect.top
        self.direction = -1

    def se_deplacer(self, direction):
        if direction:
            self.direction = direction
        self.rect.move_ip(direction * self.vitesse, 0)
        self.rect = self.rect.clamp(SCREENRECT)
        if direction < 0:
            self.image = self.images[0]
        elif direction > 0:
            self.image = self.images[1]
        self.rect.top = self.origtop - (self.rect.left // self.rebond % 2)

    def position_armure(self):
        pos = self.direction * self.décalage_armure + self.rect.centerx
        return pos, self.rect.top

Ici, nous avons défini la classe Joueur. Elle inclut des attributs tels que la vitesse, le rebond et le décalage de l'arme. La méthode se_deplacer gère le mouvement du joueur. La méthode position_armure renvoie la position de l'arme du joueur. La classe Joueur hérite de la classe pg.sprite.Sprite, qui est une classe de base pour les objets de jeu visibles.

## Définir la classe Alien
class Alien(pg.sprite.Sprite):
    """Un vaisseau spatial alien. Qui se déplace lentement vers le bas de l'écran."""

    vitesse = 13
    cycle_animation = 12
    images: List[pg.Surface] = []

    def __init__(self, *groups):
        pg.sprite.Sprite.__init__(self, *groups)
        self.image = self.images[0]
        self.rect = self.image.get_rect()
        self.direction = random.choice((-1, 1)) * Alien.vitesse
        self.trame = 0
        if self.direction < 0:
            self.rect.right = SCREENRECT.right

    def mise_a_jour(self):
        self.rect.move_ip(self.direction, 0)
        if not SCREENRECT.contains(self.rect):
            self.direction = -self.direction
            self.rect.top = self.rect.bottom + 1
            self.rect = self.rect.clamp(SCREENRECT)
        self.trame = self.trame + 1
        self.image = self.images[self.trame // self.cycle_animation % 3]

La classe Alien représente les envahisseurs d'aliens. Elle a des attributs pour la vitesse et l'animation. Les aliens se déplacent vers la gauche et la droite, et leurs images changent en boucle. La méthode mise_a_jour gère le mouvement et l'animation des aliens. La classe Alien hérite de la classe pg.sprite.Sprite, qui est une classe de base pour les objets de jeu visibles.

✨ Vérifier la solution et pratiquer

Définir des classes supplémentaires de jeu

Dans cette étape, nous allons définir des classes supplémentaires liées au jeu : Explosion, Tir, Bombe et Score. Ces classes gèrent les explosions, les tirs du joueur, les bombes des aliens et le score du jeu.

#... (code de l'étape 2)

## Définir la classe Explosion
class Explosion(pg.sprite.Sprite):
    """Une explosion. Espérons que ce soit l'alien et pas le joueur!"""

    durée_vie_par_défaut = 12
    cycle_animation = 3
    images: List[pg.Surface] = []

    def __init__(self, acteur, *groups):
        pg.sprite.Sprite.__init__(self, *groups)
        self.image = self.images[0]
        self.rect = self.image.get_rect(center=acteur.rect.center)
        self.vie = self.durée_vie_par_défaut

    def update(self):
        """Appelée à chaque tour de boucle du jeu.

        Affiche la surface d'explosion pendant 'durée_vie_par_défaut'.
        A chaque incrément de jeu (mise à jour), nous diminuons la 'vie'.

        Nous animons également l'explosion.
        """
        self.vie = self.vie - 1
        self.image = self.images[self.vie // self.cycle_animation % 2]
        if self.vie <= 0:
            self.kill()

## Définir la classe Tir
class Tir(pg.sprite.Sprite):
    """une balle tirée par le sprite du joueur."""

    vitesse = -11
    images: List[pg.Surface] = []

    def __init__(self, pos, *groups):
        pg.sprite.Sprite.__init__(self, *groups)
        self.image = self.images[0]
        self.rect = self.image.get_rect(midbottom=pos)

    def update(self):
        """Appelée à chaque tour de boucle du jeu.

        A chaque incrément, nous déplaçons le tir vers le haut.
        """
        self.rect.move_ip(0, self.vitesse)
        if self.rect.top <= 0:
            self.kill()

## Définir la classe Bombe
class Bombe(pg.sprite.Sprite):
    """Une bombe lâchée par les aliens."""

    vitesse = 9
    images: List[pg.Surface] = []

    def __init__(self, alien, explosion_group, *groups):
        pg.sprite.Sprite.__init__(self, *groups)
        self.image = self.images[0]
        self.rect = self.image.get_rect(midbottom=alien.rect.move(0, 5).midbottom)
        self.explosion_group = explosion_group

    def update(self):
        """Appelée à chaque tour de boucle du jeu.

        A chaque image, nous déplaçons le rectangle du sprite vers le bas.
        Lorsqu'il atteint le bas, nous :

        - créons une explosion.
        - supprimons la Bombe.
        """
        self.rect.move_ip(0, self.vitesse)
        if self.rect.bottom >= 470:
            Explosion(self, self.explosion_group)
            self.kill()

## Définir la classe Score
class Score(pg.sprite.Sprite):
    """Pour suivre le score."""

    def __init__(self, *groups):
        pg.sprite.Sprite.__init__(self, *groups)
        self.font = pg.font.Font(None, 20)
        self.font.set_italic(1)
        self.couleur = "blanc"
        self.dernier_score = -1
        self.update()
        self.rect = self.image.get_rect().move(10, 450)

    def update(self):
        """Nous ne mettons à jour le score que dans update() lorsqu'il a changé."""
        if SCORE!= self.dernier_score:
            self.dernier_score = SCORE
            msg = f"Score: {SCORE}"
            self.image = self.font.render(msg, 0, self.couleur)

Ces classes gèrent différents aspects du jeu, telles que les explosions, les tirs, les bombes et l'affichage du score du joueur.

✨ Vérifier la solution et pratiquer

Initialiser le jeu et charger les ressources

Maintenant, nous allons continuer à initialiser le jeu et à charger les ressources requises telles que des images et des sons.

#... (code de l'étape 3)

def main(winstyle=0):
    ## Initialiser Pygame
    if pg.get_sdl_version()[0] == 2:
        pg.mixer.pre_init(44100, 32, 2, 1024)
    pg.init()
    if pg.mixer and not pg.mixer.get_init():
        print("Avertissement, pas de son")
        pg.mixer = None

    plein_ecran = False
    ## Définir le mode d'affichage
    winstyle = 0  ## |FULLSCREEN
    meilleure_profondeur = pg.display.mode_ok(SCREENRECT.size, winstyle, 32)
    ecran = pg.display.set_mode(SCREENRECT.size, winstyle, meilleure_profondeur)

    ## Charger les images, assigner aux classes de sprites
    ## (faire cela avant d'utiliser les classes, après la configuration de l'écran)
    img = load_image("player1.gif")
    Joueur.images = [img, pg.transform.flip(img, 1, 0)]
    img = load_image("explosion1.gif")
    Explosion.images = [img, pg.transform.flip(img, 1, 1)]
    Alien.images = [load_image(im) for im in ("alien1.gif", "alien2.gif", "alien3.gif")]
    Bombe.images = [load_image("bomb.gif")]
    Tir.images = [load_image("shot.gif")]

Ici, nous initialisons Pygame, configurons la fenêtre du jeu et chargeons les images pour le joueur, les aliens, les explosions, les bombes et les tirs.

✨ Vérifier la solution et pratiquer

Compléter la configuration du jeu et la boucle principale

Maintenant, nous allons terminer la configuration du jeu et créer la boucle principale du jeu, où toute la logique et les interactions du jeu auront lieu.

#... (code de l'étape 4)

def main(winstyle=0):
    #... (code précédent)

    ## décorer la fenêtre du jeu
    icone = pg.transform.scale(Alien.images[0], (32, 32))
    pg.display.set_icon(icone)
    pg.display.set_caption("Pygame Aliens")
    pg.mouse.set_visible(0)

    ## créer l'arrière-plan, tiler l'image d'arrière-plan
    bgdtile = load_image("background.gif")
    fond = pg.Surface(SCREENRECT.size)
    for x in range(0, SCREENRECT.width, bgdtile.get_width()):
        fond.blit(bgdtile, (x, 0))
    ecran.blit(fond, (0, 0))
    pg.display.flip()

    ## charger les effets sonores
    son_boom = load_sound("boom.wav")
    son_tir = load_sound("car_door.wav")
    if pg.mixer:
        musique = os.path.join(main_dir, "data", "house_lo.wav")
        pg.mixer.music.load(musique)
        pg.mixer.music.play(-1)

    ## Initialiser les groupes de jeu
    aliens = pg.sprite.Group()
    tirs = pg.sprite.Group()
    bombes = pg.sprite.Group()
    tout = pg.sprite.RenderUpdates()
    dernier_alien = pg.sprite.GroupSingle()

    ## Créer quelques valeurs de départ
    recharge_alien = ALIEN_RELOAD
    horloge = pg.time.Clock()

    ## initialiser nos sprites de départ
    global SCORE
    joueur = Joueur(tout)
    Alien(
        aliens, tout, dernier_alien
    )  ## note, cela 'vit' car il entre dans un groupe de sprites
    if pg.font:
        tout.add(Score(tout))

    ## Exécuter notre boucle principale tant que le joueur est en vie.
    while joueur.alive():
        ## obtenir les entrées
        for event in pg.event.get():
            if event.type == pg.QUIT:
                return
            if event.type == pg.KEYDOWN and event.key == pg.K_ESCAPE:
                return
            if event.type == pg.KEYDOWN:
                if event.key == pg.K_f:
                    if not plein_ecran:
                        print("Changement en mode PLEIN ECRAN")
                        sauvegarde_ecran = ecran.copy()
                        ecran = pg.display.set_mode(
                            SCREENRECT.size, winstyle | pg.FULLSCREEN, meilleure_profondeur
                        )
                        ecran.blit(sauvegarde_ecran, (0, 0))
                    else:
                        print("Changement en mode fenêtré")
                        sauvegarde_ecran = ecran.copy()
                        ecran = pg.display.set_mode(
                            SCREENRECT.size, winstyle, meilleure_profondeur
                        )
                        ecran.blit(sauvegarde_ecran, (0, 0))
                    pg.display.flip()
                    plein_ecran = not plein_ecran

        etat_touches = pg.key.get_pressed()

        ## effacer/détruire les sprites dessinés précédemment
        tout.clear(ecran, fond)

        ## mettre à jour tous les sprites
        tout.update()

        ## gérer l'entrée du joueur
        direction = etat_touches[pg.K_RIGHT] - etat_touches[pg.K_LEFT]
        joueur.se_deplacer(direction)
        tirer = etat_touches[pg.K_SPACE]
        if not joueur.recharge and tirer and len(tirs) < MAX_SHOTS:
            Tir(joueur.position_armure(), tirs, tout)
            if pg.mixer and son_tir is not None:
                son_tir.play()
        joueur.recharge = tirer

        ## Créer un nouvel alien
        if recharge_alien:
            recharge_alien = recharge_alien - 1
        elif not int(random.random() * ALIEN_ODDS):
            Alien(aliens, tout, dernier_alien)
            recharge_alien = ALIEN_RELOAD

        ## Lâcher des bombes
        if dernier_alien and not int(random.random() * BOMB_ODDS):
            Bombe(dernier_alien.sprite, tout, bombes, tout)

        ## Détecter les collisions entre les aliens et le joueur.
        for alien in pg.sprite.spritecollide(joueur, aliens, 1):
            if pg.mixer and son_boom is not None:
                son_boom.play()
            Explosion(alien, tout)
            Explosion(joueur, tout)
            SCORE = SCORE + 1
            joueur.kill()

        ## Vérifier si les tirs touchent les aliens.
        for alien in pg.sprite.groupcollide(aliens, tirs, 1, 1).keys():
            if pg.mixer and son_boom is not None:
                son_boom.play()
            Explosion(alien, tout)
            SCORE = SCORE + 1

        ## Vérifier si les bombes des aliens touchent le joueur.
        for bombe in pg.sprite.spritecollide(joueur, bombes, 1):
            if pg.mixer and son_boom is not None:
                son_boom.play()
            Explosion(joueur, tout)
            Explosion(bombe, tout)
            joueur.kill()

        ## dessiner la scène
        sale = tout.draw(ecran)
        pg.display.update(sale)

        ## limiter le taux de rafraîchissement à 40fps. Également appelé 40HZ ou 40 fois par seconde.
        horloge.tick(40)

    if pg.mixer:
        pg.mixer.music.fadeout(1000)
    pg.time.wait(1000)


## appeler la fonction "main" si on exécute ce script
if __name__ == "__main__":
    main()
    pg.quit()

Dans la fonction principale, nous initialisons les groupes de jeu pour diverses entités, créons des valeurs de départ et mettons en œuvre la boucle principale du jeu. La logique du jeu, la gestion de l'entrée du joueur, les collisions et le dessin de la scène du jeu sont effectués dans cette boucle.

Exécution du jeu

Maintenant, nous pouvons exécuter le jeu en exécutant la commande suivante :

cd ~/projet
python aliens.py
✨ Vérifier la solution et pratiquer

Exécution du jeu

Maintenant, nous pouvons exécuter le jeu en exécutant la commande suivante :

cd ~/projet
python aliens.py
Capture d'écran d'exécution du jeu
✨ Vérifier la solution et pratiquer

Résumé

Dans ce projet, nous avons divisé le processus de construction d'un jeu simple "Aliens" utilisant Pygame en plusieurs étapes. Nous avons configuré les fichiers du projet, défini les classes de joueur et d'aliens, créé des classes supplémentaires liées au jeu et initialisé le jeu avec des ressources. Enfin, nous avons mis en œuvre la boucle principale du jeu pour gérer la logique et les interactions du jeu.

Dans les étapes suivantes, nous continuerons d'ajouter des fonctionnalités aux classes de joueur et d'aliens, de gérer le mouvement et le tir, et de mettre à jour les éléments visuels du jeu.