Javascript 多人游戏 - 用于游戏/物理逻辑的 requestAnimationFrame?

问题描述 投票:0回答:2

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

我确实遇到了一个问题,即我的播放器在 120Hz 屏幕上移动得比在 60Hz 屏幕上更快。我通过将玩家的移动速度乘以“requestAnimationFrame”的增量时间来解决此问题。

但是;将动画与逻辑分离会是更好的解决方案吗?

如:

//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 显示器来说;您的键盘输入只有四帧中的一帧会被处理,所以感觉可能不那么流畅?

我问的原因是,这将是一场竞争性的游戏,因此应该保持平衡。但我检查了各种来源,似乎一致认为使用 requestAnimationFrame (甚至是逻辑;不仅仅是绘图),尽管我对此表示怀疑,并且想知道哪个是正确的(和/或在专业中使用)竞技游戏)。

javascript game-physics real-time-multiplayer
2个回答
3
投票

requestAnimationFrame
(rAF) 只会以最大 60 fps 的速率发射。 (例外,某些浏览器的某些旧版本有关闭 VSync 的标志,这确实影响了 rAF)

注意

  • rAf
    setTimeout
    setInterval
    不可靠,可能会因多种原因丢帧。

  • 如果选项卡或窗口隐藏或(选项卡)未聚焦,rAF 将停止触发。

  • 如果选项卡或窗口隐藏或(选项卡)未聚焦,
  • setInterval
    setTimeout
    将受到限制。

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

    这意味着...

    • 如果根据 rAF 时间参数计算,帧之间的时间将会浮动,并且不能可靠地用作增量时间。
    • 时间参数代表调用的时间,而不是呈现在显示器上的帧的时间,并且可以达到 < 1/60 of a second earlier than the rendered content is presented to the display hardware.

本地网络

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

大型网络

通信取决于距离,即使在完美的点对点网络上,如果客户端位于世界的另一端,由于光传输时间,最佳 ping 可能为 130 毫秒(接近 8 帧 @60Hz)。这不包括数据包交换和实际路由长度。一般来说,到地球上最远点的 ping 时间约为 300 毫秒。

专业游戏必须考虑这种延迟,这就是为什么许多多人游戏提供本地服务器,并限制某些服务器上的玩家是否远离。 300 毫秒为距离较近的玩家提供了显着的优势。

使用浏览器 API 托管客户端的实时多人游戏最多也会出现问题,因为浏览器是不可靠且不一致的客户端主机,主要是由于客户端运行的设备范围广泛。

顺便说一句

window
是默认值
this
(
globalThis
),因此不是必需的。例如
window.requestAnimationFrame
requestAnimationFrame

相同

0
投票

试试这个

class Timeline {
  constructor(fps = 60) {
    this.fps = fps;
    this.timeDuration = 1000 / this.fps;
    this.interval = this.timeDuration;
    this.lastTime = performance.now();
  }

  request(update, frame) {
    const currentTime = performance.now();
    const deltatime = currentTime - this.lastTime;
    this.interval += deltatime;
    this.lastTime = currentTime;
    if (this.interval >= this.timeDuration) {
      while (this.interval >= this.timeDuration) {
        frame(deltatime / 1000);
        this.interval -= this.timeDuration;
      }
    }

    requestAnimationFrame(update);
  }
}

const timeline = new Timeline(90);

function update() {
  timeline.request(update, deltatime => {
    console.log('frames');
  });
}

requestAnimationFrame(update);
© www.soinside.com 2019 - 2024. All rights reserved.