pixiJS 和 socket.io 如何协同工作? (一般问题)

How pixiJS and socket.io can work togheter? (General question)

我是从pixiJS入手,跟着书:《学习Pixi.js 为游戏和 Web 创建出色的交互式图形”- Rex van der Spuy .我了解以下基本游戏的一般结构是如何工作的,

let state = play;

function gameLoop() {
  requestAnimationFrame(gameLoop);
  state();
  renderer.render(stage);
}
function play() {
//Do something
}

我不知道如何修改以上内容以包括客户端-服务器通信。 为此,我想使用 soket.io 库。我想通过与用户的交互来改变游戏的状态。用户按下一个按钮,向服务器发出一个请求,服务器接收它并发送一些数据作为响应,客户端接收该数据并且游戏继续。但是socket.io通信是一个异步过程。如果我等待在 gameLoop 中接收数据,那么在数据从服务器到达之前,每当我想使用它时,游戏肯定会崩溃。我应该如何与 gameLoop 一起实现套接字通信,以便使用来自服务器的数据正确更新游戏状态? 谢谢,我很感激任何有用的评论。

我在这里看到两个问题:

第一个问题——“游戏逻辑循环”和“动画循环”的分离:

在您粘贴到问题中的代码片段中,您的游戏逻辑(“state()”调用)与渲染逻辑/动画相关联。这样游戏逻辑更新(状态)需要等待渲染,渲染需要等待游戏逻辑更新。另外,例如:“游戏逻辑更新”非常繁重,应该每 0.2 秒调用一次 - 不如每秒发生约 60 次(60 FPS)的渲染那么频繁?

有 2 个独立的循环可以帮助解决这个问题 - 我们称它们为:“游戏逻辑循环”和“动画循环”。

function gameLogicLoop() 
  updateState();
  setTimeout(function () { gameLogicLoop(); },  200); // ms
}

function animationLoop() {
  prepareStageFromState();
  requestAnimationFrame(gameLoop);
  renderer.render(stage);
}

// start both loops:
gameLogicLoop();
animationLoop();

这样 animationLoop() 每秒被调用约 60 次,gameLogicLoop() 每秒被调用 5 次(每 0.2 秒)。

当您使用这种方法时,您应该遵守以下规则:

  • gameLogicLoop() 中发生的任何事情都不应修改渲染/动画/图形。例如,您可以在这里:
    • 更改玩家的金币数量
    • 改变玩家在游戏地图中的位置(例如:“从方块 D3 移动到方块 D4”——而不是屏幕上的“像素 x/y”)
    • 在地图等处创建新怪物
  • 无论 animationLoop() 中发生什么,都不应该修改游戏逻辑。例如,您可以在这里:
    • 在屏幕上绘制金币计数器(您从游戏状态数据中获取金币数量)
    • 动画玩家从图块 D3 到图块 D4 的移动(您可以从游戏状态数据的图块计算像素 x/y)
    • 在地图上显示新怪物(可能有一些动画)。

作为示例,请检查这个简单的脚本:https://jsfiddle.net/urvdhw8b/

  • 注意 gameLogicLoop 函数 - 处理游戏状态数据的更新(每 1 秒)。
  • 注意 animationLoopprepareStageFromState 函数 - 处理渲染(~60 FPS)。
  • 此外,游戏状态数据被修改为:
    // Click on circle should increase number of lumberjack huts:
    circle.on('click', function(event) {
        lumberjackHutCount++;
    });

单击圆圈时增加伐木工人小屋数量的“单击”事件发生在两个循环的“外部”- 因为是独立的异步事件(鼠标单击)。我们也可以将其归类为游戏逻辑。

第二个问题-client/server沟通:

The user presses a button, a request is fired to the server, the server receives it and sends some data in response, the client receives that data and the game continues. But socket.io communication is an asynchronous process.

您可以使用事件(类似于上面的 circle.on('click'...)和 gameLogicLoop() 来处理此问题。 animationLoop() 不应处理任何 client/server 通信或调用“socket.io”等中的任何内容 - 它应该只是根据游戏状态数据在屏幕上呈现内容。

我们可以为上面的“lumberjack”脚本创建以下事件:

// on client side:
socket.on("lumberjack_huts_updated", (data) => {
  lumberjackHutCount = data.newLumberjackHutCount;
});


// on server side:
hutCount++;
...
socket.emit("lumberjack_huts_updated", { newLumberjackHutCount: hutCount });

这样,当客户端从服务器收到“lumberjack_huts_updated”消息时,它会更新游戏状态数据(lumberjackHutCount 变量)。这将异步发生 - 独立于 animationLoop().

当然,这只是一个简单的例子,随着你的进步,你肯定会找到新的方法来改进和优化它。例如:在客户端中,您可以考虑对来自服务器的传入消息进行排队(将它们存储在某个数组中),然后在 gameLogicLoop().

中处理它们

这里有几个 tutorials/docs/projects 与此主题和“多人游戏”相关 - 这是利用 client/server 通信的游戏最流行的用例: