Chat
Ask me anything
Ithy Logo
Sign in to save

Création d'un jeu Danmaku en Pygame

Un guide complet pour coder un jeu de tir de type bullet-hell avec ennemis, boss et écran de fin

bullet hell game screen

Points Clés à Retenir

  • Infrastructure du jeu : Initialisation de Pygame, configuration de l'écran, gestion des événements et boucle de jeu.
  • Classes principales : Création de classes pour le joueur, les projectiles, les ennemis et le boss, chacune avec des comportements spécifiques.
  • Gestion de la dynamique de jeu : Mécanismes de tir, détection des collisions, rafraîchissement des sprites et affichage d'un écran de fin après atteindre un état de défaite ou victoire.

Introduction

Dans ce tutoriel détaillé, nous allons créer un jeu Danmaku complet en utilisant Pygame. Le jeu incorporera des zones essentielles pour un bullet-hell : un joueur mobile capable de tirer, des ennemis qui apparaissent aléatoirement et se déplacent avec des motifs définis, un boss qui propose des patterns de tir plus complexes, et enfin, une mécanique de fin de jeu lorsque la vie du joueur est épuisée ou quand le boss est vaincu.

Architecture et Classes du Jeu

La structure du code sera organisée autour de plusieurs classes pour une meilleure modularité et maintenance du jeu. Nous aborderons les classes suivantes :

1. Initialisation et Configuration

Nous commençons par initialiser Pygame, définir les couleurs et les constantes de l'écran ainsi que créer la fenêtre du jeu. Ceci constitue la base pour ensuite ajouter les éléments interactifs, comme le joueur, les ennemis, et les projectiles.

2. Classe Joueur

La classe du joueur sera définie pour gérer le déplacement (en utilisant les touches ou la souris), le tir et la détection de collision. Chaque joueur est une entité qui interagit avec des projectiles et des ennemis affichés à l'écran. Le joueur possédera également une barre de vie qui déterminera la fin de partie.

3. Classe Projectile

La classe de projectile implémente le comportement des balles tirées par le joueur. Chaque projectile est défini par sa position, sa vitesse et sa direction. Une méthode update permet de rafraîchir la position du projectile à chaque frame.

4. Classe Ennemi

Pour les ennemis, nous configurons une classe qui met en œuvre un mouvement et, potentiellement, un tir de projectiles. Les ennemis pourront apparaître aléatoirement en haut de l'écran et descendre pour atteindre le joueur. Une fonction de mise à jour détermine leur comportement continue à chaque boucle de jeu.

5. Classe Boss

Le boss se distingue en étant plus imposant, avec des phases de tir variées et une santé plus importante. La classe Boss hérite souvent d'une classe ennemie de base mais implémente des méthodes supplémentaires pour donner des patterns de tir complexes. Une gestion spéciale de la santé permet d'afficher un écran de victoire lorsque la barre de vie du boss tombe à zéro.

6. Gestion des Collisions et Conditions de Fin de Jeu

La détection des collisions est essentielle pour interagir avec différents éléments du jeu. En utilisant des rectangles de collision (objets Rect de Pygame), nous pouvons vérifier si un projectile touche le joueur ou un ennemi. La détection de collision influence la diminution de la vie du joueur ou de l'ennemi, et déclenche l'écran de fin de jeu (Game Over ou You Win).

Implémentation Complète du Code

Voici une implémentation complète en Pygame du jeu Danmaku intégrant des ennemis, un boss et une fin de jeu. Ce code est structuré de manière à être facilement extensible et à être amélioré ultérieurement.

Code complet en Python


# Importations et initialisations
import pygame
import random
import math

# Initialisation de Pygame
pygame.init()

# Constantes relatives à l'écran et couleurs
SCREEN_WIDTH, SCREEN_HEIGHT = 800, 600
BLACK   = (0, 0, 0)
WHITE   = (255, 255, 255)
RED     = (255, 0, 0)
GREEN   = (0, 255, 0)
BLUE    = (0, 0, 255)
ORANGE  = (255, 165, 0)

# Configuration de l'écran
screen = pygame.display.set_mode((SCREEN_WIDTH, SCREEN_HEIGHT))
pygame.display.set_caption("Danmaku Game")
clock = pygame.time.Clock()

