【问题标题】:Adding multiplayer functionality to singleplayer game with sockets使用套接字向单人游戏添加多人游戏功能
【发布时间】:2025-12-27 04:40:07
【问题描述】:

我对 python 和一般编程比较陌生,想知道如何将多人游戏功能添加到我用 pygame 制作的单人游戏中。我已经制作了一个接受多个连接的功能性服务器和一个客户端,以制作一个可用于发送消息的系统。我尝试制作一个运行游戏并将各种游戏数据(例如x位置)发送到服务器的客户端,同时还从其他客户端接收此类数据,但无济于事。 这是我的消息传递客户端代码:

import socket
import threading

class Client:
    sock = socket.socket(socket.AF_INET, socket.SOCK_STREAM)

    def sendMSG(self):
        while True:
           self.sock.send(bytes(input(''), 'utf-8'))

    def __init__(self):
        port = 12345
        self.sock.connect(('127.0.0.1', port))

        iThread = threading.Thread(target=self.sendMSG)
        iThread.daemon = True
        iThread.start()

        while True:
           data = self.sock.recv(1024)
           if not data:
              break
           print(str(data, 'utf-8'))


client = Client()

它完美地发挥了它的作用,但我无法将它与我的单人游戏代码结合起来。我尝试更改 sendMSG 函数的功能并将其视为游戏循环。当按下方向键并且值发生更改但尝试接收该数据时遇到问题时,我能够到达客户端发送位置数据的地步。我使用了线程,但由于某种原因,它不能同时运行游戏和接收数据。也许我的思维过程是错误的?如果您能帮我弄清楚如何将我的游戏循环添加到客户端,我将不胜感激。谢谢。

【问题讨论】:

  • 您错误地使用了套接字。 TCP 是一种流协议。你必须在它之上构建一些协议,至少是一个简单的基于行的协议。
  • @Daniel 那么我应该选择 UDP 吗?我不能制作一个在游戏运行时不断将游戏数据流式传输到所有客户端的系统吗?如果不使用套接字,我将如何实现我的目标?我一直在寻找制作多人游戏的方法,但大多数谷歌搜索结果都显示了简单的聊天应用程序或基于文本的游戏,我尝试将其逻辑转移到我的案例中但没有成功。感谢您的回答。
  • 你试过python-socketio吗?我的理解是,它可以帮助你从这些挑战中抽象出来。编辑:作为 aiohttp 中间件也来自线程问题

标签: python sockets multiplayer


【解决方案1】:

您的客户端代码存在一些问题,难以集成到据我所知有自己的事件循环的框架中(即我从未使用过的 pygame):

self.sock.send(bytes(input(''), 'utf-8'))

阻塞input,从而阻塞游戏事件循环。您试图通过将其放入自己的线程来避免这种情况,但现在您已将键盘完全移至另一个线程。还有一点就是

data = self.sock.recv(1024)

还有块。现在你刚刚在input 上解除阻塞的线程被socket.recv 阻塞了。

您使用 UDP 的想法确实很有希望。您现在可以使用两个(几乎)非阻塞方法 send_message()check_message() 构建游戏客户端类:

import socket
import json

class GameClient:
    def __init__(self, server=("127.0.0.1", 12000)):
        self.client_socket = socket.socket(socket.AF_INET, socket.SOCK_DGRAM)
        self.client_socket.settimeout(0.1)
        self.server = server
    def send_message(self, msg):
        """ JSON-encode and send msg to server. """
        message = json.dumps(msg).encode("utf8")
        self.client_socket.sendto(message, self.server)
    def check_message(self):
        """ Check if there's a message from the server and return it, or None. """
        try:
            message, address = self.client_socket.recvfrom(1024)
            return json.loads(message.decode("utf8"))
        except socket.timeout:
            pass  # this passes as "non-blocking" for now
        return None

由于我不了解 pygame,我创建了自己的原始游戏事件循环,它处理我的位置 (my_x, my_y) 和其他玩家的 (positions) 并在一次进入时将我的玩家移动到随机方向一段时间,将玩家的位置作为消息发送和接收,形式为 `{"playername": [xpos, ypos]}':

import random
import time

def game(player):
    me = GameClient()
    positions = {}
    my_x = my_y = 0
    me.send_message({player: [my_x, my_y]})
    while True:
        move = random.randrange(80)
        if move == 0:
            my_x -= 1
        elif move == 1:
            my_x += 1
        elif move == 2:
            my_y -= 1
        elif move == 3:
            my_y += 1
        if move < 4:
            me.send_message({player: [my_x, my_y]})
            print("me: {}".format([my_x, my_y]))
        updates = me.check_message()
        while updates:
            positions.update(updates)
            print("updates: {}".format(updates))
            print("positions: {}".format(positions))
            updates = me.check_message()
        time.sleep(0.1)

服务器本身只是将它收到的每条消息回显给它所听说过的所有其他客户端:

import socket
import json

def game_server():
    positions = {}
    server_socket = socket.socket(socket.AF_INET, socket.SOCK_DGRAM)
    server_socket.bind(('', 12000))
    while True:
        message, address = server_socket.recvfrom(1024)
        print("update from {}".format(address))
        positions[address] = json.loads(message.decode("utf8"))
        for client in positions.keys():
            if client != address:
                server_socket.sendto(message, client)
        print("game positions: {}".format(positions))

您必须以某种方式将game() 函数的想法融入到 pygame 事件循环中。祝你好运。

【讨论】: