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 ?
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?
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...
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.
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
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
# 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:
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
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'])
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 ?
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.
Python c'est bon, mangez-en.
Python c'est bon, mangez-en.