Javascript - 动画循环和帧率

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

我关注了一篇关于在动画循环中充分利用增量时间的文章。 这是我读过的处理循环渲染和更新的最先进的方法。 https://isaacsukin.com/news/2015/01/detailed-explanation-javascript-game-loops-and-timing

我在一个类中实现了它并且它有效。 但是,有几点我想了解:

  1. 我使用的是刷新率为 60hz 的 iMac Pro。但是,当我将“maxFPS”设置为 60(或以下任何值)时,我的帧速率会在 5 秒内从 60 FPS 下降到 32fps。所以,我需要增加“maxFPS”,当我将它设置为 70 以上时,我只能得到稳定的 60fps,任何值。为什么会这样?

  2. 无论我输入什么“maxFPS”,如果我将“if (timestamp > this.lastFpsUpdate + 1000)”语句更改为略低于 1000(甚至 950)的值,我的帧率将再次急剧下降到 32fps 左右。那个,我也不明白。有人可以解释它发生的原因吗?

  3. 无论我使用类还是直接在我的工作文件中实现循环,这些问题都会发生。尽管如此,我还是想知道:我将循环过程封装在一个类中的方式在性能方面通常是一个问题还是可以的?我这样做是为了让我的工作文件保持干净,一旦设置好,我就不需要处理与循环引擎相关的所有代码。

PS:我的画布是 3840 x 2160,但是,我上面提到的 2 个问题在该分辨率或 960x540 下完全相同。分辨率似乎根本不会影响问题(无论分辨率如何,我都获得相同的帧速率和(缺乏)性能)。

对于非常具体的参数值,fps 下降似乎很突然,感觉像是代码问题而不是性能问题。事实上,我不明白为什么 FPS 显示刷新率从 1000 毫秒变为 950 毫秒会突然如此显着地降低 FPS。而且我不明白为什么将 maxFPS 设置为 60 在 60hz 屏幕上会出现问题,而将其设置为 70 甚至 1500 就可以了。无论我是否实际渲染某些东西,或者我的绘制/更新功能是否完全为空,这些掉落都会持续发生。

这是我的循环类文件:

class Loop{

    //--------------------------------------------------------------------

    constructor(maxFPS,displayFPS){

        this.fps              = 60;
        this.maxFPS           = maxFPS; //if lower than 70, the fps is dropping fast 
        this.displayFPS       = displayFPS;
        this.timestep         = 1000/this.fps;
        this.delta            = 0;
        this.lastFrameTimeMs  = 0;
        this.framesThisSecond = 0;
        this.lastFpsUpdate    = 0;

    }

    //--------------------------------------------------------------------

    mainLoop(timestamp) {
    
        if (timestamp < this.lastFrameTimeMs + (1000 / this.maxFPS)) {

            requestAnimationFrame(this.mainLoop.bind(this));
            return;

        }

        this.delta          += timestamp - this.lastFrameTimeMs;
        this.lastFrameTimeMs = timestamp;

        //any value lower than 1000ms is dropping the FPS here...
        if (timestamp > this.lastFpsUpdate + 1000) {

            this.fps              = 0.25 * this.framesThisSecond + 0.75 * this.fps;
            // same issue if I just put:
            //this.fps            = this.framesThisSecond;
            this.lastFpsUpdate    = timestamp;
            this.framesThisSecond = 0;

        }

        this.framesThisSecond++;

        var numUpdateSteps = 0;

        while (this.delta >= this.timestep) {
        
            // the update function of my working file
            update(this.timestep);

            this.delta -= this.timestep;

            if (++numUpdateSteps >= 240) {

                this.delta = 0;
                break;

            }

        }

        if(this.displayFPS == true){

            document.getElementById('frameRate').textContent = (Math.round(this.fps * 100) / 100).toFixed(2);

        }

        context.clearRect(0, 0, canvas.width, canvas.height);

        // the draw function of my working file
        draw(this.delta/this.timestep);

        requestAnimationFrame(this.mainLoop.bind(this));

    }

    //--------------------------------------------------------------------

    start(){

        // the setup function of my working file
        setup();
    
       requestAnimationFrame(this.mainLoop.bind(this));

    }

    //--------------------------------------------------------------------

}

这是我的工作文件:

//--------------------------------------------------------------------
// Initialize
//--------------------------------------------------------------------

const canvas = document.querySelector('canvas')
      canvas.width  = 3840; // tested: no impact on my issues 
      canvas.height = 2160; // tested: no impact on my issues 
const context       = canvas.getContext('2d');

const loop = new Loop(maxFPS=70, display=true);
      loop.start();

//--------------------------------------------------------------------
// Variables
//--------------------------------------------------------------------

var boxSize;
var boxPos;
var lastBoxPos; 
var boxVelocity;
var limit;

//--------------------------------------------------------------------
// Animation Test
//--------------------------------------------------------------------

function setup(){

    boxSize     = 200;
    boxPosX     = 10;
    boxPosY     = 10;
    lastBoxPosX = 10;
    boxVelocity = 1;
    limit       = canvas.width - boxSize;

}

//--------------------------------------------------------------------

function update(delta){
    
    boxLastPosX  = boxPosX;
    boxPosX     += boxVelocity * delta;
    if (boxPosX >= limit || boxPosX <= 0) {boxVelocity = -boxVelocity;}

}
//--------------------------------------------------------------------

function draw(interpolation){
    
    context.beginPath();
    context.fillStyle = "#3e4856";
    context.fillRect((boxLastPosX + (boxPosX - boxLastPosX) * interpolation), boxPosY, boxSize, boxSize);
  
}

//--------------------------------------------------------------------

感谢您的意见。

javascript frame-rate timedelta requestanimationframe
© www.soinside.com 2019 - 2024. All rights reserved.