# Classe du joueur
class Player(pygame.sprite.Sprite):
    def __init__(self):
        super().__init__()
        self.image = pygame.Surface([30, 30])
        self.image.fill(BLUE)
        self.rect = self.image.get_rect()
        self.rect.center = (SCREEN_WIDTH // 2, SCREEN_HEIGHT - 50)
        self.speed = 5
        self.health = 100
        self.bullets = pygame.sprite.Group()

    def update(self):
        # Déplacement via les touches fléchées
        keys = pygame.key.get_pressed()
        if keys[pygame.K_LEFT] and self.rect.left > 0:
            self.rect.x -= self.speed
        if keys[pygame.K_RIGHT] and self.rect.right < SCREEN_WIDTH:
            self.rect.x += self.speed
        if keys[pygame.K_UP] and self.rect.top > 0:
            self.rect.y -= self.speed
        if keys[pygame.K_DOWN] and self.rect.bottom < SCREEN_HEIGHT:
            self.rect.y += self.speed

    def shoot(self):
        bullet = Bullet(self.rect.centerx, self.rect.top)
        self.bullets.add(bullet)
        all_sprites.add(bullet)

# Classe pour les projectiles (tir du joueur)
class Bullet(pygame.sprite.Sprite):
    def __init__(self, x, y):
        super().__init__()
        self.image = pygame.Surface([5, 10])
        self.image.fill(WHITE)
        self.rect = self.image.get_rect()
        self.rect.center = (x, y)
        self.speed = 10

    def update(self):
        self.rect.y -= self.speed
        if self.rect.bottom < 0:
            self.kill()

# Classe de base pour un ennemi
class Enemy(pygame.sprite.Sprite):
    def __init__(self):
        super().__init__()
        self.image = pygame.Surface([30, 30])
        self.image.fill(RED)
        self.rect = self.image.get_rect()
        self.rect.x = random.randrange(0, SCREEN_WIDTH - self.rect.width)
        self.rect.y = random.randrange(-150, -40)
        self.speed = random.randrange(2, 5)
        self.health = 20

    def update(self):
        self.rect.y += self.speed
        # Réapparition si l'ennemi sort de l'écran
        if self.rect.top > SCREEN_HEIGHT:
            self.rect.x = random.randrange(0, SCREEN_WIDTH - self.rect.width)
            self.rect.y = random.randrange(-150, -40)
            self.speed = random.randrange(2, 5)

    def shoot(self):
        # Optionnel : Possibilité pour l'ennemi de tirer
        bullet = EnemyBullet(self.rect.centerx, self.rect.bottom)
        enemy_bullets.add(bullet)
        all_sprites.add(bullet)

# Projectile tiré par les ennemis
class EnemyBullet(pygame.sprite.Sprite):
    def __init__(self, x, y):
        super().__init__()
        self.image = pygame.Surface([4, 10])
        self.image.fill(RED)
        self.rect = self.image.get_rect()
        self.rect.center = (x, y)
        self.speed = 6

    def update(self):
        self.rect.y += self.speed
        if self.rect.top > SCREEN_HEIGHT:
            self.kill()

# Classe Boss, hérite de Enemy pour réutiliser certaines caractéristiques
class Boss(pygame.sprite.Sprite):
    def __init__(self):
        super().__init__()
        self.image = pygame.Surface([80, 80])
        self.image.fill(ORANGE)
        self.rect = self.image.get_rect()
        self.rect.center = (SCREEN_WIDTH // 2, 100)
        self.health = 500
        self.bullets = pygame.sprite.Group()
        self.shoot_delay = 1000  # Millisecondes entre les tirs
        self.last_shot = pygame.time.get_ticks()

    def update(self):
        # Le boss peut se déplacer horizontalement si désiré
        current_time = pygame.time.get_ticks()
        if current_time - self.last_shot > self.shoot_delay:
            self.shoot()
            self.last_shot = current_time

    def shoot(self):
        # Tirez des projectiles dans plusieurs directions pour créer un pattern
        num_bullets = 8
        for i in range(num_bullets):
            angle = (2 * math.pi / num_bullets) * i
            bullet = BossBullet(self.rect.centerx, self.rect.bottom, angle)
            self.bullets.add(bullet)
            all_sprites.add(bullet)

# Projectile tiré par le boss qui se déplace en utilisant un angle
class BossBullet(pygame.sprite.Sprite):
    def __init__(self, x, y, angle):
        super().__init__()
        self.image = pygame.Surface([5, 10])
        self.image.fill(ORANGE)
        self.rect = self.image.get_rect()
        self.rect.center = (x, y)
        self.speed = 5
        self.angle = angle

    def update(self):
        self.rect.x += math.cos(self.angle) * self.speed
        self.rect.y += math.sin(self.angle) * self.speed
        if (self.rect.top > SCREEN_HEIGHT or self.rect.left < 0 or 
            self.rect.right > SCREEN_WIDTH):
            self.kill()

# Classe pour afficher l'écran de fin de jeu
class GameOver(pygame.sprite.Sprite):
    def __init__(self, message):
        super().__init__()
        self.font = pygame.font.SysFont("monospace", 72)
        self.image = self.font.render(message, True, WHITE)
        self.rect = self.image.get_rect()
        self.rect.center = (SCREEN_WIDTH // 2, SCREEN_HEIGHT // 2)

# Création des groupes de sprites
all_sprites = pygame.sprite.Group()
enemy_sprites = pygame.sprite.Group()
enemy_bullets = pygame.sprite.Group()

# Création du joueur
player = Player()
all_sprites.add(player)

# Création des ennemis
for i in range(10):
    enemy = Enemy()
    all_sprites.add(enemy)
    enemy_sprites.add(enemy)

# Création du boss
boss = Boss()
all_sprites.add(boss)
enemy_sprites.add(boss)

# Variable de score et état du jeu
score = 0
game_over = False

# Table de correspondance pour l'affichage du score et de la vie
score_table = {
    "Score": "Points accumulés en éliminant les ennemis",
    "Vie": "Points de vie restants du joueur"
}

# Fonction pour afficher les statistiques du jeu
def display_stats(screen, score, health):
    font = pygame.font.SysFont("monospace", 24)
    score_text = font.render("Score: " + str(score), True, WHITE)
    health_text = font.render("Vie: " + str(health), True, WHITE)
    screen.blit(score_text, (10, 10))
    screen.blit(health_text, (10, 40))

# Boucle principale du jeu
running = True
while running:
    clock.tick(60)
    for event in pygame.event.get():
        if event.type == pygame.QUIT:
            running = False
        # Le joueur tire en appuyant sur la barre d'espace
        if event.type == pygame.KEYDOWN:
            if event.key == pygame.K_SPACE:
                player.shoot()

    # Mise à jour des sprites uniquement si le jeu n'est pas terminé
    if not game_over:
        all_sprites.update()
        
        # Mise à jour des projectiles du joueur
        for bullet in player.bullets:
            # Détection de collision avec les ennemis
            hit_enemies = pygame.sprite.spritecollide(bullet, enemy_sprites, False)
            for enemy in hit_enemies:
                # Le boss a une gestion de vie différente
                if enemy == boss:
                    enemy.health -= 5
                    if enemy.health <= 0:
                        score += 1000
                        enemy.kill()
                        over_sprite = GameOver("YOU WIN!")
                        all_sprites.add(over_sprite)
                        game_over = True
                else:
                    enemy.health -= 10
                    if enemy.health <= 0:
                        enemy.kill()
                        score += 100
                bullet.kill()

        # Vérification de la collision entre le joueur et les projectiles ennemis et du boss
        if pygame.sprite.spritecollide(player, enemy_bullets, True):
            player.health -= 10
            if player.health <= 0:
                over_sprite = GameOver("GAME OVER")
                all_sprites.add(over_sprite)
                game_over = True

        # Vérifier si des ennemis entrent en collision avec le joueur (optionnel)
        if pygame.sprite.spritecollide(player, enemy_sprites, False):
            player.health -= 1
            if player.health <= 0:
                over_sprite = GameOver("GAME OVER")
                all_sprites.add(over_sprite)
                game_over = True

    # Rendu graphique
    screen.fill(BLACK)
    all_sprites.draw(screen)
    display_stats(screen, score, player.health)
    pygame.display.flip()

    # Une petite pause quand le jeu est fini pour laisser le temps au joueur de voir l'écran
    if game_over:
        pygame.time.wait(2000)
        running = False

pygame.quit()
  

Explications et Détails Techniques

Le code ci-dessus présente une structure complète pour un jeu de type danmaku :

Initialisation et Configuration

À l'aide de la fonction pygame.init(), le module Pygame est démarré et la fenêtre de jeu est configurée. Les constantes telles que les dimensions (800x600) et les couleurs sont définies au début pour être utilisées par la suite dans le code.

Gestion du Joueur

La classe Player gère notamment le déplacement par les touches directionnelles et le tir avec la barre d'espace. Le joueur possède une barre de santé qui diminue lors des collisions avec des projectiles ennemis. La méthode "shoot" instancie un objet Bullet, qui sera ensuite mis à jour dans la boucle principale.

Création des Projectiles

Les classes Bullet, EnemyBullet et BossBullet décrivent le comportement des balles tirées par le joueur, les ennemis et le boss, respectivement. Chaque projectile a une vitesse et une direction, et sa position est mise à jour à chaque itération de la boucle principale. Pour le boss, des angles sont calculés afin de créer un éventail de tirs, reproduisant ainsi un pattern de tir bullet-hell caractéristique.

Interaction avec les Ennemis et le Boss

La classe Enemy représente les ennemis qui descendent sur l'écran. En cas de collision avec un projectile du joueur, un système de points est incrémenté et la vie de l'ennemi est diminuée jusqu'à ce que celui-ci soit éliminé. Pour le boss, la gestion de la santé est plus complexe, et ses attaques se font selon des patterns programmés dans la méthode "shoot" utilisant des angles différents pour disperser les projectiles dans toutes les directions.

Détection des Collisions et Fin du Jeu

La détection des collisions est effectuée à l'aide de la méthode pygame.sprite.spritecollide, qui vérifie les interactions entre les projectiles et les cibles (ennemis ou joueur). Quand la santé du joueur atteint zéro, un écran Game Over s'affiche. De la même manière, quand le boss est vaincu, le message de victoire est affiché.

Tableau Récapitulatif: Statistiques du Jeu

Composant du Jeu Description Exemple de Valeur
Dimensions de l'écran Largeur et hauteur de la fenêtre du jeu 800 x 600
Vitesse du joueur Vitesse de déplacement du joueur en pixels/frame 5
Health (Vie) Points de vie du joueur ou du boss Joueur: 100, Boss: 500
Vitesse des projectiles Vitesse à laquelle les projectiles se déplacent Joueur: 10, Ennemi: 6, Boss: 5

Conseils pour Aller plus Loin

Ce code constitue une base sur laquelle vous pouvez construire et ajouter d'autres fonctionnalités du jeu. Quelques pistes pour étendre le projet incluent :

  • Power-ups : Ajouter des bonus qui améliorent la puissance de tir ou régénèrent la vie du joueur.
  • Variété d'ennemis : Implémenter plusieurs types d'ennemis ayant des patterns différents pour augmenter la difficulté.
  • Niveaux progressifs : Créer différents niveaux avec une augmentation progressive de la difficulté et des patterns de tir plus complexes pour le boss.
  • Effets visuels : Intégrer des animations, des particules et des effets sonores qui enrichiront l'expérience utilisateur.
  • Améliorations de la gestion des collisions : Optimiser la détection des collisions et les interactions pour assurer une jouabilité fluide même lorsque l'écran est saturé d'éléments.

Conclusion et Final Thoughts

Le développement d’un jeu Danmaku en Pygame est un projet passionnant qui combine la gestion d’événements, la programmation orientée objet et la manipulation d’images en temps réel. Le code proposé vous offre une structure robuste intégrant un joueur, des ennemis, un boss et une logique de fin de jeu. Les bases posées ici facilitent l’ajout de nouvelles fonctionnalités comme des power-ups, différents niveaux, et des patterns de tir avancés. En expérimentant et en étendant ce projet, vous pourrez améliorer l’expérience de jeu et créer un bullet-hell véritablement immersif. La modularité du code permet d’ajuster chaque composant indépendamment, ce qui est essentiel pour les jeux complexes.

L’adaptation du code à vos besoins spécifiques est encouragée, et vous pouvez aisément intégrer des éléments graphiques supplémentaires, des sons et des interfaces utilisateur avancées pour enrichir le gameplay. La gestion efficace des sprites, des mises à jour et des collisions assure non seulement de bonnes performances, mais ouvre également la voie à des améliorations constantes en vue d’un jeu plus interactif et captivant.


References


Recommended Further Queries

pygame.org
Bullethell

Last updated February 26, 2025

AI-assisted research. Sources, methodology and corrections · Source-checked guides and tools · Explore published answers

Ask Ithy AI
Download Article
Delete Article