Tu vas créer un jeu Zelda interactif !
| Site: | Le labo numérique de M. MIGNOTTE – Cours, projets et ressources pour les élèves de seconde SNT et STI2D |
| Course: | Chapitre 6 : Python |
| Book: | Tu vas créer un jeu Zelda interactif ! |
| Printed by: | Guest user |
| Date: | Saturday, 14 March 2026, 7:01 AM |
Table of contents
- 1. ÉTAPE 0 : INTRODUCTION & MOTIVATION
- 2. ÉTAPE 1 : Afficher une fenêtre de jeu
- 3. ÉTAPE 2 : Créer un personnage qui se déplace
- 4. ÉTAPE 3 : Ajouter des ennemis
- 5. ÉTAPE 4 : Gérer les collisions (Game Over)
- 6. ÉTAPE 5 : Le Système d'Attaque
- 7. ÉTAPE 6 : Remplacer les carrés par des images (Sprites)
- 8. ÉTAPE 7 : Créer un Monde (Fond d'écran et Limites)
- 9. ÉTAPE 8 : Tirer des projectiles (Boules de feu / Flèches)
- 10. ÉTAPE 9 : Afficher le Score et le Texte
- 11. ÉTAPE 10 : Portage sur Console (Recalbox)
1. ÉTAPE 0 : INTRODUCTION & MOTIVATION
✓ Créer un personnage qui se déplace avec les touches directionnelles
✓ Affronter des ennemis qui se déplacent seuls✓ Combattre avec une épée et vaincre les ennemis-
Durée estimée : 5-7 séances de TP (1h chacune)
-
Prérequis : Variables, boucles, conditions, bases des fonctions
-
Outils : Visual Code studio + Pygame
Visualisation
Semaine 1 : Étapes 1-2 (affichage + déplacement) Semaine 2 : Étapes 3-4 (ennemis + combat) Semaine 3 : Étapes 5-6 (son + interface) Semaine 4 : Étapes 7-8 (graphismes avancés + niveaux)
Ressource média proposée
-
Démo du jeux
2. ÉTAPE 1 : Afficher une fenêtre de jeu
Objectif
L'élève doit voir apparaître à l'écran une fenêtre noire intitulée "Zelda".
Question motivante
"Tout jeu commence par une fenêtre. Comment créer cette première fenêtre en Python ?"
Concepts clés à maîtriser
-
Importer une bibliothèque :
import pygame -
Initialiser :
pygame.init() -
Créer une fenêtre :
pygame.display.set_mode((largeur, hauteur)) -
Nommer la fenêtre :
pygame.display.set_caption("titre") -
Boucle infinie :
while True:(la fenêtre reste ouverte) -
Rafraîchir l'écran :
pygame.display.update()
pythonimport pygame import sys [COMPLETE] # Initialiser Pygame WIDTH = 1200 HEIGHT = 800 [COMPLETE] # Créer une fenêtre de 1200x800 pixels [COMPLETE] # Donner un titre à la fenêtre BLACK = (0, 0, 0) running = True while running: for event in pygame.event.get(): [COMPLETE] # Vérifier si on ferme la fenêtre [COMPLETE] # Remplir l'écran [COMPLETE] # Mettre à jour l'affichage pygame.quit() sys.exit()
À toi de jouer (défi d'extension)
-
Change la couleur de fond en bleu (
BLUE = (0, 0, 255)) -
Change la taille de la fenêtre à 800×600
-
Ajoute un titre personnalisé
Erreurs courantes & solutions
| Erreur | Cause | Solution |
|---|---|---|
ModuleNotFoundError: No module named 'pygame' | Pygame non installé | pip install pygame |
| La fenêtre ferme immédiatement | Pas de boucle infinie | Vérifier le while True: |
| L'écran reste blanc | Pas de screen.fill() | Ajouter la ligne de remplissage |
3. ÉTAPE 2 : Créer un personnage qui se déplace
Objectif
L'élève crée un carré bleu (le héros) qui se déplace avec les flèches du clavier.
Question motivante
"Maintenant qu'on a une fenêtre, où place-t-on le personnage ? Comment le fait-on bouger ?"
Concepts clés à maîtriser
-
Rectangle Pygame :
pygame.Rect(x, y, largeur, hauteur) -
Position (x, y) : coordonnées du joueur
-
Détecter les touches clavier :
pygame.key.get_pressed() -
Les touches directionnelles :
pygame.K_UP,pygame.K_DOWN,pygame.K_LEFT,pygame.K_RIGHT -
Mettre à jour la position :
player.x += 5(déplacement) -
Afficher un rectangle :
pygame.draw.rect(screen, color, rect)
import pygame
import sys
pygame.init()
WIDTH, HEIGHT = 1200, 800
screen = pygame.display.set_mode((WIDTH, HEIGHT))
pygame.display.set_caption("Zelda avec Python")
BLACK, BLUE = (0, 0, 0), (0, 0, 255)
# ===== PARAMÈTRES DU JOUEUR =====
player_x = 600
player_y = 400
player_width = 40
player_height = 40
player_speed = [COMPLETE] # À quelle vitesse se déplace le joueur ?
running = True
while running:
for event in pygame.event.get():
[COMPLETE] # Vérifier si on ferme
# ===== CAPTURER LES TOUCHES =====
keys = [COMPLETE] # Récupérer l'état des touches
if keys[pygame.K_UP]:
[COMPLETE] # Déplacer le joueur vers le haut
if [COMPLETE]: # Vérifier si DOWN est pressé
player_y += player_speed
if keys[pygame.K_LEFT]:
player_x -= player_speed
if [COMPLETE]: # Ajouter le déplacement à droite
# ===== AFFICHAGE =====
screen.fill(BLACK)
[COMPLETE] # Dessiner le rectangle bleu du joueur
pygame.display.update()
pygame.quit()
sys.exit()
À toi de jouer
-
Change la couleur du joueur en rouge ou vert
-
Augmente ou diminue la vitesse de déplacement
-
Fais apparaître le joueur à une position différente (coin haut-gauche)
-
Défi + : Empêche le joueur de sortir de l'écran (bonus : bordures invisibles)
Erreurs courantes
| Erreur | Solution |
|---|---|
| Le joueur ne bouge pas | Vérifier que keys[pygame.K_UP] est bien complet |
| Le joueur sort de l'écran | Ajouter des conditions pour limiter x et y |
| Le joueur bouge trop vite/lentement | Ajuster player_speed |
Progression observable
✓ Fenêtre noire ✓ Carré bleu au centre ✓ Carré bouge avec les flèches ✓ Le joueur peut se déplacer partout à l'écran
Lien pédagogique
Ce système de déplacement sera réutilisé pour les ENNEMIS à l'Étape 3.
4. ÉTAPE 3 : Ajouter des ennemis
Objectif
L'élève crée 2-3 ennemis qui se déplacent automatiquement à l'écran.
Question motivante
"Un jeu sans adversaires, c'est ennuyeux. Comment créer des ennemis qui se déplacent seuls ?"
Concepts clés à maîtriser
-
Les listes : stocker plusieurs ennemis
-
Les dictionnaires : regrouper les propriétés d'un ennemi (x, y, vx, vy)
-
Les boucles : parcourir et afficher tous les ennemis
-
Mouvement autonome : ennemis qui se déplacent sans intervention
-
Rebond aux murs : inverser la direction quand on touche les limites
Code à compléter
pythonimport pygame import sys pygame.init() WIDTH, HEIGHT = 1200, 800 screen = pygame.display.set_mode((WIDTH, HEIGHT)) pygame.display.set_caption("Zelda avec Python") BLACK, BLUE, RED = (0, 0, 0), (0, 0, 255), (255, 0, 0) # ===== JOUEUR ===== player = { 'x': 600, 'y': 400, 'width': 40, 'height': 40, 'speed': 5 } # ===== ENNEMIS ===== enemies = [ { 'x': 200, 'y': 200, 'width': 40, 'height': 40, 'vx': 3, # Vitesse horizontal (positif = vers la droite) 'vy': 0 # Vitesse vertical }, { 'x': 1000, 'y': 600, 'width': 40, 'height': 40, 'vx': -3, 'vy': 0 }, { 'x': 600, 'y': 100, 'width': 40, 'height': 40, 'vx': 0, 'vy': 2 # Se déplace vers le bas } ] # ===== BOUCLE PRINCIPALE ===== running = True clock = pygame.time.Clock() while running: clock.tick(60) # 60 fps (images par seconde) for event in pygame.event.get(): if event.type == pygame.QUIT: running = False # ===== DÉPLACEMENT DU JOUEUR ===== keys = pygame.key.get_pressed() if keys[pygame.K_UP]: player['y'] -= player['speed'] if keys[pygame.K_DOWN]: player['y'] += player['speed'] if keys[pygame.K_LEFT]: player['x'] -= player['speed'] if keys[pygame.K_RIGHT]: player['x'] += player['speed'] # ===== DÉPLACEMENT DES ENNEMIS ===== for enemy in enemies: enemy['x'] += enemy['vx'] enemy['y'] += enemy['vy'] # Rebondir aux murs horizontalement if enemy['x'] < 0 or enemy['x'] + enemy['width'] > WIDTH: enemy['vx'] *= -1 # Rebondir aux murs verticalement if enemy['y'] < 0 or enemy['y'] + enemy['height'] > HEIGHT: enemy['vy'] *= -1 # ===== AFFICHAGE ===== screen.fill(BLACK) # Dessiner le joueur pygame.draw.rect(screen, BLUE, (player['x'], player['y'], player['width'], player['height'])) # Dessiner les ennemis for enemy in enemies:
5. ÉTAPE 4 : Gérer les collisions (Game Over)
Objectif
L'élève apprend à détecter quand deux rectangles se touchent pour déclencher une action (ici, arrêter le jeu ou réinitialiser la position).
Question motivante
"À quoi servent des ennemis si on peut leur passer au travers sans danger ? Comment faire pour que le jeu sache quand le joueur est touché ?"
Concepts clés à maîtriser
La détection de collision (AABB) : Vérifier si les coordonnées d'un rectangle chevauchent celles d'un autre.
La structure conditionnelle
ifcomplexe : Utiliser des opérateurs logiques pour comparer les positions.La gestion de l'état du jeu : Savoir quand stopper la boucle ou modifier une variable.
Code à compléter (Suite du TP)
Vous pouvez demander à vos élèves d'ajouter ce bloc juste après la section du déplacement des ennemis :
# ===== DÉTECTION DES COLLISIONS =====
for enemy in enemies:
# On vérifie si le rectangle du joueur touche le rectangle de l'ennemi
# Formule simplifiée : (A.gauche < B.droite) et (A.droite > B.gauche) ...
if (player['x'] < enemy['x'] + enemy['width'] and
player['x'] + player['width'] > enemy['x'] and
player['y'] < enemy['y'] + enemy['height'] and
player['y'] + player['height'] > enemy['y']):
print("TOUCHÉ ! Game Over")
# Option 1 : Arrêter le jeu
# running = False
# Option 2 : Réinitialiser le joueur au centre
player['x'], player['y'] = 600, 400
Pour l'affichage (Fin de votre code précédent)
Voici ce qu'ils devaient compléter pour l'affichage des ennemis dans la boucle :
# Dessiner les ennemis
for enemy in enemies:
pygame.draw.rect(screen, RED, (enemy['x'], enemy['y'], enemy['width'], enemy['height']))
# Mettre à jour l'écran
pygame.display.flip()
pygame.quit()
sys.exit()
6. ÉTAPE 5 : Le Système d'Attaque
Objectif
L'élève apprend à détecter une pression de touche pour déclencher une action offensive et à retirer un ennemi de la liste enemies.
Question motivante
"Se faire toucher, c'est bien, mais se défendre, c'est mieux ! Comment faire pour 'éliminer' un dictionnaire d'une liste ?"
Concepts clés à maîtriser
La gestion des événements clavier : Déclencher une action unique (touche
ESPACE).La distance entre deux points : Calculer si l'ennemi est assez proche pour être touché.
La modification de liste : Utiliser
.remove()ou recréer une liste pour supprimer un ennemi mort.
Code à intégrer (Après le mouvement du joueur)
Voici la structure que vous pouvez donner à compléter à vos élèves :
# ===== SYSTÈME D'ATTAQUE =====
if keys[pygame.K_SPACE]:
# On crée une zone d'attaque autour du joueur (un peu plus large que lui)
attack_range = 60
# On parcourt la liste à l'envers ou on crée une copie pour éviter les erreurs de suppression
for enemy in enemies[:]:
# Calcul de la distance simplifiée (ou collision avec une zone d'attaque)
dist_x = abs(player['x'] - enemy['x'])
dist_y = abs(player['y'] - enemy['y'])
if dist_x < attack_range and dist_y < attack_range:
enemies.remove(enemy)
print("Ennemi vaincu !")
Pourquoi cette étape est importante ?
Logique de liste : C'est la première fois qu'ils modifient la taille de la liste
enemiesen plein jeu.Feedback visuel : L'ennemi disparaît instantanément de l'écran car il n'est plus parcouru dans la boucle d'affichage.
Défi pour les élèves rapides (Bonus) :
"Ajoutez une variable
scorequi augmente de 10 points à chaque fois qu'un ennemi est supprimé, et affichez-la dans la console !"
7. ÉTAPE 6 : Remplacer les carrés par des images (Sprites)
Objectif
L'élève apprend à charger des fichiers externes (images .png) et à les afficher à la place des formes géométriques.
Question motivante
"Un carré bleu, c'est fonctionnel, mais un héros avec une épée, c'est mieux ! Comment transformer nos données en graphismes ?"
Concepts clés à maîtriser
pygame.image.load(): Importer un fichier image dans la mémoire.pygame.transform.scale(): Redimensionner l'image pour qu'elle corresponde à la taille (width, height) du dictionnaire.screen.blit(): "Tamponner" l'image sur l'écran (remplacedraw.rect).
Code à préparer (Avant la boucle principale)
Il faut d'abord charger les images. Pour que le code fonctionne, les élèves doivent avoir un dossier nommé assets avec des fichiers player.png et enemy.png.
# ===== CHARGEMENT DES IMAGES =====
# On charge l'image et on la redimensionne directement à la taille du joueur
player_img = pygame.image.load("assets/player.png").convert_alpha()
player_img = pygame.transform.scale(player_img, (player['width'], player['height']))
enemy_img = pygame.image.load("assets/enemy.png").convert_alpha()
enemy_img = pygame.transform.scale(enemy_img, (40, 40)) # Taille standard
Code à modifier (Dans la section AFFICHAGE)
Les élèves doivent remplacer leurs pygame.draw.rect(...) par :
# Dessiner le joueur avec son image
screen.blit(player_img, (player['x'], player['y']))
# Dessiner les ennemis avec leur image
for enemy in enemies:
screen.blit(enemy_img, (enemy['x'], enemy['y']))
Le défi "Pro" (Optionnel)
Gestion de l'orientation :
"Comment faire pour que le personnage regarde à gauche quand on appuie sur la touche GAUCHE ?"
Indice : Utiliser
pygame.transform.flip(player_img, True, False).
8. ÉTAPE 7 : Créer un Monde (Fond d'écran et Limites)
Objectif
L'élève apprend à manipuler une image de fond (background) et à contraindre le joueur pour qu'il ne sorte pas de l'écran (collisions avec les bords du monde).
Question motivante
"Le vide intersidéral, c'est pour l'espace. Comment donner l'impression que notre héros marche sur de l'herbe ou dans un donjon ?"
Concepts clés à maîtriser
screen.blit()pour le décor : L'ordre d'affichage est crucial (le décor d'abord, les entités après).Les fonctions
min()etmax(): Bloquer les coordonnées du joueur entre 0 et la largeur/hauteur de l'écran.
1. Préparation (Avant la boucle)
Les élèves doivent charger une image de sol (ex: grass.png) qu'ils vont répéter ou étirer à la taille de l'écran.
# ===== CHARGEMENT DU DÉCOR =====
background_img = pygame.image.load("assets/background.png").convert()
background_img = pygame.transform.scale(background_img, (WIDTH, HEIGHT))
2. Contrainte de mouvement (Dans la section DÉPLACEMENT DU JOUEUR)
On utilise max() et min() pour s'assurer que x et y restent dans les limites de WIDTH et HEIGHT.
# Limiter le joueur aux bords de l'écran
player['x'] = max(0, min(player['x'], WIDTH - player['width']))
player['y'] = max(0, min(player['y'], HEIGHT - player['height']))
3. Affichage (Dans la section AFFICHAGE)
Attention : Il faut dessiner le fond AVANT le reste, sinon il cachera le joueur.
# 1. Remplir le fond (ou dessiner l'image de fond)
screen.blit(background_img, (0, 0))
# 2. Dessiner les ennemis
# ...
# 3. Dessiner le joueur (en dernier pour qu'il soit "au-dessus")
# ...
Le Défi "Générateur" :
"Au lieu d'un seul fond, pouvez-vous créer une boucle
forqui dessine une petite image d'herbe de 64x64 pixels sur tout l'écran comme une grille (un dallage) ?"
9. ÉTAPE 8 : Tirer des projectiles (Boules de feu / Flèches)
Objectif
L'élève apprend à gérer une deuxième liste dynamique (les projectiles) qui ont leur propre mouvement et une durée de vie limitée.
Question motivante
"Et si on pouvait éliminer les ennemis de loin ? Comment créer un objet qui 'apparaît' quand on appuie sur une touche et qui avance tout seul ?"
Concepts clés à maîtriser
Initialisation dynamique : Créer un nouveau dictionnaire au moment du clic ou de la touche.
Gestion des listes (Nettoyage) : Supprimer le projectile quand il sort de l'écran pour ne pas ralentir l'ordinateur.
1. Préparation (Avant la boucle)
On crée une liste vide pour stocker nos projectiles.
# ===== PROJECTILES =====
projectiles = []
projectile_speed = 7
2. Création du projectile (Événement clavier)
On peut utiliser la touche X ou ENTRÉE pour tirer.
# Dans la gestion des événements (ou après le mouvement)
if event.type == pygame.KEYDOWN:
if event.key == pygame.K_x:
# On crée un dictionnaire pour le nouveau projectile
new_bullet = {
'x': player['x'] + player['width'] // 2,
'y': player['y'],
'width': 10,
'height': 10,
'vx': 0,
'vy': -projectile_speed # Tire vers le haut par défaut
}
projectiles.append(new_bullet)
3. Logique et Collision (Boucle principale)
Il faut faire avancer les projectiles et vérifier s'ils touchent un ennemi.
# Déplacement des projectiles
for p in projectiles[:]:
p['y'] += p['vy']
# Supprimer si sort de l'écran
if p['y'] < 0:
projectiles.remove(p)
continue
# Collision Projectile <-> Ennemi
for enemy in enemies[:]:
if (p['x'] < enemy['x'] + enemy['width'] and
p['x'] + p['width'] > enemy['x'] and
p['y'] < enemy['y'] + enemy['height'] and
p['y'] + p['height'] > enemy['y']):
if enemy in enemies: enemies.remove(enemy)
if p in projectiles: projectiles.remove(p)
print("Cible détruite !")
4. Affichage
# Dessiner les projectiles
for p in projectiles:
pygame.draw.circle(screen, (255, 255, 0), (p['x'], p['y']), 5)
Le Défi "Sniper" :
"Modifiez le code pour que le projectile aille dans la direction où le personnage se déplace (si je vais à droite, le projectile part à droite) !"
Indice : Il faudra stocker la dernière direction du joueur dans le dictionnaire
player.
10. ÉTAPE 9 : Afficher le Score et le Texte
Objectif
L'élève apprend à manipuler les polices de caractères (fonts) pour afficher des informations en temps réel sur l'écran de jeu.
Question motivante
"C'est bien de voir 'Touché' dans la console noire en bas, mais comment le joueur peut-il voir son score directement dans le jeu ?"
Concepts clés à maîtriser
pygame.font.SysFont(): Choisir une police d'écriture.font.render(): Transformer une chaîne de caractères (String) en une image (Surface).La concaténation/f-string : Mélanger du texte fixe ("Score : ") et une variable variable.
1. Préparation (Avant la boucle)
Il faut initialiser le système de texte et créer une variable pour le score.
# ===== SCORE ET POLICE =====
score = 0
# On choisit une police système (Arial) de taille 36
font = pygame.font.SysFont("Arial", 36)
def afficher_score(x, y):
# 1. Créer l'image du texte (True = lissage des bords)
score_img = font.render(f"Ennemis vaincus : {score}", True, (255, 255, 255))
# 2. L'afficher à l'écran
screen.blit(score_img, (x, y))
2. Mise à jour du score (Dans la section COLLISION PROJECTILE)
Quand un ennemi est supprimé, on augmente le score.
# Dans votre boucle de collision projectile <-> ennemi :
if enemy in enemies:
enemies.remove(enemy)
score += 10 # On ajoute 10 points !
3. Affichage (Dans la section AFFICHAGE)
Il suffit d'appeler notre fonction tout à la fin de la boucle, pour que le score soit écrit par-dessus tout le reste.
# Tout à la fin, juste avant pygame.display.flip()
afficher_score(20, 20)
Le Défi Final "Game Over" :
"Si la liste
enemiesdevient vide (longueur = 0), affichez un grand message 'BRAVO, NIVEAU TERMINÉ !' au milieu de l'écran et arrêtez le mouvement du joueur."
Félicitations !
Avec ces 9 étapes, vos élèves ont construit un moteur de jeu complet :
Affichage & Fenêtre
Mouvement Joueur
Système d'Ennemis (IA simple)
Collisions (Game Over)
Attaque de mêlée
Graphismes (Sprites)
Monde & Limites
Projectiles (Tir)
Interface (Score)
11. ÉTAPE 10 : Portage sur Console (Recalbox)
Objectif
L'élève apprend à rendre son jeu compatible avec un matériel spécifique (le Raspberry Pi) et à remplacer les entrées clavier par celles d'une manette.
Question motivante
"Votre jeu est super sur PC, mais comment le transformer en une véritable 'ROM' jouable sur une télévision avec une manette de Super Nintendo ?"
Concepts clés à maîtriser
Le module
Joystick: Détecter et lire les axes/boutons d'une manette.Le mode
FULLSCREEN: Utiliser tout l'affichage de la télévision.La gestion de la sortie : Pouvoir quitter le jeu sans clavier (indispensable sur console).
1. Préparation du matériel (Avant la boucle)
On initialise la manette et on force le plein écran pour l'immersion.
# ===== CONFIGURATION CONSOLE =====
pygame.joystick.init()
joysticks = [pygame.joystick.Joystick(i) for i in range(pygame.joystick.get_count())]
# Passage en plein écran (0,0 s'adapte automatiquement à la TV)
screen = pygame.display.set_mode((0, 0), pygame.FULLSCREEN)
WIDTH, HEIGHT = screen.get_size()
2. Mouvement à la manette (Dans la boucle)
On remplace (ou on ajoute) la lecture des axes de la manette.
# Lecture de la manette si elle est branchée
for joy in joysticks:
# Axe 0 = Horizontal, Axe 1 = Vertical
# On multiplie par la vitesse du joueur
player['x'] += joy.get_axis(0) * player['speed']
player['y'] += joy.get_axis(1) * player['speed']
# SÉCURITÉ : Quitter le jeu avec le bouton "START" (souvent le bouton 7)
if joy.get_button(7):
running = False
3. Actions (Tirer / Attaquer)
On utilise l'événement JOYBUTTONDOWN pour déclencher les actions.
for event in pygame.event.get():
if event.type == pygame.QUIT:
running = False
# Si on appuie sur un bouton de la manette
if event.type == pygame.JOYBUTTONDOWN:
if event.button == 0: # Bouton 'A' sur SNES
# Appeler ici le code d'attaque ou de tir
print("Action manette détectée !")
Le Défi "Arcade" :
"Faites en sorte que si on appuie sur 'SELECT' (bouton 6) et 'START' (bouton 7) en même temps, le jeu se ferme. C'est le raccourci standard sur Recalbox !"