JavaScript 多人游戏 - 使用 requestAnimationFrame 实现游戏/物理逻辑?

7

我正在开发一个基于JavaScript的多人游戏。目前我已经准备好了基本的服务器、客户端和网络。

之前我遇到一个问题,我的玩家在120Hz屏幕上移动速度比在60Hz屏幕上快。我通过将玩家运动速度乘以“requestAnimationFrame”的deltatime来解决了这个问题。

然而,将动画与逻辑分离是不是更好的解决方案呢?

例如:

//Game loop; Do each 1/60th of a second
setInterval(gameTick, 1000/60);
function gameTick(){
  player.handleKeys();
  enemies.foreach( (enemy) => {
    enemy.update();
  });
}

//Draw loop; match to player screen refresh rate
window.requestAnimationFrame(gameLoop);
function gameLoop() {
    player.draw();
    enemies.foreach( (enemy) => {
    enemy.draw();
  });
  window.requestAnimationFrame(gameLoop);
}

尽管差异可能很微小,但我希望避免使用240Hz的玩家在服务器上“刷屏”并获得比其他玩家更大的优势。
另一方面,对于240Hz显示器,只有4帧中的1帧会处理您的键盘输入,因此可能感觉不够流畅?
我之所以问这个问题是因为这将是一款竞技游戏,因此应该保持平衡。但是,我已经查看了各种来源,并且共识似乎是使用requestAnimationFrame(即使用于逻辑;不仅仅是绘图),尽管我对此表示怀疑,并想知道哪个是正确的(和/或在专业的竞技游戏中使用)。

requestAnimationFrame试图在任何机器上达到60fps,但如果无法达到呢?如果在慢速机器上只能获得20fps,那么你可能会遇到相同的问题。不过这是一个有趣的问题! - somethinghere
在我的设备上 @somethinghere 尝试并成功地达到了144Hz,因为这是屏幕的本机刷新率。我现在考虑使用间隔来进行物理/游戏逻辑,并使用请求动画帧(带有增量时间)进行绘制循环。通过这种方式,波动(或不同屏幕)仅影响动画,而不影响游戏逻辑(处理输入并发送到服务器的频率)。在144Hz下,如果正确完成,则动画将更加流畅;但不会更快,因为它们包含delta时间。那么,如果绘制掉帧<60hz,则setinterval是否仍应打勾60hz? - Paul
setInterval 再次强调,对于实际计时并不是那么可靠。由于它共享线程(你的 requestAnimationFrame 也是如此),如果计算机不能在单个循环中完成您想要的所有操作,则仍可能会减慢速度。我的 猜测 是尝试创建一个 Web Worker,您的主线程不断更新它,并在单独的线程中具有自己的 - 非常轻量级的 - 时间间隔,以更可预测的方式与用户和服务器交换数据。但这只是一个猜测,我没有直接构建它的经验。 - somethinghere
我倾向于认为我们正在过度思考,但使用Web Workers的有趣观点可能使我能够分离绘图和物理负载。 - Paul
1个回答

3

requestAnimationFrame (rAF)最快只会以60fps的速率触发。(但一些早期版本的某些浏览器有关掉垂直同步的标志,这实际上影响了requestAnimationFrame)

注意

  • rAfsetTimeoutsetInterval都不是可靠的,而且由于许多原因可能会丢帧。

  • 如果选项卡或窗口被隐藏或(选项卡)没有焦点,则rAF将停止触发。

  • 如果选项卡或窗口被隐藏或(选项卡)没有焦点,则setIntervalsetTimeout将被节流。

  • rAF的时间参数并不代表(下一帧显示的)帧的时间,而是rAF回调被调用的时间。

    也就是说...

    • 如果从rAF时间参数计算帧之间的时间差,它将浮动并且不能可靠地用作增量时间。
    • 时间参数代表调用的时间,而不是呈现给显示硬件的时间,并且可以比呈现的内容早<1/60秒。

本地网络

对于多人游戏,服务器应该提供时间,以便所有玩家都在一个共同且可靠的时间戳上运行。客户端可以从上一个时间戳将时间投影向前,以避免冻结,如果网络延迟数据包,但在接收到数据包时应回到同步(服务器时间)时间。

大型网络

通信是与距离相关的,即使在最完美的点对点网络上,如果客户端位于世界的另一端,最佳ping时间也会因光线传播时间而为130ms(近8帧@60Hz)。这不包括数据包交换和实际路线长度。通常,到全球最遥远点的ping时间约为300ms。

必须考虑这种延迟对于专业游戏来说是必要的,这就是为什么许多多人游戏提供本地服务器,并限制玩家无法连接到某些距离过远的服务器。300毫秒会给更近的玩家带来明显的优势。

使用浏览器API托管客户端的实时多人游戏最多也只是不太可靠和不一致,主要原因在于客户端运行的设备范围极广。

顺便说一下

window是默认的this(globalThis),因此不需要加。例如:window.requestAnimationFramerequestAnimationFrame是相同的。


有趣的观点,不过我必须补充一下,在我的笔记本电脑上,rAF 运行在内置的 144Hz 显示器上(我添加了一个 FPS 计数器,它是 1/delta)。这和 setInterval 在隐藏/失去焦点时仍然会执行“某些操作”的组合,使其更适用于网络。不过我仍然会在绘图方面使用 rAF。我已经添加了 FPS 和 Tick rate 调试值来监视两者。 - Paul
关于ping的观点很好,我也得为此做一个调试,考虑到“滞后者”不会有不公平的优势(并且踢出太高的ping)。我同意它可能不是最好的客户端,但主要是因为客户端非常不可信,客户端代码始终可见于浏览器,并且可以使用调试工具注入命令。我确实想过让客户端只显示和发送键盘命令,但那样你就需要在客户端进行一些伪造来使其看起来更流畅。 - Paul

网页内容由stack overflow 提供, 点击上面的
可以查看英文原文,
原文链接