Implémentation du jeu Snake en Python
Cet article décrit le processus de développement d'un jeu Snake en utilisant Python et la bibliothèque Turtle, avec des fonctionnalités telles que le déplacement du serpent, la gestion des scores, la génération d'obstacles et la détection des collisions.
Configuration initiale de la fenêtre de jeu
La bibliothèque Turtle est utilisée pour créer une intefrace graphique simple. Voici comment configurer la fenêtre :
import turtle
import random
fenetre = turtle.Screen()
fenetre.title("Jeu Snake")
fenetre.bgcolor("noir")
fenetre.setup(width=600, height=600)
fenetre.tracer(0) # Désactive le rafraîchissement automatique pour éviter le scintillement
La méthode tracer(0) est essentielle pour contrôler manuellement le rafraîchissement de l'écran, améliorant ainsi les performances.
Initialisation des éléments du jeu
Le serpent est composé d'une tête et d'un corps stocké dans une liste. La nourriture et les obstacles sont créés comme des objets Turtle séparés.
# Tête du serpent
tete = turtle.Turtle()
tete.speed(0)
tete.shape("square")
tete.color("rouge", "jaune")
tete.penup()
tete.goto(0, 0)
tete.direction = "stop"
# Corps du serpent (liste de segments)
corps = []
# Nourriture
nourriture = turtle.Turtle()
nourriture.speed(0)
nourriture.shape("circle")
nourriture.color("vert")
nourriture.penup()
nourriture.goto(100, 0)
# Score
score = 0
affichage_score = turtle.Turtle()
affichage_score.speed(0)
affichage_score.color("blanc")
affichage_score.penup()
affichage_score.hideturtle()
affichage_score.goto(0, 260)
affichage_score.write("0", align="center", font=("Arial", 24, "normal"))
Logique de déplacement et de contrôle
Le déplacement du serpent est géré par des fonctions qui changent la direction en fonction des touches clavier. La position est mise à jour en incrémentant les coordonnées x ou y.
def deplacer_haut():
if tete.direction != "bas":
tete.direction = "haut"
def deplacer_bas():
if tete.direction != "haut":
tete.direction = "bas"
def deplacer_gauche():
if tete.direction != "droite":
tete.direction = "gauche"
def deplacer_droite():
if tete.direction != "gauche":
tete.direction = "droite"
def mouvement():
if tete.direction == "haut":
y = tete.ycor()
tete.sety(y + 20)
elif tete.direction == "bas":
y = tete.ycor()
tete.sety(y - 20)
elif tete.direction == "gauche":
x = tete.xcor()
tete.setx(x - 20)
elif tete.direction == "droite":
x = tete.xcor()
tete.setx(x + 20)
Gestion de la croissance du corps
Lorsque le serpent mange la nourriture, un nouveau segment est ajouté au corps. La logique de déplacement du corps consiste à faire suivre chaque segment par le précédent.
def verifier_nourriture():
global score
if tete.distance(nourriture) < 20:
# Placer la nourriture aléatoirement sur une grille de 20 pixels
x_nourriture = 20 * random.randint(-14, 14)
y_nourriture = 20 * random.randint(-14, 14)
nourriture.goto(x_nourriture, y_nourriture)
# Ajouter un segment au corps
nouveau_segment = turtle.Turtle()
nouveau_segment.speed(0)
nouveau_segment.shape("square")
nouveau_segment.color("jaune", "vert")
nouveau_segment.penup()
if len(corps) == 0:
nouveau_segment.goto(tete.xcor(), tete.ycor())
else:
nouveau_segment.goto(corps[-1].xcor(), corps[-1].ycor())
corps.append(nouveau_segment)
score += 1
affichage_score.clear()
affichage_score.write(str(score), align="center", font=("Arial", 24, "normal"))
def deplacer_corps():
for i in range(len(corps) - 1, 0, -1):
corps[i].goto(corps[i-1].xcor(), corps[i-1].ycor())
if len(corps) > 0:
corps[0].goto(tete.xcor(), tete.ycor())
Détection des collisions
Le jeu détecte les collisions avec les limites de la fenêtre, le corps du serpent et les obstacles. Pour les limites, le serpent traverse l'écran en réapparaissant de l'autre côté.
def collision():
# Traversée des limites
if tete.xcor() > 280:
tete.setx(-280)
elif tete.xcor() < -280:
tete.setx(280)
if tete.ycor() > 280:
tete.sety(-280)
elif tete.ycor() < -280:
tete.sety(280)
# Collision avec le corps
for segment in corps:
if tete.distance(segment) < 20:
return True
# Collision avec les obstacles (si implémentés)
for obstacle in liste_obstacles:
if tete.distance(obstacle) < 20:
return True
return False
Gestion des états du jeu et obstacles
Pour ajouter des fonctionnalités avancées comme des menus et des cartes, une machine à états est utilisée. Les obstacles sont créés dynamiquement en fonction de la carte choisie.
# États du jeu
MENU = 0
JEU_EN_COURS = 1
JEU_TERMINE = 2
etat_jeu = MENU
liste_obstacles = []
def creer_obstacle(x, y):
obstacle = turtle.Turtle()
obstacle.speed(0)
obstacle.shape("square")
obstacle.color("gris")
obstacle.penup()
obstacle.goto(x, y)
liste_obstacles.append(obstacle)
def creer_carte(choix_carte):
for obs in liste_obstacles:
obs.clear()
obs.hideturtle()
liste_obstacles.clear()
if choix_carte == 1:
for x in range(-280, 280, 20):
if abs(x) > 20:
creer_obstacle(x, 0)
elif choix_carte == 2:
for x in range(-280, 140, 20):
creer_obstacle(x, 40)
for y in range(80, 280, 20):
creer_obstacle(120, y)
Boucle principale du jeu
La boucle principale utilise ontimer pour exécuter les mises à jour à intervalles réguliers, permettant un contrôle fluide du déroulement du jeu.
def demarrer_jeu(choix_carte):
global etat_jeu
creer_carte(choix_carte)
tete.showturtle()
nourriture.showturtle()
etat_jeu = JEU_EN_COURS
def partie():
global etat_jeu
if etat_jeu == JEU_EN_COURS:
deplacer_corps()
mouvement()
if collision():
etat_jeu = JEU_TERMINE
tete.hideturtle()
nourriture.hideturtle()
for segment in corps:
segment.hideturtle()
else:
verifier_nourriture()
fenetre.update()
fenetre.ontimer(partie, 50)
# Liaison des touches
fenetre.listen()
fenetre.onkeypress(deplacer_haut, "Up")
fenetre.onkeypress(deplacer_bas, "Down")
fenetre.onkeypress(deplacer_gauche, "Left")
fenetre.onkeypress(deplacer_droite, "Right")
fenetre.onkeypress(lambda: demarrer_jeu(1), "1")
fenetre.onkeypress(lambda: demarrer_jeu(2), "2")
partie()
fenetre.mainloop()
Problèmes rencontrés et solutions
Pendant le développement, plusieurs défis ont été surmontés, notamment :
- Mouvement du corps : L'initialisation correcte de la logique de suivi a résolu le problème où les segments se superposaient.
- Génération de nourriture : L'utilisation d'une grille de 20 pixels a assuré que la nourriture s'aligne avec le mouvement du serpent.
- Gestion des collisions : La traversée des limites a été ajustée pour éviter les blocages aux bords de l'écran.
- Performence : L'optimisation du rafraîchissement avec
tracer(0)a amélioré la fluidité du jeu.