【问题标题】:How pixiJS and socket.io can work togheter? (General question)pixiJS 和 socket.io 如何一起工作? (一般问题)
【发布时间】:2021-04-21 13:18:44
【问题描述】:

我从 pixiJS 开始,遵循这本书:“Learn 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 一起实现套接字通信,以便使用来自服务器的数据正确更新游戏的状态? 谢谢,感谢任何有用的 cmets。

【问题讨论】:

    标签: socket.io pixi.js


    【解决方案1】:

    我在这里看到两个问题:

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

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

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

    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/

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

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

    第二个问题——客户端/服务器通信:

    用户按下按钮,向服务器发出请求,服务器接收它并发送一些数据作为响应,客户端接收该数据,游戏继续。但是socket.io通信是一个异步过程。

    您可以使用事件(类似于上面的circle.on('click'...)和gameLogicLoop() 来处理这个问题。 animationLoop() 不应处理任何客户端/服务器通信或调用“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() 中处理它们。

    这里有一些与此主题和“多人游戏”相关的教程/文档/项目 - 这是利用客户端/服务器通信的游戏最流行的用例:

    【讨论】:

    • domis86,非常感谢您,非常感谢您的宝贵时间。您在详细的回答中给了我很多思考和学习的东西,这是一个真正的教训,而不是我会说的简单回答。再次感谢。
    • 我很高兴能帮上忙 :)
    猜你喜欢
    • 1970-01-01
    • 1970-01-01
    • 1970-01-01
    • 2015-12-29
    • 1970-01-01
    • 1970-01-01
    • 1970-01-01
    • 2011-07-06
    • 1970-01-01
    相关资源
    最近更新 更多