【问题标题】:framerate and monitor refresh rate causes lag帧率和显示器刷新率导致延迟
【发布时间】:2021-08-30 14:24:53
【问题描述】:

我一直在对 html5 和 canvas 进行游戏开发的一些测试编码,并遇到了一个我无法通过的错误。当我使用 requestAnimationFrame 运行基本动画循环时,对象的速度得到了更新,运动变得流畅,然后我在一台具有 144Hz 刷新率(从 60Hz 到 144Hz)的显示器上运行了脚本),而我的梦想刚刚坠入深渊。

所以我开始阅读 delta time 以及它如何解决游戏中的 fps 问题,它确实有效,但并不完全符合预期。

function update(timestamp = Date.now()){
    if(!previous) previous = timestamp;
    dt = (timestamp - previous) / 1000;
    fps = 1000 / (timestamp - previous);
    previous = timestamp;
...
   window.requestAnimationFrame(update);
}
window.requestAnimationFrame(update);

我用requestAnimationFrame调用update,但也可以不用(timestamp = Date.now()),得到正确的信息fps => 143.78... dt = 0.006978...

    this.vx = 2;

...

    this.x += this.vx * dT * fps;
    this.y += this.vy * dT * fps;
    this.vy += gravity;
...

计算检查 144 和 60 Hz 显示器(2 * 0.0069 * 144 = 2.001 和 2 * 0.016 * 60 = 2.001),但在 60Hz 显示器上运行时延迟太大。 2px 的移动并不像应有的那样平滑;这让我想到了我的问题,这个问题有解决办法吗?

【问题讨论】:

    标签: javascript canvas frame-rate timedelta requestanimationframe


    【解决方案1】:

    如果您已经有一个为 60Hz 编写的工作代码并且您希望将其修复为在任何帧速率下工作,那么最简单的方法是将您当前的绝对值转换为以 px per ms 表示的速度值(或秒,没关系)。

    例如在你的情况下,你会这样做

    const expectedFrameRate = 60;
    obj.vx = 2 * (expectedFrameRate / 1000); // px per ms
    

    将所有这些值转换为速度后,只需将其乘以自上一帧以来经过的时间:

    function animate(timestamp) {
      const dt = (timestamp - previous);
      previous = timestamp;
      
      update(dt);
      requestAnimationFrame(animate);
    }
    function update(dt) {
      // ...
      obj.x += obj.vx * dt; // no matter how it long it took
                            // to render the last frame
                            // it will be on the correct position
      // ...
    

    这里有一个小演示,展示了“基于速度”的动画如何无论计时器的精度如何都能保持正确的位置:

    const canvases = document.querySelectorAll("canvas");
    canvases.forEach( (c) => c.height = 60);
    const expectedFrameRate = 60;
    
    class Obj {
      constructor(canvas, color) {
        this.canvas = canvas;
        this.ctx = canvas.getContext("2d");
        this.color = color;
        this.y = 0;
        this.x = 0;
        this.width = 60;
        this.height = 60;
        this.vx = 2;
        this.xPerMs = 2 * (expectedFrameRate / 1000);
      }
      draw() {
        const { ctx, color, x, y, width, height } = this;
        ctx.clearRect(0, 0, ctx.canvas.width, ctx.canvas.height);
        ctx.fillStyle = color;
        ctx.fillRect(x, y, width, height);
      }
    };
    
    // red is simple x += vx
    // completely dependent on the framerate
    // will be late if switching tab or if the browser can't keep up @60FPS
    {
      const canvas = canvases[0];
      const obj = new Obj(canvas, "red");
      const anim = (timestamp) => {
        obj.x = (obj.x + obj.vx) % canvas.width;
        obj.draw();
        requestAnimationFrame(anim);
      };
      requestAnimationFrame(anim);
    }
    // green uses speed, inside rAF callback
    // smooth and correct
    {
      const canvas = canvases[1];
      const obj = new Obj(canvas, "green");
      let previous = document?.timeline?.currentTime || performance.now();
      const anim = (timestamp) => {
        const dt = timestamp - previous;
        previous = timestamp;
        obj.x = (obj.x + obj.xPerMs * dt) % canvas.width;
        obj.draw();
        requestAnimationFrame(anim);
      };
      requestAnimationFrame(anim);
    }
    // blue uses speed, inside random timeout callback
    // expect hiccups, but "correct" overall position
    {
      const canvas = canvases[2];
      const obj = new Obj(canvas, "blue");
      let previous = performance.now();
      const anim = () => {
        const timestamp = performance.now();
        const dt = timestamp - previous;
        previous = timestamp;
        obj.x = (obj.x + obj.xPerMs * dt) % canvas.width;
        obj.draw();
        setTimeout(anim, Math.random() * 100);
      };
      setTimeout(anim, Math.random() * 100);
    }
    canvas { border: 1px solid; display: block }
    <canvas></canvas>
    <canvas></canvas>
    <canvas></canvas>

    【讨论】:

    • 谢谢,它确实澄清了一些关于如何以恒定 fps 进行编码的事情。我只需要努力实现与速度相加的物理变量相同的系统。
    猜你喜欢
    • 2022-09-01
    • 2017-02-14
    • 1970-01-01
    • 2011-12-18
    • 1970-01-01
    • 1970-01-01
    • 2020-08-16
    • 2016-07-10
    • 1970-01-01
    相关资源
    最近更新 更多