Session 7 : Bug Zapper

Les bugs tombent du haut vers le bas. Toi, tu tires vers le haut pour les toucher. Dans cette session, tu apprends la POO : tu écris ta première classe à toi (Bug), puis deux sous-classes : une qui oscille et une qui plonge sur toi. Les classes sont les briques de presque tous les gros programmes.

Ce que tu vas apprendre aujourd’hui

  • Écrire une classe avec __init__ et tes propres méthodes
  • L’héritage : une sous-classe qui étend une classe existante
  • Garder les projectiles dans une liste
  • La détection de collision entre les projectiles et les bugs

Étape 0 : Installation

  1. Ouvre Thonny.
  2. Télécharge le starter via ce lien, décompresse le ZIP et ouvre main.py dans Thonny.
  3. Clique sur Run. Tu vois un écran avec un joueur en bas, mais pas encore de bugs et pas de projectiles.
  4. Des problèmes ? Demande à un coach.

Étape 1 : Faire apparaître des bugs et tirer

La classe Bug

Cherche le commentaire # ÉTAPE 1 — CLASSE BUG et écris :

class Bug:
    def __init__(self, x, y):
        self.x = x
        self.y = y
        self.alive = True

    def update(self):
        self.y += 2  # tombe vers le bas

    def draw(self, screen):
        pygame.draw.circle(screen, GREEN, (int(self.x), int(self.y)), 15)
        # Yeux
        pygame.draw.circle(screen, BLACK, (int(self.x) - 5, int(self.y) - 4), 3)
        pygame.draw.circle(screen, BLACK, (int(self.x) + 5, int(self.y) - 4), 3)

Faire apparaître des bugs : le starter a déjà un timer. Cherche # SPAWN BUG :

bugs.append(Bug(random.randint(30, WIDTH - 30), -20))

Tirer : cherche # TIR (c’est dans la boucle des événements, à K_SPACE) :

bullets.append(pygame.Rect(player_x - 3, player_y - 20, 6, 12))

Mets les projectiles à jour à chaque frame (cherche # MISE À JOUR PROJECTILES) :

for b in bullets[:]:
    b.y -= 10
    if b.y < 0:
        bullets.remove(b)
pygame.draw.rect(screen, YELLOW, b)  # ceci est déjà en place

Étape 2 : ✅ Basic : toucher les bugs

Maintenant tu écris la détection de collision : quand un projectile touche un bug, le bug meurt.

Cherche # ÉTAPE 2 — COLLISION :

for bug in bugs[:]:
    bug.update()
    bug.draw(screen)
    if bug.y > HEIGHT + 20:
        bugs.remove(bug)
        continue
    bug_rect = pygame.Rect(bug.x - 15, bug.y - 15, 30, 30)
    for bullet in bullets[:]:
        if bullet.colliderect(bug_rect):
            bug.alive = False
            bullets.remove(bullet)
            score += 1
            break
    if not bug.alive:
        bugs.remove(bug)

Clique sur Run. Tire sur les bugs : ils disparaissent dès que tu les touches !

Étape 3 : ⭐ Stretch : SlingerBug

Maintenant, tu crées une sous-classe : un Bug qui oscille de gauche à droite.

import math

class SlingerBug(Bug):
    def __init__(self, x, y):
        super().__init__(x, y)
        self.start_x = x
        self.timer = 0

    def update(self):
        self.timer += 0.05
        self.x = self.start_x + math.sin(self.timer) * 60
        self.y += 1.5  # un peu plus lent, mais plus dur à toucher

    def draw(self, screen):
        super().draw(screen)
        # Cercle orange autour, comme repère visuel
        pygame.draw.circle(screen, ORANGE, (int(self.x), int(self.y)), 18, 2)

Faire apparaître des SlingerBugs : ajoute ceci dans le code de spawn (par exemple un bug sur trois) :

if random.random() < 0.3:
    bugs.append(SlingerBug(random.randint(30, WIDTH - 30), -20))
else:
    bugs.append(Bug(random.randint(30, WIDTH - 30), -20))

super().__init__(x, y) appelle le __init__ de la classe parente (Bug), donc tu ne dois pas réécrire self.x, self.y et self.alive.

Étape 4 : 🔥 Expert : DuikBug

Le DuikBug détecte la position du joueur et plonge droit sur lui.

class DuikBug(Bug):
    def __init__(self, x, y, player_x_ref):
        super().__init__(x, y)
        self.player_x_ref = player_x_ref  # fonction qui donne le x actuel du joueur
        self.speed_x = 0
        self.speed_y = 1

    def update(self):
        target_x = self.player_x_ref()
        dx = target_x - self.x
        dist = max(abs(dx), 1)
        self.x += dx / dist * 2
        self.y += 2.5

    def draw(self, screen):
        super().draw(screen)
        pygame.draw.circle(screen, RED, (int(self.x), int(self.y)), 18, 2)

Spawn : utilise un lambda pour passer le x du joueur :

bugs.append(DuikBug(random.randint(30, WIDTH - 30), -20, lambda: player_x))

Bonus : dessine une barre de HP. Commence avec lives = 3 et enlève-en une quand un bug atteint le bas de l’écran.

Showcase

Montre ton jeu à un coach et à un buddy. Combien de bugs arrives-tu à zapper avant de ne plus avoir de vies ?

À la prochaine !

« La prochaine fois : une route infinie dans le ciel, et tout DÉFILE derrière toi. »

À emporter chez toi

Essaie une de ces extensions à la maison :

  1. Facile : rends les bugs plus grands ou plus rapides à mesure que le score monte.
  2. Moyen : ajoute un système de lives avec 3 vies ; si un bug atteint le bas, tu en perds une.
  3. Difficile : écris un PantserbBug qui a besoin de 3 tirs avant de mourir (utilise un attribut hp).
  4. Très difficile : ajoute le contrôle à la souris : le joueur suit la position de la souris et tire automatiquement toutes les 0,5 seconde.

Bloqué ? Demande au dojo suivant à un coach, ou essaie simplement autre chose. Programmer, c’est faire.

Dojo Defender : boss final

Dans cette session, tu continues aussi à construire Dojo Defender. Tu ajoutes un boss final qui apparaît toutes les 5 vagues, en 3 phases.

Va voir la session 6 de Dojo Defender pour les instructions.

Le starter et la solution sont prêts.


Regarde le cheatsheet de cette session