TD4 : Gauntlet
L’objectif de ce TD est d’étudier la gestion des collisions dans un environnement complexe. Nous étudierons un jeu de labyrinthe type Gauntlet célèbre franchise des années 90. Vous pourrez retrouver sa version 3D Slayer Edition sortie en 2014.
Le jeu Gauntlet original 1987
Les bornes d’arcade de ce jeu se vendent aujourd’hui très chères, si vous avez un talent de bricoleur, cela peut être très rentable :
Mise en place
Téléchargez le projet Gauntlet.py
et ouvrez le.
Au lancement du programme, vous obtenez ceci :
Le projet est incomplet. Quelques remarques :
La fenêtre de jeu est trop petite
Le personnage se déplace uniquement verticalement
Le héros ne collisionne pas avec les murs
Il sort de l’écran et disparaît…
Rappels Python
Nous rappelons la syntaxe des principaux containers de donnée en Python.
Liste
L = ['titi', 5, 'toto']
print(L) >> [' titi', 5, 'toto']
print(L[0]) >> ‘titi’
L[1] += 1
L[2] = 'tata'
print(L) >> ['titi', 6, 'tata']
len(L) >> 3
Tableau 2D
import numpy as np
T = np.zeros((2,2))
T[0,0] = 1
T[0,1] = 2
T[1,1] = 3
print(T) >> array([[ 1., 2.], [ 0., 3.]])
T.shape[0] >> 2
Dictionnaire
D = { }
D['mami'] = 76
D['mami'] += 1
print(D['mami']) >> 77
D['papi'] = 74
print(D) >> {'mami': 77, 'papi': 74}
Différence
Pourquoi des listes et des tableaux ? Une liste, en Python, peut contenir des éléments de types différents : nombres, texte, sprites, listes… C’est un objet Python, il n’est pas optimisé et donc plutôt lent.
Le tableau Numpy est issu d’une librairie C recompilé pour Python. Il peut avoir une à plusieurs dimensions mais tous ces éléments doivent être du même type et il ne peut stocker que des types de base : entier, flottant, caractère. En contrepartie, il est très performant.
Il existe aussi le container dictionnaire. Il se comporte comme une liste sauf qu’il peut être indexé sur tout type de donnée comme notamment des strings : D[“age_du_capitaine”] = 44 :
En résumé :
J’ai plusieurs dimensions à gérer, les éléments sont de même type >> Tableau
J’ai une dimension indexée sur des string >> Dictionnaire
Dans les autres cas, une liste devrait convenir
Initialisation
On utilise différentes syntaxes :
L = ['titi', 5, 'toto']
T = np.zeros((2,2)) # init à 0
T = np.ones((2,2)) # init à 1
T = np.empty((2,2)) # allocation sans init
D = { key1:value1, key2:value2, key3:value3}
Notions clefs
Plan
Pour simplifier les choses, nous stockons le plan du labyrinthe dans une liste de strings :
Cela nous permet de représenter très simplement le plan du labyrinthe dans le code.
Couleurs
Dans ce projet, chaque lettre va coder une couleur. Pour cela, nous créons un dictionnaire nommé palette :
Ainsi, palette[“B”] correspond à la couleur bleu et palette[” “] à la couleur noire.
Grâce à ce choix, nous pouvons convertir le tableau plan vers un tableau 3 dimensions contenant les couleurs de chaque pixel en utilisant le code suivant :
Ainsi, LABY[x,y] désigne la couleur de la case [x,y] du labyrinthe.
Sprite du personnage
Certains d’entre vous n’aiment pas dessiner ou manipuler des images. Nous proposons ici un autre système de représentation basé sur le pixel-art. Chaque sprite est ainsi dessiné en utilisant une liste de string, chaque lettre codant la couleur d’un pixel. Le mécanisme est simple et permet de dessiner plein d’objets assez facilement :
La fonction ToSprite(…) permet de convertir une liste de string en sprite pour Pygame. Son principe est identique à celui de la construction du tableau LABY vu ci-dessus.
To-do List
Faites en sorte que l’aventurier se déplace grâce aux flèches du clavier gauche et droite
Utilisez le deuxième sprite de l’aventurier. Deux fois par seconde, alternez les deux sprites pour donner l’impression que le personnage marche en permanence
Indice : int(pygame.time.get_ticks()/500) augmente de 1 toutes les 500ms
La fonction modulo % devrait vous aider
Créez un sprite trésor (clef, porte..) qui symbolise la sortie, affichez le dans le jeu sur une case vide
Lorsque la distance entre la sortie et le sprite de l’aventurier est inférieure à 5 pixels, affichez « WIN » en gros au milieu de l’écran
Pour calculer une distance entre deux points : d( (x1,y1) , (x2,y2) ) ² = (x1-x2)² + (y1-y2)²
Notre aventurier a tendance à traverser les murs. Ce n’est pas acceptable ! Lorsqu’il avance vers la droite, testez sa collision avec le mur en regardant la couleur du pixel à sa droite :
Le sprite est symbolisé par un rectangle vert
Nous regardons la couleur du pixel devant le sprite à mi-hauteur
Si la couleur est noire, le joueur peut avancer, si la couleur est bleue, cela veut dire que le joueur est face à un mur et qu’il ne doit pas bouger dans cette direction
On doit lire la couleur du pixel après avoir dessiné le labyrinthe, pas avant !
La fonction screen.get_at((x,y)) retourne la couleur du pixel de l’écran
Les crochets [0] [1] et [2] permettent de récupérer les composantes R,V,B de la couleur
Les fonctions xxx.get_width() et xxx.get_height() donnent les dimensions d’un sprite
Le pixel de test doit être placé quelques pixels en avant du sprite et ne doit pas être collé à lui. En effet, si le sprite avance de 4 pixels par 4 pixels, il faut positionner le pixel de test 4 pixels devant le sprite
Gérez les collisions dans les 4 directions de déplacement
Créez un labyrinthe 20x20
Vous avez le choix entre diminuer la taille des cases par deux ou doubler la taille de la fenêtre de jeu
Pensez à créer un parcours complexe avec plusieurs chemins possibles et des emplacements pour des pièges. La sortie n’est pas forcément en bas à droite !
Créez un sprite piège : mine, trappe, flamme, bombe… Positionnez le dans le jeu
Lorsque le joueur passe sur le piège, remettez-le à la position de départ
L’animation de la marche a tendance à clignoter, en effet, nous utilisons seulement deux sprites, c’est insuffisant
Créez de nouveaux sprites pour l’animation de la marche pour améliorer le jeu de jambe du personnage
Modifiez la boucle d’animation en conséquence.
Créez un sprite trésor (coffre, gemme, pièce d’or…)
Ajoutez plusieurs trésors dans le parcours
Lorsque le joueur passe à proximité d’un trésor, celui-ci disparaît et le joueur gagne 100 points
Affichez le score en haut de l’écran
Créez un sprite clef et un sprite porte
Ajoutez la clef dans le labyrinthe
Positionnez le sprite porte au niveau de la sortie
Lorsque le joueur passe à proximité de la clef, elle disparaît et apparaît à coté du score pour indiquer qu’elle ait maintenant dans son inventaire
La sortie ne s’active pas si le héros n’a pas ramassé la clef
Faites en sorte de positionner plusieurs pièges