Premier mini-projet : concevoir un jeu ressemblant à Space Invaders
Space Invaders
Space Invaders est un jeu sorti en 1978 sur borne d'arcade.
Le principe est de piloter un vaisseau qui lutte contre des invasions d'aliens à l'aide d'un canon laser.
TRES IMPORTANT
- la création de ce programme a été découpée en plusieurs étapes pour te faciliter les choses
- chaque étape te propose un code pré-écrit dans lequel il y a des trous représentés par des pointillés que tu devras compléter
- pour réussir à compléter ces trous, il faut BIEN LIRE les commentaires et la documentation
L'objectif de ce projet, dans un premier temps, est de créer ce qui suit :
Etape 1 : Coder la fenêtre de notre jeu
C'est exactement la même étape dans les deux mini-projets
import pyxel
# constantes (des valeurs qui ne changent jamais)
LARGEUR = ... # largeur de notre fenêtre
HAUTEUR = ... # hauteur de notre fenêtre
FPS = ... # nombre d'images par seconde dans notre jeu
NOIR = ... # voir la documentation pour connaître le nombre correspondant à la couleur noire
BLANC = ... # faire pareil avec les autres couleurs
VERT = ...
ORANGE = ...
ROUGE = ...
TITRE = ... # titre de la fenêtre (à mettre entre guillemets)
# taille de la fenetre 128x128 pixels
# penser à utiliser les constantes définies juste au-dessus
pyxel.init(..., ..., title = ... , fps = ...)
def update():
pass # pour l'instant, on ne fait rien dans cette fonction
def draw():
# effacer la fenêtre et la remplir de couleur noire (voir la documentation pour savoir comment faire)
# utiliser la constante NOIR définie plus haut
...
pyxel.run(update, draw)
A faire dans cet exercice :
- copier / coller le code ci-dessus dans votre éditeur et faites ce qui suit :
- dans les parenthèses de pyxel.init, remplacer les ... par les valeurs appropriées pour obtenir une fenêtre de 128 pixels de large sur 128 pixels de haut avec le titre que vous souhaitez et qui se ferme grâce à la touche que vous aurez choisie.
- dans la fonction draw(), écrire le code qui permet d'effacer le contenu de la fenêtre et de mettre un fond noir. Si besoin, chercher dans la documentation de pyxel
- exécuter votre programme pour vérifier qu'il fonctionne
Vous pouvez vous aider du cours de ce site et de la documentation en ligne pour répondre à ce premier exercice.
Ci-dessous les codes des couleurs à utiliser dans Pyxel :
Etape 2 : Insérer une forme (un sprite) qui représentera notre vaisseau
Pour faire simple, on représentera notre vaisseau par un carré.
Nous pourrons, lors de la prochaine étape, déplacer cette forme, cet élément de notre jeu. Il va donc porter le nom de sprite.
import pyxel
# constantes (des valeurs qui ne changent jamais)
LARGEUR = ... # largeur de notre fenêtre
HAUTEUR = ... # hauteur de notre fenêtre
FPS = ... # nombre d'images par seconde dans notre jeu
NOIR = ... # voir la documentation pour connaître le nombre correspondant à la couleur noire
BLANC = ... # faire pareil avec les autres couleurs
VERT = ...
ORANGE = ...
ROUGE = ...
TITRE = ... # titre de la fenêtre (à mettre entre guillemets)
TAILLE_SPRITE = ... # taille (arête) de notre sprite (carré)
# variables globales (des valeurs qui vont changer et qui seront utilisées dans tout le programme)
vaisseau_x = ... # définir la position horizontale du vaisseau au début
vaisseau_y = ... # définir la position verticale du vaisseau au début
# taille de la fenetre 128x128 pixels
# penser à utiliser les constantes définies juste au-dessus
pyxel.init(..., ..., title = ... , fps = ...)
def update():
pass # pour l'instant, on ne fait rien dans cette fonction
def draw():
# effacer la fenêtre et la remplir de couleur noire (voir la documentation pour savoir comment faire)
# utiliser la constante NOIR définie plus haut
...
# dessiner une forme carrée de couleur ORANGE (notre vaisseau) (voir la documentation ou ci-dessous pour savoir comment faire)
# le carré est positionné aux coordonnées de l'écran vaisseau_x, vaisseau_y et il est de taille CASE
...
pyxel.run(update, draw)
On complète notre programme précédent :
- au début du programme, on détermine les coordonnées x, y pour positionner le vaisseau (vaisseau_x, vaisseau_y).
A vous de choisir. Attention la fenêtre ne fait que 128 sur 128 pixels, il ne faut pas dépasser ces valeurs.
- dans draw(), nous allons dessiner un carré représentant notre vaisseau. Pour cela nous allons utiliser la fonction pyxel.rect().
Cette fonction est paramétrée de la façon suivante : pyxel.rect(positionx, positiony, largeur, hauteur, couleur) :
- positionx, positiony : position (horizontale, verticale) du carré
- largeur : largeur du carré
- hauteur : hauteur du carré
- couleur : couleur du carré (à choisir parmi les 16 disponibles)
Etape 3 : Déplacement du vaisseau
Reprenons le programme précédent
A FAIRE
- On ajoute une constante VITESSE_VAISSEAU qui définira la vitesse de déplacement de notre sprite
- On modifie la fonction update
- Complète ton programme en copiant en dessous ce qui manque dans le programme précédent et en complétant les pointillés
import pyxel
# constantes
VITESSE_VAISSEAU = ...
def update():
# on doit mettre ce qui suit pour que les variables globales puissent être utilisées dans la fonction update()
global vaisseau_x, vaisseau_y
# mise à jour de la position du sprite en ajoutant la vitesse à la position
vaisseau_x += ...
Etape 4 : Gérer les sorties d'écrans
Problème du programme précédent : notre sprite sort de la fenêtre !!!
Corrigeons cela avec une condition.
A FAIRE
- Une fois encore, on ne modifie que la fonction update
- Complète ton programme en rajoutant ce qu'il manque et en complétant les pointillés
def update():
# on doit mettre ce qui suit pour que les variables globales puissent être utilisées dans la fonction update()
global vaisseau_x, vaisseau_y
# mise à jour de la position du sprite en ajoutant la vitesse à la position
# si la position du vaisseau dépasse la largeur de la fenêtre, on déplace le vaisseau le vaisseau de l'autre côté de la fenêtre
if vaisseau_x < ... - ... :
vaisseau_x += ...
Si tu n'as pas fait d'erreur, le sprite se déplace vers la droite jusqu'au bord de la fenêtre et s'arrête.
Etape 5 : Utilisation du clavier pour changer la direction du vaisseau
Maintenant que tu as compris comment déplacer le sprite, ajoutons du code pour faire ce déplacement seulement quand on appuie sur une touche du clavier.
Pour détecter une touche clavier enfoncée, on utilise pyxel.btn() en passant en paramètre la touche sur laquelle on appuie.
Rappel : pour connaître les touches, il faut aller voir à la fin de la documentation.
Par exemple : pyxel.btn(pyxel.KEY_RIGHT) deviendra vrai si on appuie sur la flèche droite du clavier.
def update():
# on doit mettre ce qui suit pour que les variables globales puissent être utilisées dans la fonction update()
global vaisseau_x, vaisseau_y
# si la touche flèche droite du clavier est enfoncée
if ...:
# mise à jour de la position du sprite en ajoutant la vitesse à la position
# si la position du vaisseau ne dépasse pas [largeur de la fenêtre - largeur du sprite], on déplace le vaisseau
if vaisseau_x < ...- ...:
vaisseau_x += ...
Etape 6 : Utilisation du clavier pour déplacer le vaisseau dans l'autre sens
Compléter le code précédent dans def update() avec un déplacement dans l'autre direction (vers la gauche).
Essayer d'améliorer votre programme, pour que le sprite ne sorte pas de la fenêtre.
Conseil
- Il suffit de faire presque la même chose que pour déplacer le vaisseau vers la gauche
- Plutôt que d'utiliser if, on utilisera elif pour else if afon d'éviter le cas où l'utilisateur appuie sur les 2 touches en même temps
Il faut donc rajouter le code suivant (en le complétant) à notre update() :
# dans def update(), on rajoute :
# si la touche flèche gauche du clavier est enfoncée
elif pyxel.btn(...):
# mise à jour de la position du sprite en ajoutant la vitesse à la position
if vaisseau_x > ... : # si le vaisseau ne sort pas de l'écran à gauche
... -= ... # on déplace le vaisseau vers la gauche
Etape 7 : Ajouter un canon laser à notre vaisseau
A partir de maintenant, nous voulons que notre vaisseau tire chaque fois que nous appuyons sur la touche Espace.
A FAIRE
- déclarer deux nouvelles variables (à mettre avec les autres variables) tir_x, tir_y qui donneront la position de notre tir
- quand on appuie sur la touche espace, un rectangle jaune (représentant le tir) apparaît sur l'écran à la position tir_x, tir_y
- le tir se déplace vers le haut de la fenêtre et disparaît
Attention
Une fois de plus, dans le code ci-dessous, n'apparaît que ce qu'il faudra ajouter à ton programme par rapport à ce que tu as déjà écrit. FAIT BIEN ATTENTION !
# constantes
LARGEUR_TIR = 1
HAUTEUR_TIR = 4
JAUNE = 10
# variables
tir_x = 0 # peu importe les valeurs, elles seront modifiées dès qu'on appuie sur la touche Espace par la suite
tir_y = 0
vitesse_tir = 4 # vitesse de déplacement du tir
def update():
# on doit mettre ce qui suit pour que les variables globales puissent être utilisées dans la fonction update()
global vaisseau_x, vaisseau_y, tir_x, tir_y
# si la touche espace est enfoncée et relâchée, le tir est créé DEPUIS LA POSITION DU VAISSEAU
if pyxel.btnr(...): # btnr parce que le tir en partira que la touche ESPACE est relâchée
tir_x = ... + TAILLE_SPRITE // 2 # pour que le tir parte du milieu du vaisseau
tir_y = ...
# le tir se déplace vers le haut de la fenêtre
tir_y -= ...
def draw():
# dessin des tirs : on dessine un rectangle positionné en tir[0], tir[1], de taille largeur_tir par hauteur_tir de couleur JAUNE
pyxel.rect(tir[0], tir[1], ..., ..., ...)
Révisions
Si tu ne comprends pas tir[0], tir[1], relis le cours sur les listes
Essaie ton programme une fois complété... Ca marche comme prévu ?
Pas vraiment, non... Si la touche Espace est enfoncée de façon répétée le tir précédent ne va pas jusqu'en haut de la fenêtre parce qu'il est automatiquement remplacé par un nouveau tir.
ATTENTION A partir d'ici, ça se complique !!!
Si tu ne sais pas ce que sait, il faut ABSOLUMENT que tu remontes dans la partie PYTHON DEBUTANT pour comprendre ce qu'est une liste et comment on s'en sert
- pour obtenir ce que l'on veut, nous allons devoir créer une liste dans laquelle nous allons stocker tous les tirs créés
- nous dessinerons chaque élément de cette liste (chaque tir donc)
- nous ferons se déplacer chaque élément de cette liste vers le haut
Dans la partie variable du programme, on remplace tir_x et tir_y par la liste liste_tirs.
# variables
liste_tirs = [] # création d'une liste vide pour stocker les tirs
def update():
# si la touche espace est enfoncée et relâchée, le tir est créé
if pyxel.btnr(...):
tir_x = ... + TAILLE_SPRITE // 2 # pour que le tir parte du milieu du vaisseau
tir_y = ...
liste_tirs.append([tir_x,tir_y]) # on ajoute le tir à la liste défini par sa position horizontale et sa position verticale
# pour chaque tir de la liste, le tir se déplace vers le haut de la fenêtre (on modifie la position verticale du tir)
for tir in liste_tirs:
tir[1] -= ... # tir[1] désigne le second élément de tir donc la position y - revoir les listes si ce n'est pas clair
def draw():
# pour chaque tir présent dans la liste, on dessine un rectangle positionné en tir[0], tir[1], de taille largeur_tir par hauteur_tir et de couleur JAUNE
for tir in liste_tirs:
pyxel.rect(tir[0], tir[1], ..., ..., ...)
Mais une fois que le tir est sorti de l'écran, il reste dans la liste.
Ca ne va pas. Il faut le retirer.
def update():
# suppression du tir de liste_tirs une fois qu'il est sorti de l'écran
for tir in liste_tirs:
if tir[1] < 0:
liste_tirs.remove(tir)
Etape 8 : Dessiner les aliens sur une première rangée
Pour représenter les aliens, nous allons, de nouveau, dessiner des carrés de largeur TAILLE_SPRITE.
Pour ne pas les dessiner et les gérer un par un, nous allons regrouper tous ces aliens dans une liste appelée liste_aliens.
Rappel
Une fois de plus ici, nous ne mettons que le code à ajouter ou à modifier.
# variables
liste_aliens = [] # on crée une liste vide pour stocker les POSITIONS des aliens
# création des aliens
for x in range(15, LARGEUR - 10, 12):
liste_aliens.append([x, 10])
def draw():
# dessiner les aliens
for alien in liste_aliens:
# on dessine des carrés en position alien[0], alien[1], de dimensions TAILLE_SPRITE x TAILLE_SPRITE et de couleur blanche
pyxel.rect(alien[0], alien[1], ..., ..., ...)
Etape 9 : Dessiner deux autres rangées d'aliens
Il n'y a qu'une seule rangée d'aliens alors que dans la version originale du jeu, il y a plusieurs rangées.
Nous allons modifier notre programme pour créer trois rangées.
# création des aliens
# on crée une boucle dite imbriquée : on augmente 3 fois y de 10 pour créer 3 rangées
# dans chaque rangée, on crée 10 aliens comme précédemment
for y in range(10,40,10):
for x in range(10,LARGEUR-10,12):
liste_aliens.append([x, y])
Etape 10 : Remplacer les carrés (vaisseau et aliens) par des images
Le fichier (la banque d'images) qui contient les sprites est à accessible en cliquant sur le lien suivant :
Il faut le télécharger et le déposer dans le même répertoire que le programme Python.
A FAIRE
- télécharger le fichier space.pyxres et le déposer dans le même répertoire que le programme Python
- dans le programme, charger le fichier à l'aide de pyxel.load
- remplacer le carré du vaisseau et les carrés des aliens par des images de la banque d'images
pyxel.load("space.pyxres") # !!! à placer en dessous de pyxel.init
def draw():
# on supprime cette ligne
#pyxel.rect(vaisseau_x, vaisseau_y, CASE, CASE, VERT)
# on la remplace par pyxel.blt
# il faut indiquer la position du vaisseau à la place des pointillés
# 0,0,0 signifie l'image 0 de la banque positionnée en 0,0 dans la banque de dimension TAILLE_SPRITE par TAILLE_SPRITE)
#
pyxel.blt(..., ... , O, O ,O , ..., ...)
# dessiner les aliens
for alien in liste_aliens:
# on remplace cette ligne
# pyxel.rect(alien[0], alien[1], CASE, CASE, BLANC) # ajout d'un décalage de 5 à x pour centrer la rangée d'alien
# par la ligne suivante
pyxel.blt(alien[0], alien[1] , O, 8 ,O , TAILLE_SPRITE, TAILLE_SPRITE)
Etape 11 : Affichage du score
Chaque fois qu'un alien est détruit, le score doit être augmenté de 1.
A FAIRE
- créer une variable score qu'on initialise à 0.
- dans draw(), afficher le score placé en haut à gauche
# variables
score = ...
def draw():
# afficher le score et du texte en haut à gauche avec la couleur BLANCHE
pyxel.txt(..., ..., "Score " + str(score), ...)
Etape 12 : Collisions entre les tirs du vaisseau et les aliens
Dans cette partie, pas de devinette.
Nous allons donner directement la fonction qui permet de vérifier si deux sprites sont en collision.
Volontairement, c'est une fonction généraliste puisque nous allons l'utiliser pour la collision entre les tirs du vaisseau et les aliens MAIS AUSSI, plus tard, entre les tirs des aliens et le vaisseau.
# fonction qui détermine si un objet A de largeurA et hauteurA entre en collision avec un objet B de largeurB et hauteurB
# si les deux objets sont en collision, la fonction renvoie vrai (True)
# sinon elle rennvoire Faux (False)
def collision(A, B, largeurA, hauteurA, largeurB, hauteurB):
# A : tir / B : alien
if (B[0] + largeurB > A[0]) and (B[0] < A[0] + largeurA) and \
(B[1] + hauteurA > A[1]) and (B[1] < A[1] + hauteurB):
return True
else:
return False
A FAIRE
Si le tir du vaisseau entre en collision avec un alien : * retirer l'alien de la liste * augmenter le score de 1
def update():
# gestion des collisions entre les tirs du vaisseau et les aliens
for alien in liste_aliens:
for tir in liste_tirs:
if collision(tir, alien, LARGEUR_TIR, HAUTEUR_TIR, TAILLE_SPRITE, TAILLE_SPRITE):
liste_aliens.remove(alien)
liste_tirs.remove(tir)
score += 1
En cours de rédaction...