Tuto 4 : gérer des collisions

Ajouter des ennemis

Les tirs sont destinés à abattre des ennemis, il faut donc d'abord les créer dans le jeu !

Les images sont affichées à l’écran à raison de 30 frames ( = images ) par seconde ( ceci est modifiable lors de la création de la fenêtre du jeu ).

L’attribut frame_count du module Pyxel comptabilise le nombre d’images affichées depuis le début du jeu.
Ainsi, pour créer un ennemi toutes les secondes par exemple, on vérifie que le nombres d’images est un multiple de 30 :


if pyxel.frame_count % 30 == 0:	# utilisation de l'opérateur modulo = reste de la division 
	....... # création d'un ennemi			
			
Tuto 1

La création elle-même des ennemis repose sur le même principe que celui des tirs ( utilisation d'une liste liste_ennemis, attribut de la classe Jeu, et contenant des objets de type Ennemi ), mais on utilise la méthode randint() du module Python random. du module random pour les créer de façon aléatoire selon l’axe des x.

Cette création pourra se faire directement dans la méthode update() de la classe Jeu.

Compléter le code du script précédent en y ajoutant la création aléatoire d'ennemis toutes les secondes :

  • écrire une classe Ennemi, avec notamment ses méthodes move() et draw() qui géreront le déplacement des ennemis, et le test pour déterminer si ils arrivent en bas de la fenêtre ( attribut alive ).
  • un ennemi sera représenté par un carré de même taille, mais de couleur différente du vaisseau.
  • la position initiale d'un ennemi sera un x aléatoire pris entre 0 et 127 ( pensez à importer au préalable la méthode randint() ! ), et un y égal à 0.
  • on complétera les méthodes update() et draw() de la classe Jeu pour y inclure la création et la gestion du mouvement des ennemis, ainsi que leur éventuelle suppression de la liste.

Gestion des collisions tir/ennemi

Lorsqu'un des tirs touche un des ennemis, il faut faire disparaître les deux.

Tuto 1

Deux problèmes se posent donc :

Algorithme à suivre


pour chaque ennemi dans la liste des ennemis:
	pour chaque tir dans la liste des tirs:
		si ennemi et tir entrent en collision:
			les supprimer tous les deux de leur liste respective
			augmenter ( éventuellement ) le score
			

C'est donc deux boucles imbriquées de parcours de listes qu'il faudra utiliser.

Détection de la collision

Pour le cas de la collision d’un tir avec un ennemi, trois conditions sur les coordonnées du tir et de l'ennemi doivent être vérifiées simultanément :

  • la coordonnée ytir doit être inférieure ou égale à yennemi + 8
  • la coordonnée xtir doit être comprise entre xennemi et xennemi + 8
Collision tir/ennemi

La méthode update() de la classe Jeu avec le code correspondant à la détection d'une collision tir/ennemi :


    def update(self):

        ......

        for ennemi in self.liste_ennemis:
            for tir in self.liste_tirs:
                xt = tir.x
                yt = tir.y
                xe = ennemi.x
                ye = ennemi.y
                if yt <= ye+8 and ( xt >= xe and xt <= xe + 8):
                    self.liste_tirs.remove(tir)
                    self.liste_ennemis.remove(ennemi)
                    self.score = self.score + 1
			

Gestion des collisions vaisseau/ennemi

Dans ce cas, il suffit de parcourir la liste liste_ennemis, et de détecter la collision entre chacun de ses éléments et le vaisseau ( plus besoin de boucles imbriquées ).


pour chaque ennemi dans la liste des ennemis:
	si ennemi et vaisseau entrent en collision:
		supprimer l'ennemi de la liste des ennemis
		diminuer le nombre de vies de 1
			

Par contre, les conditions à vérifier lorsqu'il y a collisions sont un peu plus délicates, car plusieurs situations peuvent se présenter.

Ne considérons pour l'instant que les conditions à vérifier sur l'axe des x; les deux situations limites pour lesquelles il peut y avoir collision sont :

Collision vaisseau ennemi horizontal

Par un raisonnement similaire sur l'axe des y, on constaterait qu'il y a collision si : ye - 8 <= yv <= ye + 8

Il y a donc 4 conditions à vérifier simultanément pour détecter une collision entre le vaisseau et un ennemi.

Bien entendu, ces relations sont à adapter au cas où les deux acteurs n'ont pas la même taille...

Compléter la méthode update() du script pour gérer la collision entre un ennemi et le vaisseau.

On pourra éventuellement gérer un système de vies pour le vaisseau, décrémentées lors d'une collision avec un ennemi; dans la méthode draw(), on pourra alors n'afficher les acteurs que si le nombre de vies est supérieur à 0, et sinon, afficher "GAME OVER" et le score ( méthode pyxel.text() de Pyxel ).


class Jeu:

	.....
		
	def draw(self):
	
		pyxel.cls(0)
		
		if self.vies > 0:
			# on affiche les acteurs
			.......
			
		else:
			pyxel.text(50, 64, 'GAME OVER', 7) # affichage au milieu de la fenêtre
						
			

Remarque : le jeu continue cependant à "tourner"; pour quitter complètement une application Pyxel, utiliser la méthode pyxel.quit().

Ajout des explosions

Raffinement supplémentaire : rajouter des explosions lors de la destruction d'un ennemi.

Ces explosions peuvent être simplement représentées par quelques cercles de rayon croissant ( 5 par exemple dont le rayon va de 1 à 5 pixels ) qui se "propagent" depuis l'ancienne position de l'ennemi.

Tuto 1

La gestion des explosions peut se faire sur le même principe que les tirs et les ennemis, à savoir utiliser une liste liste_explosions, dans laquelle on ajoutera/supprimera au besoin des objets de type Explosion ( classe à écrire ! ).

Cependant, en plus des coordonnées de "départ" de l'explosion ( qui seront bien entendu égales, lors de la création de l'explosion, à celles de l'ancienne position de l'ennemi ), il faut prévoir un troisième élément, qui indiquera à quel "stade" l'explosion est, en stockant par exemple son rayon actuel; la valeur de cet élément sera incrémenté à chaque frame, et lorsqu'il sera devenu égal au rayon du plus grand cercle, l'explosion sera supprimée de la liste.


for ennemi in liste_ennemis:
    """ Test des collisions avec les tirs"""
    for tir in liste_tirs:
        x = tir[0]
        y = tir[1]
        xe = ennemi[0]
        ye = ennemi[1]
        if y <= ye+8 and ( x >= xe and x <= xe + 8):
            liste_tirs.remove(tir)
            liste_ennemis.remove(ennemi)
            liste_explosions.append([xe+4, ye+4, 1]) # ajout d'une explosion à la liste ( rayon initial = 1 pixel )
            score = score + 1

    """Tests des collisions avec le vaisseau"""
    if vaisseau_x < ennemi[0] + 8 and vaisseau_x > ennemi[0] - 8 and vaisseau_y < ennemi[1] + 8 and vaisseau_y > ennemi[1] - 8:
        liste_ennemis.remove(ennemi)
        liste_explosions.append([xe+4, ye+4, 1]) 	# ajout d'une explosion à la liste
        vie = vie - 1
			

Ecrire une classe Explosion et ses méthodes move() et draw(), qui seront appelées respectivement depuis les méthodes update() et draw() de la classe Jeu pour gérer les explosions.

On utilisera la même technique que précédemment ( utilisation d'un attribut alive ) pour gérer le cas où l'explosion a atteint son "maximum" et où il faut la supprimer de la liste.

Vous trouverez ici le code complet de cette première partie du tutoriel.

Suivant