javascript - 通过查找平均 FPS 提高 Canvas 游戏性能

标签 javascript html performance canvas

看一下我游戏的移动功能:

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 或其他值才能使游戏顺利运行(这意味着每个玩家的“移动”将等于之前的移动,否则他们会遇到滞后)。所以我在想,要么在游戏第一次“加载”时 secret 运行游戏并找出平均 FPS,要么保持某种学习曲线以调整该函数运行的次数。 不管怎样,我真的不知道如何完美地实现它,我知道这可能很难,但请我需要一些帮助。

最佳答案

帧速率

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

处理帧速率的最佳方法是首先留出一些稳定时间。监视帧速率,当您看到连续许多帧丢失时,请等待。一旦获得一致的速率,请使用最小速率作为基本速率,限制帧渲染,如第二个片段所示。

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

关于javascript - 通过查找平均 FPS 提高 Canvas 游戏性能,我们在Stack Overflow上找到一个类似的问题: https://stackoverflow.com/questions/41130520/

相关文章:

javascript - 三个js键盘旋转

javascript - 从一个巨大的数组中获取数据

sql - 无需加入即可获取员工及其经理的详细信息

r - Armadillo 中的新 `find_finite` 函数比循环慢 3.5 倍?

php - 使用 codeigniter 删除数据库记录

python - 在 pandas 数据框中使用正则表达式匹配组的性能

javascript - 更改颜色 collapsibleset Jquery Mobile

javascript - 如何将图表数据点与数组中的图表数据点进行匹配

具有 float div 和合并列的 CSS3 列数

javascript - 选择一组三个 li 并将它们显示在下一个或上一个按钮上