Partage
  • Partager sur Facebook
  • Partager sur Twitter

Collisions sur Snake en python

Via Pygame

    28 novembre 2015 à 17:31:08

    Bonjour à tous et à toutes,

    Je débute en python depuis peu et je cherche à créer un jeu typique "Snake" !

    Je travaille sous Pygame et j'ai suivi les cours du site sur Python et Pygame :)

    J'ai également trouvé un programme d'un internaute qui avait réalisé un jeu Snake sous python il y'a quelques années et je m'en suis inspiré pour créer le mien. Le fait est que cet internaute avait précisé qu'il n'avait pas pris en compte des éléments comme les collisions (pas d'obstacles, pas de difficulté).

    Voici le lien de son programme : https://openclassrooms.com/forum/sujet/jeu-video-python-snake-avec-pygame-86858

    J'ai cherché des solutions sur plusieurs sites et sur plusieurs forums mais rien ne me convenait malheureusement.

    Quelqu'un pourrait-il m'aider à faire en sorte que la partie soit finie dès que mon serpent mange sa propre queue ?

    Merci d'avance ! :)

    • Partager sur Facebook
    • Partager sur Twitter
      28 novembre 2015 à 18:46:39

      Bonjour,

      Qu'est ce qui ne te convient pas dans les solutions que tu a vu ? (histoire de nous orienter sur ce que tu voudrais ;) )

      • Partager sur Facebook
      • Partager sur Twitter
        28 novembre 2015 à 22:25:35

        Mmh ça fait un bail que je n'ai plus fait de pygame mais le problème est juste de savoir si à un instant t la tête du serpent est à un endroit, à un instant t+1 elle n'appartient pas à l'ensemble des positions des différents carrés du serpent, ce n'est pas très compliqué, qu'est ce que tu n'arrives pas à faire?

        • Partager sur Facebook
        • Partager sur Twitter
          28 novembre 2015 à 23:17:53

          Tu as regardé ici : https://openclassrooms.com/forum/sujet/pygame-mini-projet-snake-28824
          • Partager sur Facebook
          • Partager sur Twitter

          Python c'est bon, mangez-en. 

            28 novembre 2015 à 23:53:11

            Merci pour vos réponses !

            Disons que je voudrais effectuer une simple boucle while qui parcourrait les coordonnées du serpent (sous forme de liste si possible) !

            Le fait est que je ne sais pas comment correctement écrire mon code pour faire en sorte qu'en cas de collision, la partie s'arrête. Je comprends le principe d'une collision en fonction de la tête et des autres carrés du serpent mais je ne vois pas vraiment comment rédiger ça :/

            Josmiley, j'ai déjà eu l'occasion de voir ton post (qui m'a par ailleurs aidé) mais je dois avouer que j'ai des difficultés à comprendre comment tu as géré tes collisions ici...

            • Partager sur Facebook
            • Partager sur Twitter
              29 novembre 2015 à 0:45:22

              Admettons que ton serpent soit une liste de coordonnées uniques  (chaque partie est une coordonnée), si le serpent se mord, alors tu te retrouves avec 2 fois une même coordonnée : la coordonnée de la tête est la même que celle de la partie mordue. Suffit de tester la présence de la coordonnée de la tête dans le reste du corps, un "in" devrait faire l'affaire.
              • Partager sur Facebook
              • Partager sur Twitter

              Python c'est bon, mangez-en. 

                29 novembre 2015 à 12:14:04

                Je vous mets mon code au cas où :

                import pygame

                import os, sys

                import time                       # temps

                from random import randint        # randint consiste à apporter un entier aléatoire (nous voulons être sûr que le serpent passe sur un entier)

                from pygame.locals import * 

                pygame.init()

                pygame.mixer.music.load("Theme_of_Solid_Snake_-_Super_Smash_Bros._Brawl.mp3")

                pygame.mixer.music.play()

                LARGEUR = 600

                HAUTEUR = 500

                fenetre = pygame.display.set_mode((LARGEUR, HAUTEUR)) # Création de la fenetre d'affichage du jeu (avec largeur et hateur prédéfinie)

                pygame.display.set_caption('Jeu Snake')               # On choisit le nom de la fenetre pour le jeu

                #------------------------------------------------------------------------------------------------------------------------------------------------------

                #Choix des couleurs (En RVB comme d'autres langages de programmation)

                GRIS = (140 , 140, 140)

                NOIR = (0, 0, 0)

                BLANC = (255, 255, 255)

                ROUGE = (255, 0, 0)

                VERT = (0, 255, 0)

                BLEU = (0, 0, 255)

                BLEUCLAIR = (120, 200, 240)        # On peut également changer les teintes de nos couleurs primaires

                BLEUFONCE = (20, 80, 130)

                DIRECTION = 0                      # On initialise le départ du serpent et donc du jeu !

                COTE = 10  

                FOOD = 1

                fenetre.fill(NOIR)                 # fond de la fenêtre (on le choisit noir afin de pouvoir voir chaque carré quelque soit sa couleur)

                #------------------------------------------------------------------------------------------------------------------------------------------------------

                class Snake(object):

                    def __init__(self):

                        self.corps = BLEUCLAIR

                        self.tete = BLEUFONCE

                        self.serpent = [{'rect':pygame.Rect(LARGEUR/2, HAUTEUR/2,COTE,COTE), 'color':self.tete, 'dire':0}]

                        self.score = 0                                                                                     # On crée notre serpent et on l'initialise à 0

                    def addblock(self, n):

                                                                                                                           # On vient ajouter n carrés à droite de la queue du serpent      

                        i = 0

                        while i < n:                                                                                       # On utilise les valeurs de la queue pour définir le carré

                            Y = self.serpent[len(self.serpent)-1]['rect'].top - COTE - 1

                            X = self.serpent[len(self.serpent)-1]['rect'].left          

                            DIRE = self.serpent[len(self.serpent)-1]['dire']

                            self.serpent.append({'rect':pygame.Rect(X, Y, COTE, COTE),'color':self.corps, 'dire':DIRE})    # append permet d'ajouter un élément dans une liste, comme inser

                            i+=1

                    def moove(self):                                                                                       # On supprime la queue du serpent (carré par carré)

                        del self.serpent[len(self.serpent)-1]

                                                                                                                           # On définit une nouvelle tête à partir de l'ancienne

                        Y = self.serpent[0]['rect'].top

                        X = self.serpent[0]['rect'].left

                        if DIRECTION == 1:                                                                                 # On définit les directions grace aux touches 1 ,2 ,3 ,4 selon les côtés 

                            Y += (COTE + 1)                                                                                # 1, 2, 3, 4 = ('Bas', 'Haut', 'Droite', 'Gauche') et X guide la nouvelle tête du serpent

                        if DIRECTION == 2:

                            Y -= (COTE + 1)            

                        if DIRECTION == 3:

                            X += (COTE + 1)

                        if DIRECTION == 4:

                            X -= (COTE + 1)

                        self.serpent = [{'rect':pygame.Rect(X, Y, COTE, COTE), 'color':self.tete, 'dire':DIRECTION}] + self.serpent

                        self.serpent[1]['color'] = self.corps                                                              # Ici, ces deux lignes permettent au serpent de prendre toutes les caracteristiques que l'on a définit

                #------------------------------------------------------------------------------------------------------------------------------------------------------                                                                                                          

                def nourriture():                                                  # On créer des carrés qui correspondent à la nourriture du serpent 

                    X = randint(0, LARGEUR-10)

                    Y = randint(0, HAUTEUR-10)                                 

                    color = (randint(0, 255), randint(0, 255), randint(0, 255))                                                   

                    food = {'rect':pygame.Rect(X, Y, 10, 10), 'color':color}       # On prend -10 pour que le carré soit toujours affiché en "entier" : randint nous permet d'afficher des carrés de couleurs aléatoires

                    global food

                #------------------------------------------------------------------------------------------------------------------------------------------------------

                sn = Snake()

                sn.addblock(2)     # Notre serpent débute avec une tête et deux carrés de longueur

                nourriture()

                while True:

                    for event in pygame.event.get():

                        if event.type == QUIT:

                            pygame.quit()

                            sys.exit()                    

                        if event.type == KEYDOWN:

                            # On définit la direction en empêchant les demi-tours du serpent (le serpent à l'instant t+1 ne peut revenir sur sa position à l'instant t)

                            if event.key == K_DOWN and DIRECTION != 2: DIRECTION = 1

                            if event.key == K_UP and DIRECTION != 1: DIRECTION = 2

                            if event.key == K_RIGHT and DIRECTION != 4: DIRECTION = 3

                            if event.key == K_LEFT and DIRECTION != 3: DIRECTION = 4

                            # On fait en sorte que le serpent ne puisse qu'afficher le nombre de carrés dont il dispose grâce aux carrés qu'il aura absorbé 

                            if event.key == K_RSHIFT: DIRECTION = 0

                    # fond de la fenêtre      

                    fenetre.fill(NOIR)

                    # On utilise la commande moove() pour que le serpent se déplace librement (sans demi-tour bien sûr)

                    # On attend 0.05 secondes pour obtenir une vitesse stable et jouable (nous avons choisi cette valeur car autrement le serpent va trop vite ou trop lentement)

                    if DIRECTION:

                        time.sleep(0.05)

                        sn.moove()

                    # On fait en sorte que le serpent puisse passer d'un bord à l'autre de la fenêttre sans perdre la partie   

                    if sn.serpent[0]['rect'].top >= HAUTEUR: sn.serpent[0]['rect'].top = 1

                    if sn.serpent[0]['rect'].top <= 0: sn.serpent[0]['rect'].top = HAUTEUR

                    if sn.serpent[0]['rect'].left >= LARGEUR: sn.serpent[0]['rect'].left = 1

                    if sn.serpent[0]['rect'].left <= 0: sn.serpent[0]['rect'].left = LARGEUR

                    # On affiche le serpent avec les formes et couleurs choisies

                    for carre in sn.serpent:

                        pygame.draw.rect(fenetre, carre['color'], carre['rect'])

                    # On affiche également la nourriture du serpent (qui prend bien une couleur aléatoire à chaque carré)

                    pygame.draw.rect(fenetre, food['color'], food['rect'])

                #------------------------------------------------------------------------------------------------------------------------------------------------------

                    # Cette étape nous permet de mettre en contact la nourriture et la tête du serpent : si le serpent rencontre un carré (nourriture) pendant sa course...

                    # ... Alors on ajoute un point au score et un carré au serpent pour qu'il grandisse...

                    # ... Et surtout on fait apparaitre un nouveau carré !

                    if sn.serpent[0]['rect'].left >= food['rect'].left:

                        if sn.serpent[0]['rect'].left <= food['rect'].right:

                            if sn.serpent[0]['rect'].top >= food['rect'].top:

                                if sn.serpent[0]['rect'].top <= food['rect'].bottom:

                                    sn.score += 1

                                    sn.addblock(1)

                                    nourriture()

                    if sn.serpent[0]['rect'].right >= food['rect'].left:

                        if sn.serpent[0]['rect'].right <= food['rect'].right:

                            if sn.serpent[0]['rect'].bottom <= food['rect'].bottom:

                                if sn.serpent[0]['rect'].bottom >= food['rect'].top:

                                    sn.score += 1

                                    sn.addblock(1)

                                    nourriture()

                #------------------------------------------------------------------------------------------------------------------------------------------------------

                # On affiche la taille du serpent et le score du joueur en haut à gauche de la fenêtre ! (en blanc bien sûr pour un fond noir)

                    police = pygame.font.Font(None, 20)

                    tvit = police.render('Taille : {} ; Score : {}'.

                                format(len(sn.serpent), sn.score),1,BLANC)

                    fenetre.blit(tvit,(10,10))

                    pygame.display.update()

                • Partager sur Facebook
                • Partager sur Twitter
                  29 novembre 2015 à 12:38:37

                  import pygame
                  import os, sys
                  import time                       # temps
                  from random import randint        # randint consiste à apporter un entier aléatoire (nous voulons être sûr que le serpent passe sur un entier)
                  from pygame.locals import * 
                   
                  pygame.init()
                  
                  pygame.mixer.music.load("Theme_of_Solid_Snake_-_Super_Smash_Bros._Brawl.mp3")
                  pygame.mixer.music.play()
                  
                  LARGEUR = 600
                  HAUTEUR = 500
                   
                  fenetre = pygame.display.set_mode((LARGEUR, HAUTEUR)) # Création de la fenetre d'affichage du jeu (avec largeur et hateur prédéfinie)
                  pygame.display.set_caption('Jeu Snake')               # On choisit le nom de la fenetre pour le jeu
                  
                  
                  #------------------------------------------------------------------------------------------------------------------------------------------------------
                   
                  #Choix des couleurs (En RVB comme d'autres langages de programmation)
                  GRIS = (140 , 140, 140)
                  NOIR = (0, 0, 0)
                  BLANC = (255, 255, 255)
                  ROUGE = (255, 0, 0)
                  VERT = (0, 255, 0)
                  BLEU = (0, 0, 255)
                  BLEUCLAIR = (120, 200, 240)        # On peut également changer les teintes de nos couleurs primaires
                  BLEUFONCE = (20, 80, 130)
                   
                  
                  DIRECTION = 0                      # On initialise le départ du serpent et donc du jeu !
                   
                  COTE = 10  
                   
                  FOOD = 1
                  
                  fenetre.fill(NOIR)                 # fond de la fenêtre (on le choisit noir afin de pouvoir voir chaque carré quelque soit sa couleur)
                   
                  #------------------------------------------------------------------------------------------------------------------------------------------------------
                   
                  class Snake(object):
                      def __init__(self):
                          
                          self.corps = BLEUCLAIR
                          self.tete = BLEUFONCE
                          self.serpent = [{'rect':pygame.Rect(LARGEUR/2, HAUTEUR/2,COTE,COTE), 'color':self.tete, 'dire':0}]
                          self.score = 0                                                                                     # On crée notre serpent et on l'initialise à 0
                  
                      def addblock(self, n):
                                                                                                                             # On vient ajouter n carrés à droite de la queue du serpent      
                          i = 0
                          while i < n:                                                                                       # On utilise les valeurs de la queue pour définir le carré
                              Y = self.serpent[len(self.serpent)-1]['rect'].top - COTE - 1
                              X = self.serpent[len(self.serpent)-1]['rect'].left          
                              DIRE = self.serpent[len(self.serpent)-1]['dire']
                               
                              self.serpent.append({'rect':pygame.Rect(X, Y, COTE, COTE),'color':self.corps, 'dire':DIRE})    # append permet d'ajouter un élément dans une liste, comme inser
                              i+=1
                          
                      def moove(self):                                                                                       # On supprime la queue du serpent (carré par carré)
                          
                          del self.serpent[len(self.serpent)-1]
                           
                                                                                                                             # On définit une nouvelle tête à partir de l'ancienne
                          Y = self.serpent[0]['rect'].top
                          X = self.serpent[0]['rect'].left
                           
                          if DIRECTION == 1:                                                                                 # On définit les directions grace aux touches 1 ,2 ,3 ,4 selon les côtés 
                              Y += (COTE + 1)                                                                                # 1, 2, 3, 4 = ('Bas', 'Haut', 'Droite', 'Gauche') et X guide la nouvelle tête du serpent
                          if DIRECTION == 2:
                              Y -= (COTE + 1)            
                          if DIRECTION == 3:
                              X += (COTE + 1)
                          if DIRECTION == 4:
                              X -= (COTE + 1)
                               
                          self.serpent = [{'rect':pygame.Rect(X, Y, COTE, COTE), 'color':self.tete, 'dire':DIRECTION}] + self.serpent
                                   
                          self.serpent[1]['color'] = self.corps                                                              # Ici, ces deux lignes permettent au serpent de prendre toutes les caracteristiques que l'on a définit
                  
                          
                  #------------------------------------------------------------------------------------------------------------------------------------------------------                                                                                                          
                  
                  def nourriture():                                                  # On créer des carrés qui correspondent à la nourriture du serpent 
                      X = randint(0, LARGEUR-10)
                      Y = randint(0, HAUTEUR-10)                                 
                      color = (randint(0, 255), randint(0, 255), randint(0, 255))                                                   
                      food = {'rect':pygame.Rect(X, Y, 10, 10), 'color':color}       # On prend -10 pour que le carré soit toujours affiché en "entier" : randint nous permet d'afficher des carrés de couleurs aléatoires
                      global food
                      
                  #------------------------------------------------------------------------------------------------------------------------------------------------------
                   
                  sn = Snake()
                  sn.addblock(2)     # Notre serpent débute avec une tête et deux carrés de longueur
                  nourriture()
                   
                  while True:
                      for event in pygame.event.get():
                          if event.type == QUIT:
                              pygame.quit()
                              sys.exit()                    
                               
                          if event.type == KEYDOWN:
                               
                              # On définit la direction en empêchant les demi-tours du serpent (le serpent à l'instant t+1 ne peut revenir sur sa position à l'instant t)
                              if event.key == K_DOWN and DIRECTION != 2: DIRECTION = 1
                              if event.key == K_UP and DIRECTION != 1: DIRECTION = 2
                              if event.key == K_RIGHT and DIRECTION != 4: DIRECTION = 3
                              if event.key == K_LEFT and DIRECTION != 3: DIRECTION = 4
                                   
                              # On fait en sorte que le serpent ne puisse qu'afficher le nombre de carrés dont il dispose grâce aux carrés qu'il aura absorbé 
                              if event.key == K_RSHIFT: DIRECTION = 0
                             
                        
                      # fond de la fenêtre      
                      fenetre.fill(NOIR)
                       
                      # On utilise la commande moove() pour que le serpent se déplace librement (sans demi-tour bien sûr)
                      # On attend 0.05 secondes pour obtenir une vitesse stable et jouable (nous avons choisi cette valeur car autrement le serpent va trop vite ou trop lentement)
                      if DIRECTION:
                          time.sleep(0.05)
                          sn.moove()
                       
                      # On fait en sorte que le serpent puisse passer d'un bord à l'autre de la fenêttre sans perdre la partie   
                      if sn.serpent[0]['rect'].top >= HAUTEUR: sn.serpent[0]['rect'].top = 1
                      if sn.serpent[0]['rect'].top <= 0: sn.serpent[0]['rect'].top = HAUTEUR
                      if sn.serpent[0]['rect'].left >= LARGEUR: sn.serpent[0]['rect'].left = 1
                      if sn.serpent[0]['rect'].left <= 0: sn.serpent[0]['rect'].left = LARGEUR
                       
                      # On affiche le serpent avec les formes et couleurs choisies
                      for carre in sn.serpent:
                          pygame.draw.rect(fenetre, carre['color'], carre['rect'])
                           
                      # On affiche également la nourriture du serpent (qui prend bien une couleur aléatoire à chaque carré)
                      pygame.draw.rect(fenetre, food['color'], food['rect'])
                  #------------------------------------------------------------------------------------------------------------------------------------------------------
                      
                      # Cette étape nous permet de mettre en contact la nourriture et la tête du serpent : si le serpent rencontre un carré (nourriture) pendant sa course...
                      # ... Alors on ajoute un point au score et un carré au serpent pour qu'il grandisse...
                      # ... Et surtout on fait apparaitre un nouveau carré !
                      if sn.serpent[0]['rect'].left >= food['rect'].left:
                          if sn.serpent[0]['rect'].left <= food['rect'].right:
                              if sn.serpent[0]['rect'].top >= food['rect'].top:
                                  if sn.serpent[0]['rect'].top <= food['rect'].bottom:
                                      sn.score += 1
                                      sn.addblock(1)
                                      nourriture()
                       
                      if sn.serpent[0]['rect'].right >= food['rect'].left:
                          if sn.serpent[0]['rect'].right <= food['rect'].right:
                              if sn.serpent[0]['rect'].bottom <= food['rect'].bottom:
                                  if sn.serpent[0]['rect'].bottom >= food['rect'].top:
                                      sn.score += 1
                                      sn.addblock(1)
                                      nourriture()
                  
                  #------------------------------------------------------------------------------------------------------------------------------------------------------
                           
                  # On affiche la taille du serpent et le score du joueur en haut à gauche de la fenêtre ! (en blanc bien sûr pour un fond noir)
                      police = pygame.font.Font(None, 20)
                      tvit = police.render('Taille : {} ; Score : {}'.
                                  format(len(sn.serpent), sn.score),1,BLANC)
                      fenetre.blit(tvit,(10,10))
                     
                               
                      pygame.display.update()
                  

                  Au temps  pour moi !

                  -
                  Edité par ThomasFranzoni 29 novembre 2015 à 12:39:08

                  • Partager sur Facebook
                  • Partager sur Twitter
                    29 novembre 2015 à 16:38:28

                    Il y a beaucoup de choses à simplifier et à revoir.

                    Ton serpent est composé d'une liste de dictionnaires avec un Rect, une couleur et ... une direction ?? C'est étrange et inutile. Tu dois juste tenir à jour la direction dans laquelle la tête va se déplacer. Le reste du corps ne fait que suivre. Même la couleur est discutable. Si par convention la tête est d'une couleur et le reste du corps d'une autre couleur, pourquoi répéter la couleur du corps pour chaque élément du corps ? Pour une meilleure approche, je définirais une méthode draw(self, surface) dans la classe Snake qui permet de se dessiner dans la surface passée en argument. Dans la boucle principale, tu auras juste à appelé sn.draw(fenetre) pour dessiner ton serpent.

                    Dans la méthode moove, pour supprimer le dernier élément, il suffit de faire del self.serpent[-1] ou encore en plus concis self.serpent.pop(). Pour s'y retrouver avec les directions, perso je créerais des constantes avec des noms plus parlant.

                    # Tout en haut
                    UP, DOWN, LEFT, RIGHT = range(4)
                    
                    # Et dans le code, comme à la ligne 107
                    if event.key == K_DOWN and DIRECTION != UP: DIRECTION = DOWN
                    

                    Pour déterminer si 2 Rect sont en collision, on utilise la méthode colliderect des Rect. Donc les 4 if en lignes 142-145 peuvent se résumer à if sn.serpent[0]['rect'].colliderect(food):

                    Ligne 161, tu vas charger ta font à chaque tour de boucle de ton jeu ! Sors cette ligne de la boucle et place-la avant le while.

                    Pour l'ajout de bloque, on procède généralement différemment. Sachant que quand le serpent bouge, on efface le dernier élément et qu'on en recolle un nouveau du côté de la tête (en début de liste), si on veut faire grandir le serpent, on omet simplement d'effacer le dernier élément. Ainsi tu pourrais simplement avoir une variable de ta classe Snake qui pourrait prendre comme valeur True ou False self.doit_grandir = False. Lorsque tu veux faire grandir le serpent, cette variable passe à True. Dans la méthode moove tu aurais alors un test dans le style

                    def moove(self):
                        if self.doit_grandir:
                            self.doit_grandir = False
                        else:
                            self.serpent.pop()
                    

                    Ainsi on n'efface le dernier élément de la queue que quand la variable self.doit_grandir est False.

                    Avec tout ça en place, pour voir si la tête touche une partie du corps du serpent, il faudra tester si le Rect de la tête touche une des Rect du reste du corps. Il y a une méthode collidelist(liste_de_rects) qui teste si le rectangle est en collision avec n'importe quel rectangle de la liste passée en argument. Cependant tu n'as pas cette liste toute prête. Tu peux la construire en faisant

                    liste_de_rects = [element['rect'] for element in self.serpent]
                    # Ou bien si tu ne connais pas les listes en compréhension
                    liste_de_rects = []
                    for element in self.serpent:
                        liste_de_rects.append(element['rect'])
                    
                    • Partager sur Facebook
                    • Partager sur Twitter
                      29 novembre 2015 à 16:58:43

                      Merci beaucoup pour toutes ces précisions !

                      Il y'a en effet de nombreuses simplifications à faire :)

                      Je vais tenter de construire cette liste dont tu me parlais afin de gérer les collisions ! A quelle section de mon programme dois-je cependant la coder ?

                      • Partager sur Facebook
                      • Partager sur Twitter

                      Collisions sur Snake en python

                      × Après avoir cliqué sur "Répondre" vous serez invité à vous connecter pour que votre message soit publié.
                      × Attention, ce sujet est très ancien. Le déterrer n'est pas forcément approprié. Nous te conseillons de créer un nouveau sujet pour poser ta question.
                      • Editeur
                      • Markdown