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
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
-
Danmaku Project - Pygame
- Pygame Primer - Real Python
- Bullet Hell Shooter Guide - Dev.to
- Project WitchCraft - GitHub
Recommended Further Queries
Last updated February 26, 2025