【问题标题】:Improving Canvas Game Performance by Finding average FPS通过查找平均 FPS 提高 Canvas 游戏性能
【发布时间】:2017-04-29 01:41:00
【问题描述】:

看看我的游戏的运动功能:

https://jsfiddle.net/qk7ayx7n/92/

var canvas = document.getElementById("canvas");
var ctx=canvas.getContext("2d");
height = window.innerHeight;
canvas.width = window.innerWidth; 
canvas.height = height; 

playerY = height / 2;
playerX = 50;
var circle = new Image();
circle.src = "https://s21.postimg.org/4zigxdh7r/circ.png";

ctx.drawImage(circle, playerX, playerY);

move();

function move(){

velocity = height * 0.00050; // the velocity should be proptional to screen height.
startPos = playerY,
endPos = 0,
distanceY = endPos - playerY,  
startTime = Date.now(),
duration = Math.abs(distanceY) / velocity;
requestAnimationFrame(startMoving);

function startMoving(){
    var elapsedTime = Math.min(Date.now() - startTime, duration),
    cbEnd = false;
    if (elapsedTime < duration){ //time's up
        setTimeout(function() {
        requestAnimationFrame(startMoving);
        }, 1000 / 60);
    }
    else cbEnd = true;
    playerY = startPos - (elapsedTime * velocity);
    console.log(playerY);
    ctx.clearRect(0, 0, window.innerWidth, height);
    ctx.drawImage(circle, playerX, playerY); //drawing circle
    if(cbEnd) console.log("over");
    }
}

现在,我将解释:我的实际游戏是玩家的速度随着游戏的进行而变化的游戏。速度与窗口的高度成正比,因为玩家的目标是向上移动。 我使用this technique 是为了更好地控制我执行的玩家抽签次数。我们稍后会解决这个问题。

我还使用this technique(见答案)来测量时间,以防最后一帧有延迟,它只会覆盖它应该覆盖的距离,这也意味着目标 Y在所有设备上始终准时到达。

FPS 平均值是我遇到的最大问题。不同的设备有不同的规格。为了打造适合所有设备的好游戏,我必须掌握这个功能:

setTimeout(function() {
var fps = 60;
requestAnimationFrame(startMoving);

}, 1000 / fps);

我已经在许多设备上测试了我的游戏,在某些设备上,fps 需要为 40、50、30 或其他一些设备才能使游戏顺利运行(这意味着每个玩家的“移动”将与之前的相同,否则他们会遇到滞后)。所以我在想,要么在游戏第一次“加载”时偷偷运行游戏并找出平均 FPS,要么保持某种学习曲线以调整此功能的运行次数。 无论哪种方式,我都不知道如何完美地实现它,我知道这可能很难,但我需要一些帮助。

【问题讨论】:

  • requestAnimationFrame 的回调以高精度返回时间,以毫秒为单位 1/1,000,000 秒 1/1,000(最小增量 0.001 毫秒)将其用于您的计时数据。例如reaquestAnimatFrame(myLoop); function myLoop(timeInMS){ Appart from that 我不确定你在问什么?
  • 如何根据通常发生的平均时间(通过测量以前的调用)知道调用 requestAnimationFrame 的确切时间,因此调整 setTimeout 以仅在安全时触发说不会有滞后。我基本上想实现彼此相等的动作,没有突然的“尖峰”。
  • requestAnimationFrame (rAF) 与显示刷新同步。只要您的动画渲染时间低于 1/60 秒,它就会以 60fps 的速度触发。但是,如果您超过 1/60 秒,则 rAF 将等到下一次显示刷新,帧速率为 30fps。在那之后20fps。如果您想保持帧速率,您可以忽略早期的 rAF 调用并在不渲染的情况下退出,仅在所需速率的帧上渲染。
  • 我的建议是将你的动画绑定到时间而不是 FPS,那么 FPS 就无关紧要了(而不是运动/帧,考虑运动/时间等)。这样,画布就成为了场景中的“相机”和对象,而与画布显示/更新场景的频率无关。

标签: javascript html performance canvas


【解决方案1】:

帧速率

要使用 requestAnimationFrame (rAF) 控制帧速率,您需要监控每次调用 rAF 回调的时间。这可以通过使用 rAF 传递给您的函数的第一个参数来完成。

示例获取 60 帧的平均帧速率。

function log(data){
    var div = document.createElement("div");
    div.textContent = data;
    document.body.appendChild(div);
    scroll(0,10000);
}
var samples = [];
function myLoop(time){
    if(samples.length < 60){
        samples.push(time);
        requestAnimationFrame(myLoop) 
    }else{
        log("First 60 callback times in ms.");
        samples.forEach(s=>log(s.toFixed(3)));
        log("Mean frame time : "+ ((samples[59]-samples[0])/60).toFixed(3))
        log("Mean frame rate : "+ (1000/((samples[59]-samples[0])/60)).toFixed(0))
    }
}
requestAnimationFrame(myLoop) 

此示例在采样帧速率之前等待 4 秒(大约)。

function log(data){
    var div = document.createElement("div");
    div.textContent = data;
    document.body.appendChild(div);
    scroll(0,10000);
}
log("4 second wait for stable");
var waitFor = 240; // wait for browser to be nice.
var samples = [];
function myLoop(time){
    if(waitFor > 0){
        waitFor -= 1;
        requestAnimationFrame(myLoop) 
        return;
    }
    if(samples.length < 60){
        samples.push(time);
        requestAnimationFrame(myLoop) 
    }else{
        log("First 60 callback times in ms.");
        samples.forEach(s=>log(s.toFixed(3)));
        log("Mean frame time : "+ ((samples[59]-samples[0])/60).toFixed(3))
        log("Mean frame rate : "+ (1000/((samples[59]-samples[0])/60)).toFixed(0))
    }
}
requestAnimationFrame(myLoop) 

要控制帧速率,您需要忽略进入的早期帧。由于帧时间并不完美,但因为您知道帧的速度不会超过 1/60,所以有点混乱,所以很容易处理与。

const FRAME_RATE = 30;
// set min frame time at frame rate sub 1/4 frame
const FRAME_MIN_TIME = (1000/FRAME_RATE) - (1000/(60*4));
var lastFrameTime = 0;    

function log(data){
    var div = document.createElement("div");
    div.textContent = data;
    document.body.appendChild(div);
    scroll(0,10000);
}
var samples = [];
function myLoop(time){
    // if the frame is early call the next frame and dont render
    if(time - lastFrameTime < FRAME_MIN_TIME){            
        requestAnimationFrame(myLoop);
        return;
    }
    lastFrameTime = time;

    if(samples.length < 60){
        samples.push(time);
        requestAnimationFrame(myLoop) 
    }else{
        log("First 60 callback times in ms.");
        samples.forEach(s=>log(s.toFixed(3)));
        log("Mean frame time : "+ ((samples[59]-samples[0])/60).toFixed(3))
        log("Mean frame rate : "+ (1000/((samples[59]-samples[0])/60)).toFixed(0))
    }
}
requestAnimationFrame(myLoop) 

好数字?

使用 javascript 很难获得良好的帧速率估计,尤其是在启动时,因为有很多事情会导致丢帧。根据页面上发生的情况,您可能需要几秒钟或更长时间才能可靠地估计游戏的负载。

处理帧速率的最佳方法是首先允许一些稳定时间。监控帧速率,当您看到连续丢掉许多帧时,请稍候。一旦你有一个一致的速率,使用最小速率作为基本速率,节流帧渲染如第二个 sn-p 所示。

您只能在 rate = 60/n 处具有一致的帧速率,其中 n 是一个 >= 1 的整数。帧速率为 60、30、20、15、12、10、8.87... 等等。

【讨论】:

  • 如何从帧速率(必须最低)的数字中了解我可以使用哪些数字来避免 fps 下降?
  • @RunningFromShia 最后一个 sn-p 的帧速率是 1000 / (time - lastFrameTime) 。这次只是一个估计,所以将它四舍五入到最接近的帧率(答案的最后一段)。就我个人而言,我在所有设备上以 60 或 30 的速度运行我的游戏。如果它运行缓慢,那么我使代码变得复杂,需要找到更多优化方法。我为打算使用该产品的基本(最低限度)系统编写代码,而不是相反。
【解决方案2】:

canvas.getContext("2d") - 很慢。

您需要使用 webgl。 要使用 webgl,请使用 Pixi.js。不错的图书馆。

在这个DEMOwithout source)上掠夺

【讨论】:

  • 这很好,但我没有看到更好的结果;在较弱的设备上,即使在我的笔记本电脑上,您的示例也会出现 FPS 下降(左上角有平均帧数),就像我的游戏一样。
  • 您可以设置-2x分辨率,然后较弱的设备会更好地工作。并在上面的演示中打开抗锯齿。没有抗锯齿:57.playcode.io 分辨率为 0.5:58.playcode.io 或两种方法:59.playcode.io 是的,较弱的手机不适合网页游戏...
猜你喜欢
  • 1970-01-01
  • 1970-01-01
  • 1970-01-01
  • 1970-01-01
  • 2022-11-21
  • 2017-12-04
  • 1970-01-01
  • 1970-01-01
  • 2012-12-13
相关资源
最近更新 更多