


Construire quelque chose qui n'est pas une application Todo : un jeu de serpent multijoueur en ligne
Je suis récemment tombé sur une citation de l'influenceur tech "the primeagen". Je ne m'en souviens pas exactement mais si je me souviens bien, ça ressemble à ceci :
"Si vous n'échouez pas dans ce que vous faites, alors vous n'apprenez pas."
Cela m'a fait réfléchir à mon parcours de codage. Je suis devenu assez à l'aise avec la construction de backends, au point même d'écrire import express à partir de « express » ; est devenu une corvée.
Au lieu de passer par l'événement canon consistant à apprendre encore un autre framework JavaScript afin de créer ma millionième application de tâches révolutionnaire (car clairement, le monde en a besoin de plus), j'ai décidé de faire autre chose. J'avais lu des articles sur le protocole WebSocket et j'ai trouvé fascinante sa capacité à gérer des messages asynchrones bidirectionnels entre le serveur et le client. Je voulais construire quelque chose avec, mais j'avais besoin d'une pause avec JavaScript.
Après réflexion, j'ai opté pour un simple jeu multijoueur 2D. Cela impliquerait des calculs (détection de collision), des structures de données (listes chaînées, hashmaps) et la synchronisation des joueurs. Un jeu de serpent semblait parfait, avec quelques règles simples :
Manger un fruit fait grandir et ajoute 1 à votre score
S'écraser sur le corps d'un autre joueur vous fait rétrécir, réinitialise votre position de manière aléatoire et remet à zéro votre score
Les collisions face-à-face font rétrécir les deux joueurs, réinitialisent leurs positions et mettent leurs scores à zéro
Tous ces calculs sont effectués côté serveur pour empêcher les joueurs de falsifier la logique du jeu. Nous utiliserons Python 3 avec Pygame pour les graphiques et la bibliothèque websockets pour gérer les messages asynchrones via asyncio.
Maintenant, plongeons-nous dans le code, vous risquez de le trouver compliqué, car rappelez-vous la première règle de programmation :
"Si ça marche, n'y touchez pas."
Vous avez assez lu mes jappements, passons à la partie amusante : Codage. Mais si vous souhaitez éviter les discussions et plonger directement dans le vif du sujet, rendez-vous simplement sur le dépôt GitHub.
Si vous souhaitez contribuer, n'hésitez pas à ouvrir un ticket ou à soumettre une pull request. Toutes les améliorations ou corrections de bugs sont les bienvenues !
Tout d'abord, nous définissons nos structures de données :
class Object : def __init__(self , x : float , y :float , width:int , height :int): #################################### # init object's size and postion # #################################### self.x = x self.y = y self.height = height self.width = width def render(self , screen , color) : pygame.draw.rect(screen ,color ,pygame.Rect(self.x , self.y , self.width , self.height)) class Player(Object) : def __init__(self, x: float, y: float, width: int, height: int): super().__init__(x, y, width, height) self.next = None self.prev = None self.tail = self self.direction = 'LEFT' self.length = 1 self.color = 'red' # move the Snake to a certain direction # the "changed" will be a way to tell either to continue in the same direction # or change the direction of the head to the new direction # it is used in the game def change_direction(self, keys): changed = False if self.direction in ['LEFT', 'RIGHT']: if keys[pygame.K_w] and self.direction != 'DOWN': self.direction = 'UP' changed = True elif keys[pygame.K_s] and self.direction != 'UP': self.direction = 'DOWN' changed = True elif self.direction in ['UP', 'DOWN']: if keys[pygame.K_a] and self.direction != 'RIGHT': self.direction = 'LEFT' changed = True elif keys[pygame.K_d] and self.direction != 'LEFT': self.direction = 'RIGHT' changed = True return changed # move the Snake to a certain direction with a certain speed def move(self, screen, dt): speed = 150 * dt if self.direction == 'UP': self.move_all(screen, 0, -speed) elif self.direction == 'DOWN': self.move_all(screen, 0, speed) elif self.direction == 'LEFT': self.move_all(screen, -speed, 0) elif self.direction == 'RIGHT': self.move_all(screen, speed, 0) def bound(self , screen) : if self.y < 0 : self.y = screen.get_height() if self.y > screen.get_height() : self.y = 0 if self.x < 0 : self.x = screen.get_width() if self.x > screen.get_width() : self.x = 0 def get_pos(self) : arr = [] current = self while current : arr.append([current.x , current.y]) current = current.next return arr # move the snake and its body to some coordinates def move_all(self, screen, dx, dy): # Store old positions old_positions = [] current = self while current: old_positions.append((current.x, current.y)) current = current.next # Move head self.x += dx self.y += dy self.bound(screen) # self.render(screen, self.color) # Move body current = self.next i = 0 while current: current.x, current.y = old_positions[i] current.bound(screen) # current.render(screen, self.color) current = current.next i += 1 def add(self ): new = Player(self.tail.x+self.tail.width+10 , self.tail.y ,self.tail.width , self.tail.height) new.prev = self.tail self.tail.next = new self.tail = new self.length +=1 def shrink(self , x , y): self.next = None self.tail = self self.length = 1 self.x = x self.y = y # used for the and the opponent player when # receiving its coordinates def setall(self , arr) : self.x = arr[0][0] self.y = arr[0][1] self.next = None self.tail = self current = self for i in range(1 , len(arr)) : x = arr[i][0] y = arr[i][1] new = Player(x ,y ,self.width , self.height) current.next = new self.tail = new current = current.next # render the whole snake on the screen # used for both the current player and the opponent def render_all(self, screen, color): current = self if self.next : self.render(screen,'white') current = self.next while current : current.render(screen , color) current = current.next
La classe Object est une classe de base pour les objets de jeu, tandis que la classe Player l'étend avec des fonctionnalités spécifiques au serpent. La classe Player comprend des méthodes pour changer de direction, se déplacer, grandir, rétrécir et rendre le serpent.
Ensuite, nous avons la logique du jeu :
import pygame from objects import Player import websockets import asyncio import json uri = 'ws://localhost:8765' pygame.font.init() # Render the text on a transparent surface font = pygame.font.Font(None, 36) # playing the main theme (you should hear it) def play() : pygame.mixer.init() pygame.mixer.music.load('unknown.mp3') pygame.mixer.music.play() def stop(): if pygame.mixer.music.get_busy(): # Check if music is playing pygame.mixer.music.stop() # initialize players and the fruit def init(obj) : print(obj) player = Player(*obj['my'][0]) opp = Player(*obj['opp'][0] ) food = Food(*obj['food']) return (player , opp , food) async def main(): async with websockets.connect(uri) as ws: choice = int(input('1 to create a room \n2 to join a room\n>>>')) room_name = input('enter room name: ') await ws.send(json.dumps({ "choice" : choice , "room" : room_name })) ## waiting for the other player to connecet res = {} while True: res = await ws.recv() try: res = json.loads(res) break except Exception as e: pass player, opp, food = init(res) pygame.init() screen = pygame.display.set_mode((600, 400)) clock = pygame.time.Clock() running = True dt = 0 play() while running: for event in pygame.event.get(): if event.type == pygame.QUIT: running = False screen.fill("black") my_pos = { 'pos': player.get_pos(), 'len': player.length } await ws.send(json.dumps(my_pos)) response = await ws.recv() response = json.loads(response) # Update food position pygame.draw.rect(screen ,'green' ,pygame.Rect(response['food'][0] ,response['food'][1] ,20, 20)) # Handle actions if response['act'] == 'grow': player.add() elif response['act'] == 'shrinkall': player.shrink(*response['my'][0][0:2]) opp.shrink(*response['opp'][0][0:2]) elif response['act'] == 'shrink': player.shrink(*response['my'][0][0:2]) # restarting the song each time you bump into the other player stop() play() else: opp.setall(response['opp']) # Render everything once per frame player.render_all(screen, 'red') opp.render_all(screen, 'blue') ## score ## x | y => x you , y opponent text = font.render(f'{response["my_score"]} | {response["other_score"]}', True, (255, 255, 255)) screen.blit(text, (0, 0)) pygame.display.flip() keys = pygame.key.get_pressed() changed = player.change_direction(keys) # keep moving in the same direction if it is not changed if not changed: player.move(screen, dt) dt = clock.tick(60) / 1000 pygame.quit() asyncio.run(main())
cela vous connectera au serveur et vous permettra de créer et de rejoindre une salle, il enverra une mise à jour de votre position au serveur et obtiendra la position de l'adversaire et la position du fruit et un acte qui sera un ordre au joueur de soit rétrécir, soit grandir
Enfin, le code du serveur :
import asyncio import websockets import random import json def generate_food_position(min_x, max_x, min_y, max_y): x = random.randint(min_x, max_x) y = random.randint(min_y, max_y) return [x, y, 20, 20] rooms = {} def collide(a , b , width): return (abs(a[0] - b[0]) < width and abs(a[1] - b[1]) < width) # detecting possible collides : def collides(a , b , food ) : head_to_head = collide(a[0] , b[0] ,30) ; head_to_food = collide(a[0] , food ,25 ) head_to_body = False this_head = a[0] for part in b : if collide(this_head , part ,30) : head_to_body = True break return (head_to_head , head_to_body , head_to_food) # return response as (act ,opponents position(s) , my position(s) , the food and the scores) def formulate_response(id , oid , food , roomName) : this= rooms[roomName][id]['pos'] other= rooms[roomName][oid]['pos'] hh , hb , hf = collides(this ,other ,food) act = 'None' if hh : act = 'shrink' rooms[roomName][id]['pos'] = initPlayer() rooms[roomName][id]['score'] =0 # rooms[roomName][oid]['pos'] = initPlayer() # rooms[roomName][oid]['respawn'] = True elif hb : act = 'shrink' rooms[roomName][id]['pos'] = initPlayer() rooms[roomName][id]['score'] = 0 elif hf : act = 'grow' rooms[roomName]['food'] = generate_food_position(20, 580, 20, 380) rooms[roomName][id]['score']+=1 return { 'act' : act , 'opp' : rooms[roomName][oid]['pos'] , 'my' : rooms[roomName][id]['pos'] , 'food': rooms[roomName]['food'] , 'my_score' : rooms[roomName][id]['score'] , 'other_score' : rooms[roomName][oid]['score'] } def initPlayer(): return [[random.randint(30 , 600 ) , random.randint(30 , 400 ) , 30 , 30 ]] async def handler(websocket) : handshake = await websocket.recv() handshake = json.loads(handshake) roomName = handshake["room"] if handshake['choice'] == 1 : rooms[roomName] = {} rooms[roomName]['food'] =generate_food_position(30 ,570 ,30 ,370) rooms[roomName][websocket.id] = { 'socket' : websocket , 'pos' : initPlayer() , 'respawn' : False , 'score' :0 } if len(rooms[roomName]) >= 3 : await broadcast(rooms[roomName]) id = websocket.id while True : room = rooms[roomName] this_pos = await websocket.recv() ## synchrnisation issue with this ## after couple of times they collide head to head ## the cordinates shown on the screen aren't same ## as the server if room[id]['respawn']==True : rooms[roomName][id]['respawn'] = False # generate response : response = { 'act' : 'shrinkall', 'my' : room[id]['pos'] , 'opp' : get_other_pos(get_other_id(id ,room),room), 'food': room['food'] } await websocket.send(json.dumps(response)) else : # update player position this_pos = json.loads(this_pos) rooms[roomName][id]['pos'] = this_pos['pos'] rooms[roomName][id]['len'] = this_pos['len'] other_id = get_other_id(id , room) food = room['food'] response = formulate_response(id ,other_id ,food ,roomName) await websocket.send(json.dumps(response)) def get_other_id(id , room) : for thing in room.keys(): if thing != 'food' and thing != id : return thing def get_other_pos(id , room) : return room[id]['pos'] async def broadcast(room) : for thing in room.keys() : if thing!= 'food' : init = { 'my' : room[thing]['pos'] , 'opp' : room[get_opp(thing, room)]['pos'] , 'food': room['food'] } await room[thing]['socket'].send(json.dumps(init)) def get_opp(id , room) : for thing in room.keys() : if thing!= 'food' and thing != id: return thing async def main(): async with websockets.serve(handler , 'localhost' ,8765 ): await asyncio.Future() if __name__ == '__main__' : print('listenning ... ') asyncio.run(main())
il gère les mouvements, les collisions et les interactions des joueurs. Il génère de la nourriture, détecte les collisions et synchronise l'état du jeu entre les joueurs. Le serveur gère également les connexions des joueurs et envoie les mises à jour du jeu
Et voilà, un jeu de serpent multijoueur qui pourrait être la prochaine grande nouveauté dans le monde du jeu vidéo. Qui sait, il pourrait même être présent à la prochaine grande conférence technologique !
Pour l’instant, pourquoi ne pas faire un tour et voir ce que vous pouvez ajouter ? Consultez le dépôt GitHub et faites votre marque sur la prochaine grande nouveauté des jeux de serpent.
Bon codage !
Ce qui précède est le contenu détaillé de. pour plus d'informations, suivez d'autres articles connexes sur le site Web de PHP en chinois!

