Comment vous le savez déjà, j'ai réalisé un Snake en 2D grâce à la librairie Pygame ; parfaite pour ces types de jeux.
Mon code-source me permettait de jouer tout seul, sans score et c'est pourquoi je me suis lancé dans la réalisation du jeu du Snake en multijoueur.
Ne sachant pas comment m'y prendre, j'ai dû commencé par un mode deux joueurs qui fonctionne, avec la gestion des scores et du temps. Le problème c'est que c'est un mode qui se joue sur un même ordinateur .
Ce que j'aimerai réaliser
Un snake multijoueur en réseau local !
Les outils
Pygame
Sockets (Vu que Pygame ne possède pas de module réseau)
Les contraintes
Je n'ai jamais fait de réseau mis à part le mini-tchat de Gérard-Swinnen.
Voici comment je compte procéder: Côté-Client (Admettons qu'il y ait 2 joueurs)
0: Connexion au serveur
1: On instancie les objets à partir du fichier texte
2: Envoie de la variable self.m de chaque client au serveur
3: Le client_1 récupère la variable self.m du client_2 et le client_2 récupère la variable self.m du client_1
4: Mis à jour de la carte (Méthode:voirConditionVariableM & voirConditionVariableE)
5: Draw-Blit-Fill
Côté serveur:
0: Mise en route du serveur. Attend des connexions
1: Réceptionne la variable self.m de chaque client
2: Renvoie la variable self.m du client_1 au client_2 et la variable self.m du client_2 au client_1
Mes Questions
Est-ce que ma méthode est viable ?
Que pourrais-je améliorer ?
Quels autres informations le serveur doit il connaître ?
j'ai finalement réussi à implémenter un snake deux joueurs en réseau local, mais un problème persiste.
En effet, en regardant l'écran de mon ordinateur et celui de mon frère, les deux snakes ne sont pas synchronisés.
Comment cela se fait il ?
Auriez vous une solution à suggérer ?
Merci d'avance, voici mes deux codes-sources:
Côté serveur:
#Partie serveur du mini-chat
#Utilise les Thread pour gérer les connexionx en parallèle
import socket, sys, threading
HOST = socket.gethostname()
PORT = 50000
print(HOST)
class ThreadClient(threading.Thread):
'''dérivation d'un objet thread pour gérer la connexion avec un client'''
def __init__(self, conn):
threading.Thread.__init__(self)
self.connexion = conn
def run(self):
# Dialogue avec le client :
nom = self.getName() # Chaque thread possède un nom
while 1:
msgClient = self.connexion.recv(1024).decode("Utf8")
if not msgClient or msgClient.upper() =="FIN":
break
#message = "{0}-> {1}".format(nom, msgClient)
message = "{0}".format(msgClient)
print(message)
# Faire suivre le message à tous les autres clients :
for cle in conn_client:
if cle != nom: # ne pas le renvoyer à l'émetteur
conn_client[cle].send(message.encode("Utf8"))
# Fermeture de la connexion :
self.connexion.close() # couper la connexion côté serveur
del conn_client[nom] # supprimer son entrée dans le dictionnaire
print("Client {0} déconnecté.".format(nom))
# Le thread se termine ici
# Initialisation du serveur - Mise en place du socket :
mySocket = socket.socket(socket.AF_INET, socket.SOCK_STREAM)
try:
mySocket.bind((HOST, PORT))
except socket.error:
print("La liaison du socket à l'adresse choisie a échoué.")
sys.exit()
print("Serveur prêt, en attente de requêtes ...")
mySocket.listen(3)
# Attente et prise en charge des connexions demandées par les clients :
conn_client = {} # dictionnaire des connexions clients
while 1:
connexion, adresse = mySocket.accept()
# Créer un nouvel objet thread pour gérer la connexion :
th = ThreadClient(connexion)
th.start()
# Mémoriser la connexion dans le dictionnaire :
it = th.getName() # identifiant du thread
conn_client[it] = connexion
print("Client {0} connecté, adresse IP {1}, port {2}.".format\
(it, adresse[0], adresse[1]))
# Dialogue avec le client :
#msg = "Vous êtes connecté. Envoyez vos messages."
msg = ""
connexion.send(msg.encode("Utf8"))
Côté client:
from pygame.locals import *
import pygame, os, socket, sys, threading
host = socket.gethostname()
port = 50000
class Gerer():
def __init__(self, conn):
self.connexion = conn
#Instance
self.jeu = Jeu()
self.th_E = ThreadEmission(self.connexion, self.jeu)
self.th_R = ThreadReception(self.connexion, self.jeu)
self.th_E.start()
self.finConnexionR = self.th_R.start()
self.jeu.mainloop()
if self.finConnexionR == False:
self.th_E._stop()
print("Client arrêté. Connexion interrompue.")
self.connexion.close()
class ThreadReception(threading.Thread):
"""objet thread gérant la réception des messages"""
def __init__(self, conn, jeu):
threading.Thread.__init__(self)
self.connexion = conn # réf. du socket de connexion
self.jeu = jeu
def run(self):
while 1:
self.jeu.horloge2.tick(4)
message_recu = self.connexion.recv(1024).decode("Utf8")
self.jeu.e = message_recu ##La
if message_recu != "B" or message_recu != "H" or \
message_recu != "G" or message_recu != "D":
print("* " + message_recu + " *")
if not message_recu or message_recu.upper() == "FIN":
break
return False
# Le thread <réception> se termine ici.
# On force la fermeture du thread <émission> :
#th_E._stop()
#print("Client arrêté. Connexion interrompue.")
#self.connexion.close()
class ThreadEmission(threading.Thread):
"""objet thread gérant l'émission des messages"""
def __init__(self, conn, jeu):
threading.Thread.__init__(self)
self.connexion = conn # réf. du socket de connexion
self.jeu = jeu #Encapsulation
def run(self):
while 1:
self.jeu.horloge2.tick(4)
#self.jeu.contenu #Envoir de la map au serveur
if self.jeu.m == "D": message_emis = "D"
elif self.jeu.m == "G": message_emis = "G"
elif self.jeu.m == "B": message_emis = "B"
elif self.jeu.m == "H": message_emis = "H"
else: message_emis = input("->")
self.connexion.send(message_emis.encode("Utf8"))
#*******************************************************#
#------- Partie jeu -------#
#*******************************************************#
#-*-*- <Les Classes> || Début de <Jeu> -*-*-#
class Jeu(pygame.sprite.Sprite):
"""Classe <Jeu>"""
def __init__(self, width = 400, height = 400):
"""Constructeur de la classe:
- Créer une fenêtre pygame
- Initialise les variables
"""
pygame.sprite.Sprite.__init__(self)
pygame.init()
#Variables. vitesse du serpent = 20
self.horloge = pygame.time.Clock()
self.horloge2 = pygame.time.Clock()
self.width = width
self.height = height
self.vitesse = 20
self.m = "D"
self.e = "D"
#Screen + titre
self.screen = pygame.display.set_mode((self.width, self.height))
pygame.display.set_caption("MySnake")
self.perso = Perso()
self.mur = Mur()
self.bloc = Bloc()
self.persoE = Perso() ##La
self.blocE = Bloc() ##La
#Répértoire
self.path = os.getcwd() + "\\lv_snake\\"
#*******************************************************#
#-------Méthode pricipale: Gestion des évènements-------#
#*******************************************************#
def mainloop(self):
"""On entre dans la boucle principale de <Jeu>
- Gestion des événements clavier
- Gestion des collisions
- Gestion des Blitages
"""
self.creeGroupe()
self.chargementNiveau()
continuer = True
#Le curseur sera non-visible
pygame.mouse.set_visible(0)
while continuer:
self.horloge.tick(4)
for event in pygame.event.get():
if event.type == pygame.QUIT:
self.m = None
self.e = None
pygame.quit()
break
elif event.type == pygame.KEYDOWN:
if event.key == pygame.K_UP: self.m = "H"
elif event.key == pygame.K_DOWN: self.m = "B"
elif event.key == pygame.K_RIGHT: self.m = "D"
elif event.key == pygame.K_LEFT: self.m = "G"
elif event.key == pygame.K_l:
self.m = None
self.e = None
pygame.quit()
break
#Ici on bouge le snake en fonction de /self.m/
if not pygame.sprite.spritecollide(self.perso, self.g_mur, False) and not \
pygame.sprite.spritecollide(self.perso, self.g_bloc, False):
ancienRect = self.perso.rect.topleft
ancienRectE = self.persoE.rect.topleft ##La
self.conditionDeplacementVoirVariableM(ancienRect)
self.conditionDeplacementVoirVariableE(ancienRectE) ##La
else:
#Si collision on QUIT
self.drawBlitFill()
self.m = None
self.e = None
pygame.quit()
break
self.drawBlitFill()
def afficherBackground(self, image):
"""Sert uniquement à afficher
les backgrounds dans le main
Evite de créer une autre classe.
"""
self.screen.blit(image, (0,0))
pygame.display.flip()
def creeGroupe(self):
"""On crée un groupe avec les
instances self.perso/pomme/mur.
"""
self.g_perso = pygame.sprite.Group()
self.g_mur = pygame.sprite.Group()
self.g_bloc = pygame.sprite.Group(self.bloc)
self.g_persoE = pygame.sprite.Group() ##La
self.blocE.image = self.blocE.blocV ##La
self.g_blocE = pygame.sprite.Group(self.blocE) ##La
def drawBlitFill(self):
"""Dessine chaque groupe
Blit & Flip & Fill.
"""
self.g_perso.draw(self.screen)
self.g_mur.draw(self.screen)
self.g_bloc.draw(self.screen)
self.g_persoE.draw(self.screen) ##La
self.g_blocE.draw(self.screen) ##La
pygame.display.flip()
self.screen.fill((0, 0, 0))
def conditionDeplacementVoirVariableM(self, ancienRect):
"""Condition de déplacement après
la boucle d'évènements.
"""
if self.m == "H":
self.perso.rect.move_ip(0, -self.vitesse)
#S'il dépasse l'écran, il réaparaît à l'opposé
if self.perso.rect.topleft[1] < 0:
self.perso.rect.topleft = (self.perso.rect[0], self.perso.rect[1]+self.height)
elif self.m == "B":
self.perso.rect.move_ip(0, self.vitesse)
#(-20 pour réaparaitre sur le bord)
if self.perso.rect.topleft[1] > self.height:
self.perso.rect.topleft = (self.perso.rect[0], self.perso.rect[1]-self.height-20)
elif self.m == "D":
self.perso.rect.move_ip(self.vitesse , 0)
#(-20 pour réaparaitre sur le bord)
if self.perso.rect.topleft[0] > self.width:
self.perso.rect.topleft = (self.perso.rect[0]-self.width-20, self.perso.rect[1])
elif self.m == "G":
self.perso.rect.move_ip(-self.vitesse, 0)
if self.perso.rect.topleft[0] < 0:
self.perso.rect.topleft = (self.perso.rect[0]+self.width, self.perso.rect[1])
#Echange les coordonnées de chaque bloc par celui qui le précède
for i in self.g_bloc: i.rect.topleft, ancienRect = ancienRect, i.rect.topleft
def conditionDeplacementVoirVariableE(self, ancienRectE):
"""Condition de déplacement du serpent enemie
après la boucle d'évènements.
"""
if self.e == "H":
self.persoE.rect.move_ip(0, -self.vitesse)
#S'il dépasse l'écran, il réaparaît à l'opposé
if self.persoE.rect.topleft[1] < 0:
self.persoE.rect.topleft = (self.persoE.rect[0], self.persoE.rect[1]+self.height)
elif self.e == "B":
self.persoE.rect.move_ip(0, self.vitesse)
#(-20 pour réaparaitre sur le bord)
if self.persoE.rect.topleft[1] > self.height:
self.persoE.rect.topleft = (self.persoE.rect[0], self.persoE.rect[1]-self.height-20)
elif self.e == "D":
self.persoE.rect.move_ip(self.vitesse , 0)
#(-20 pour réaparaitre sur le bord)
if self.persoE.rect.topleft[0] > self.width:
self.persoE.rect.topleft = (self.persoE.rect[0]-self.width-20, self.persoE.rect[1])
elif self.e == "G":
self.persoE.rect.move_ip(-self.vitesse, 0)
if self.persoE.rect.topleft[0] < 0:
self.persoE.rect.topleft = (self.persoE.rect[0]+self.width, self.persoE.rect[1])
#Echange les coordonnées de chaque bloc par celui qui le précède
for i in self.g_blocE: i.rect.topleft, ancienRectE = ancienRectE, i.rect.topleft
#*******************************************************#
#-------Méthodes sur la manipulation des fichiers-------#
#*******************************************************#
def chargementNiveau(self):
"""Initialise le terrain de jeu
en fonction des fichiers textes
"""
with open(self.path + "snake_essai.txt", "r") as monFichier:
self.contenu = monFichier.read()
monFichier.close()
compteur = 0
for i in range(int(self.width/20)):
for j in range(int(self.height/20)):
#1 = Mur
if self.contenu[compteur] == "1":
self.mur = Mur()
self.mur.rect.topleft = (j*20, i*20)
self.g_mur.add(self.mur)
#@ = Joueur
elif self.contenu[compteur] == "@":
self.perso = Perso()
self.perso.rect.topleft = (j*20, i*20)
self.g_perso.add(self.perso)
#Bloc
elif self.contenu[compteur] == "b":
self.bloc = Bloc()
self.bloc.rect.topleft = (j*20, i*20)
self.g_bloc.add(self.bloc)
#* = JoueurE ##La
elif self.contenu[compteur] == "*":
self.persoE = Perso()
self.persoE.rect.topleft = (j*20, i*20)
self.persoE.image = self.persoE.persoV
self.g_persoE.add(self.persoE)
#Bloc ##La
elif self.contenu[compteur] == "c":
self.blocE = Bloc()
self.blocE.image = self.blocE.blocV
self.blocE.rect.topleft = (j*20, i*20)
self.g_blocE.add(self.blocE)
compteur+=1
#-*-*- <Les Classes> || Début de <Perso> -*-*-#
class Perso(pygame.sprite.Sprite):
"""Classe <Perso>"""
def __init__(self):
"""Constructeur de la classe <Perso>
- Initialise les images
- Enlève leur contour blanc
- Donne une surface rectangle
"""
pygame.sprite.Sprite.__init__(self)
#Chargement des images
self.persoV = pygame.image.load("image\\snake_v.PNG")
self.persoO = pygame.image.load("image\\snake_o.PNG")
#Gestion du contour blanc
self.persoV.set_colorkey((255, 255, 255))
self.persoO.set_colorkey((255, 255, 255))
#Il faut une imageActuelle car il y a 4 têtes diférentes
self.image = self.persoO
self.rect = self.image.get_rect()
#-*-*- <Les Classes> || Début de <Bloc> -*-*-#
class Bloc(pygame.sprite.Sprite):
"""Classe <Bloc>"""
def __init__(self):
"""Constructeur de la classe <Bloc>
- Initialise les images
- Enlève leur contour blanc
- Donne une surface rectangle
"""
pygame.sprite.Sprite.__init__(self)
#Chargement des images
self.blocV = pygame.image.load("image\\snake_v.PNG")
self.blocO = pygame.image.load("image\\snake_o.PNG")
#Gestion contour blanc
self.blocO.set_colorkey((255, 255, 255))
self.blocV.set_colorkey((255, 255, 255))
#ImageActuelle
self.image = self.blocO
self.rect = self.image.get_rect()
self.rect.topleft = (-60,-60) #Hors écran
#-*-*- <Les Classes> || Début de <Mur> -*-*-#
class Mur(pygame.sprite.Sprite):
"""Classe <Mur>"""
def __init__(self):
"""Constructeur de la classe <Mur>
- Initialise les images
- Enlève leur contour blanc
- Donne une surface rectangle
"""
pygame.sprite.Sprite.__init__(self)
self.image = pygame.image.load("image\\blocBleu2.PNG")
self.rect = self.image.get_rect()
self.image.set_colorkey((255, 255, 255))
if __name__ == "__main__":
#Établissement de la connexion :
connexion = socket.socket(socket.AF_INET, socket.SOCK_STREAM)
try:
connexion.connect((host, port))
except socket.error:
print("La connexion a échoué.")
sys.exit()
print("Connexion établie avec le serveur.")
# Dialogue avec le serveur : on lance deux threads pour gérer
# indépendamment l'émission et la réception des messages :
a = Gerer(connexion)
Je vous remercie d'avance,
bonne soirée.
Realmagma.
P.S: Les commentaires du style "##La" sont là uniquement pour moi, n'y prêtez pas attention.
Le problème c'est qu'ils sont décalés de pas mal de cases, cela varie beaucoup, dé-fois de 10 cases, ou 5 cases (horizontal et vertical).
C'est très embêtent.
Je cherche aussi de mon côté, mais si tu as la réponse, j'aimerai bien que tu m'expliques.
ben, le serveur peut attendre que tous les clients aient transmis avant de redistribuer l'information, tous les clients seront calés sur le client le plus lent ...
j'avais fait un serveur qui lançait un thread par client, chaque thread imprimait l'info dans une liste; le serveur redistribuait la liste, donc les clients roulaient à la vitesse du serveur et non plus à la vitesse du client le plus lent.
Bon, ça correspond pas à ce que tu veux en fait.
Pour être synchro, faut pas threader. Le client envoie, et attend une réception de façon bloquante.
× 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.
Python c'est bon, mangez-en.