Partage
  • Partager sur Facebook
  • Partager sur Twitter

[Pygame][Socket] Le jeu du snake

... mode multijoueur

Sujet résolu
    22 novembre 2010 à 12:41:20

    Bonjour tout le monde,


    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 :p .


    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 ?



    Merci pour votre future aide,
    Realmagma.

    EDIT: Erreur corrigé dans le schéma.
    • Partager sur Facebook
    • Partager sur Twitter
      23 novembre 2010 à 19:06:32

      Re-bonjour,

      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.
      • Partager sur Facebook
      • Partager sur Twitter
        23 novembre 2010 à 19:17:29

        en réseau locale c'est pas très grave, sinon c'est un jeu rapide ... et je crois que vitesse et synchro ne sont pas très copains ...
        mais je réfléchis ^^
        • Partager sur Facebook
        • Partager sur Twitter

        Python c'est bon, mangez-en. 

          23 novembre 2010 à 19:25:27

          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. :)

          • Partager sur Facebook
          • Partager sur Twitter
            23 novembre 2010 à 19:52:32

            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.

            Image utilisateur

            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.
            • Partager sur Facebook
            • Partager sur Twitter

            Python c'est bon, mangez-en. 

              29 novembre 2010 à 16:14:30

              c'en est où?
              • Partager sur Facebook
              • Partager sur Twitter

              Python c'est bon, mangez-en. 

              [Pygame][Socket] Le jeu du snake

              × 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