在PyGame中实现一个WebSocket服务器,通过HTML WebSocket客户端控制对象

vqlkdk9b  于 12个月前  发布在  其他
关注(0)|答案(1)|浏览(180)

总体思路

我成功地配置了一个树莓派作为接入点,这样我就可以通过WIFI与我的移动的手机或笔记本电脑连接。现在,我想在连接到屏幕的raspberry pi上运行PyGame,并通过连接到raspberry pi的移动的手机或笔记本电脑控制游戏中的对象。
在下文中,我首先提供简单的工作示例,然后展示哪些不起作用。

测试WebSocket Server成功

为了向客户端提供一些内容,我在raspberry pi上安装了一个nginx服务器。当我用raspberry pi的IP地址(192.168.4.1)打开浏览器时,会出现索引页面。
然后,为了测试WebSocket服务器,我编写了一个基于websockets package的简单python脚本:

import websockets
import asyncio

# handler processes the message and sends "Success" back to the client
async def handler(websocket, path):
    async for message in websocket:
        await processMsg(message)
        await websocket.send("Success")

async def processMsg(message):
    print(f"[Received]: {message}")

async def main():
    async with websockets.serve(handler, "192.168.4.1", 6677):
        await asyncio.Future() # run forever

if __name__ == "__main__":
    asyncio.run(main())

我通过设置一个HTML页面来测试服务器,该页面通过JavaScript文件连接到WebSocket服务器,并实现了一个按钮来向服务器发送字符串:

<html>
  <head>
    <meta charset="UTF-8">
    <meta name="viewport" content="width=device-width, initial-scale=1.0">
    <title>WebSocket Test</title>
    <script type="text/javascript" src="static/client.js"></script>
  </head>
  <body>
    <div>
      <h2>WebSocket Test</h2>
      <input type="button" name="send" value="Send Hello!" onClick="sendHello()">
    </div>
  </body>
</html>

client.js文件:

let socket = new WebSocket("ws://192.168.4.1:6677/");

socket.onopen = function(e) {
  console.log("[open] Connection established");
  console.log("[send] Sending to server");
  socket.send("Web connection established")
};

socket.onmessage = function(event) {
  console.log(`[message] Data received from server: ${event.data}`);
};

socket.onclose = function(event) {
  if (event.wasClean) {
    console.log(`[close] Connection closed cleanly, code=${event.code} reason=${event.reason}`);
  } else {
    console.log('[close] Connection died!')
  }
};

socket.onerror = function(error) {
  console.log(`[error] ${error.message}`);
};

function sendHello() {
  console.log("[send] Sending to server");
  socket.send("Hello!");
};

通过这些简单的示例文件,我可以成功地在服务器和客户端之间建立持久连接并交换数据。

PyGame添加WebSocket Server失败

为了用PyGame测试WebSocket服务器,我设置了一个简单的游戏,只显示一个蓝色的圆圈:

# import and init pygame library
import pygame
pygame.init()

# screen dimensions
HEIGHT = 320
WIDTH = 480

# set up the drawing window
screen = pygame.display.set_mode([WIDTH,HEIGHT])

# run until the user asks to quit
running = True
while running:
    # did the user close the window
    for event in pygame.event.get():
        if event.type == pygame.QUIT:
            running = False

    # fill the background with white
    screen.fill((255,255,255))

    # draw a solid blue circle in the center
    pygame.draw.circle(screen, (0,0,255), (int(WIDTH/2),int(HEIGHT/2)), 30)

    # flip the display
    pygame.display.flip()

pygame.quit()

游戏正在正常运行,在白色背景上显示一个蓝色圆圈。
问题是,当我想将WebSocket服务器添加到游戏中时,其中一个正在阻止另一部分的执行,即,我只能在没有活动服务器的情况下运行游戏,或者我可以运行WebSocket服务器,但游戏不显示。例如,将WebSocket服务器放在游戏循环前面,如下所示

# WS server
async def echo(websocket, path):
    async for message in websocket:
        msg = message
        print(f"[Received] {message}")
        await websocket.send(msg)

async def server():
    async with websockets.serve(echo, "192.168.4.1", 6677):
        await asyncio.Future()

asyncio.ensure_future(server())

或者包括服务器内部的游戏循环:

async def server():
    async with websockets.serve(echo, "192.168.4.1", 6677):
        #await asyncio.Future()

        # set up the drawing window
        screen = pygame.display.set_mode([WIDTH,HEIGHT])

        # run until the user asks to quit
        running = True
        while running:
            #pygame code goes here

通过广泛的Google搜索,我发现PyGame和Penccio包(websockets基于Penccio)不能简单地一起工作,因为我必须以某种方式手动处理Penccio循环和游戏循环。
我希望有人能帮助我解决这个问题...

zc0qhyus

zc0qhyus1#

感谢我的一位同事,我想出了让Pygame运行的解决方案,包括一个Websockets服务器和响应移动的客户端上的按钮。重要的一点是在不同的线程中运行Websockets服务器并正确处理事件。
下面是server.py的代码。我改变了IP地址到本地主机(127.0.0.1),以测试我的笔记本电脑上的代码.

import websockets
import asyncio
import pygame

IPADDRESS = "127.0.0.1"
PORT = 6677

EVENTTYPE = pygame.event.custom_type()

# handler processes the message and sends "Success" back to the client
async def handler(websocket, path):
    async for message in websocket:
        await processMsg(message)
        await websocket.send("Success")

async def processMsg(message):
    print(f"[Received]: {message}")
    pygame.fastevent.post(pygame.event.Event(EVENTTYPE, message=message))

async def main(future):
    async with websockets.serve(handler, IPADDRESS, PORT):
        await future  # run forever

if __name__ == "__main__":
    asyncio.run(main())

这里重要的部分是触发Pygame事件的方法pygame.fastevent.post
然后,在game.py中,服务器在不同的线程中启动:

# import and init pygame library
import threading
import asyncio
import pygame
import server

def start_server(loop, future):
    loop.run_until_complete(server.main(future))

def stop_server(loop, future):
    loop.call_soon_threadsafe(future.set_result, None)

loop = asyncio.get_event_loop()
future = loop.create_future()
thread = threading.Thread(target=start_server, args=(loop, future))
thread.start()

pygame.init()
pygame.fastevent.init()

# screen dimensions
HEIGHT = 320
WIDTH = 480

# set up the drawing window
screen = pygame.display.set_mode([WIDTH, HEIGHT])

color = pygame.Color('blue')
radius = 30
x = int(WIDTH/2)

# run until the user asks to quit
running = True
while running:
    # did the user close the window
    for event in pygame.fastevent.get():
        if event.type == pygame.QUIT:
            running = False
        elif event.type == server.EVENTTYPE:
            print(event.message)
            color = pygame.Color('red')
            x = (x + radius / 3) % (WIDTH - radius * 2) + radius

    # fill the background with white
    screen.fill((255,255,255))

    # draw a solid blue circle in the center
    pygame.draw.circle(screen, color, (x, int(HEIGHT/2)), radius)

    # flip the display
    pygame.display.flip()

print("Stoping event loop")
stop_server(loop, future)
print("Waiting for termination")
thread.join()
print("Shutdown pygame")
pygame.quit()

当你想退出Pygame时,请注意正确关闭运行Websockets服务器的线程!
现在,当你使用包含我在问题中发布的client.js脚本的HTML文件时,蓝色的圆圈应该在你每次按下“Send hello”按钮时变成红色并向右移动。
我希望这能帮助每个试图使用Pygame设置在线多人游戏的人。

相关问题