Outils d'IA chauds

Undresser.AI Undress
Application basée sur l'IA pour créer des photos de nu réalistes

AI Clothes Remover
Outil d'IA en ligne pour supprimer les vêtements des photos.

Undress AI Tool
Images de déshabillage gratuites

Clothoff.io
Dissolvant de vêtements AI

Video Face Swap
Échangez les visages dans n'importe quelle vidéo sans effort grâce à notre outil d'échange de visage AI entièrement gratuit !

Article chaud

Outils chauds

Bloc-notes++7.3.1
Éditeur de code facile à utiliser et gratuit

SublimeText3 version chinoise
Version chinoise, très simple à utiliser

Envoyer Studio 13.0.1
Puissant environnement de développement intégré PHP

Dreamweaver CS6
Outils de développement Web visuel

SublimeText3 version Mac
Logiciel d'édition de code au niveau de Dieu (SublimeText3)

Sujets chauds











Python est plus facile à apprendre et à utiliser, tandis que C est plus puissant mais complexe. 1. La syntaxe Python est concise et adaptée aux débutants. Le typage dynamique et la gestion automatique de la mémoire le rendent facile à utiliser, mais peuvent entraîner des erreurs d'exécution. 2.C fournit des fonctionnalités de contrôle de bas niveau et avancées, adaptées aux applications haute performance, mais a un seuil d'apprentissage élevé et nécessite une gestion manuelle de la mémoire et de la sécurité.

Est-ce suffisant pour apprendre Python pendant deux heures par jour? Cela dépend de vos objectifs et de vos méthodes d'apprentissage. 1) Élaborer un plan d'apprentissage clair, 2) Sélectionnez les ressources et méthodes d'apprentissage appropriées, 3) la pratique et l'examen et la consolidation de la pratique pratique et de l'examen et de la consolidation, et vous pouvez progressivement maîtriser les connaissances de base et les fonctions avancées de Python au cours de cette période.

Python est meilleur que C dans l'efficacité du développement, mais C est plus élevé dans les performances d'exécution. 1. La syntaxe concise de Python et les bibliothèques riches améliorent l'efficacité du développement. Les caractéristiques de type compilation et le contrôle du matériel de CC améliorent les performances d'exécution. Lorsque vous faites un choix, vous devez peser la vitesse de développement et l'efficacité de l'exécution en fonction des besoins du projet.

Python et C ont chacun leurs propres avantages, et le choix doit être basé sur les exigences du projet. 1) Python convient au développement rapide et au traitement des données en raison de sa syntaxe concise et de son typage dynamique. 2) C convient à des performances élevées et à une programmation système en raison de son typage statique et de sa gestion de la mémoire manuelle.

PythonlistSaReparmentofthestandardLibrary, tandis que les coloccules de colocède, tandis que les colocculations pour la base de la Parlementaire, des coloments de forage polyvalent, tandis que la fonctionnalité de la fonctionnalité nettement adressée.

Python excelle dans l'automatisation, les scripts et la gestion des tâches. 1) Automatisation: La sauvegarde du fichier est réalisée via des bibliothèques standard telles que le système d'exploitation et la fermeture. 2) Écriture de script: utilisez la bibliothèque PSUTIL pour surveiller les ressources système. 3) Gestion des tâches: utilisez la bibliothèque de planification pour planifier les tâches. La facilité d'utilisation de Python et la prise en charge de la bibliothèque riche en font l'outil préféré dans ces domaines.

Les applications de Python en informatique scientifique comprennent l'analyse des données, l'apprentissage automatique, la simulation numérique et la visualisation. 1.Numpy fournit des tableaux multidimensionnels et des fonctions mathématiques efficaces. 2. Scipy étend la fonctionnalité Numpy et fournit des outils d'optimisation et d'algèbre linéaire. 3. Pandas est utilisé pour le traitement et l'analyse des données. 4.Matplotlib est utilisé pour générer divers graphiques et résultats visuels.

Les applications clés de Python dans le développement Web incluent l'utilisation des cadres Django et Flask, le développement de l'API, l'analyse et la visualisation des données, l'apprentissage automatique et l'IA et l'optimisation des performances. 1. Framework Django et Flask: Django convient au développement rapide d'applications complexes, et Flask convient aux projets petits ou hautement personnalisés. 2. Développement de l'API: Utilisez Flask ou DjangorestFramework pour construire RestulAPI. 3. Analyse et visualisation des données: utilisez Python pour traiter les données et les afficher via l'interface Web. 4. Apprentissage automatique et AI: Python est utilisé pour créer des applications Web intelligentes. 5. Optimisation des performances: optimisée par la programmation, la mise en cache et le code asynchrones
