requestAnimationFrame循环不正确FPS

时间:2017-04-12 21:04:59

标签: javascript html5 canvas

我有一个javascript函数,我的游戏循环(希望)每秒60次控制输入,绘图等。

它目前编码的方式似乎总是在52左右,明显低于60 fps,即使没有其他事情发生,它甚至会下降到25-30 fps

function loop() {
    setTimeout(function () {
        requestAnimationFrame(loop);
        time += (1000 / 60);
        if (time % 600 == 0) {
            oldtick = tick;
            tick += 1;
            time = 0;
            aiMovement();
            combat();
        }
        context.clearRect(0, 0, c.width, c.height);
        drawMap();
        playerInput();
        movePlayer();
        drawEntities();
        drawPopups();
        var thisLoop = new Date;
        var fps = 1000 / (thisLoop - lastLoop);
        lastLoop = thisLoop;
        context.drawImage(cursor, mouse.x, mouse.y, 16, 16);
        context.fillStyle = "#ffff00";
        context.fillText("FPS: " + Math.floor(fps) + " Time: " + Math.floor(time) + " tick: " + tick, 10, 450);
        context.fillText("Gold: " + gold, 10, 460);

        //requestAnimationFrame(loop);
    }, 1000 / 60);
}

如果我从顶部删除setTimeout和第一个requestAnimationFrame并取消注释底部的reuqestAnimationFrame并删除其他setTimeout事物,则FPS提高到58但在58和62之间快速变化,再次,不是静态60.是否与1000/60有关系不是一个整数?如果这是真的,那么使用requestAnimationFrame的人如何达到60 fps?

2 个答案:

答案 0 :(得分:14)

不要使用setTimeout或setInterval作为动画。

问题是您正在请求动画事件中调用计时器事件。删除超时,只需使用requestAnimationFrame。

<div style="background:url('../img/test.jpg') no-repeat; background-size:cover;">
</div>

RequestAnimationFrame(rAF)始终保持同步(除非浏览器已关闭垂直同步)。下一帧将以1 / 60th,2 / 60th,3 / 60th等呈现。你不会使用rAF获得每秒52帧,而不是60fps,30fps,15fps等......

下面的演示显示了使用上的差异。

因为requestAnimationFrame使用一些智能来为动画计时,所以它们不能同时运行,所以点击画布启动它。

您还可以添加加载来模拟渲染。有14ms的负载和28ms的负载。 28ms的负载设计是为了弄乱rAF,因为它会在许多机器上以每秒30到60帧的速度轻弹。关键是要表明rAF每秒只能有60,30,20,......等帧。

function loop(time){  // microsecond timer 1/1,000,000 accuracy in ms 1/1000th
    // render code here
    requestAnimationFrame(loop);
    // or render code here makes no diff
}
requestAnimationFrame(loop); // to start
var ctx1 = can1.getContext("2d");
var ctx2 = can2.getContext("2d");
var ctx3 = can3.getContext("2d");
var lastTime1 = 0;
var lastTime2 = 0;
var lastTime3 = 0;
var frameFunction = frame1;
var frameText = "";
var drag = false;
var loadAmount = 14;
var stats = [{
     data : [],
     pos : 0,
     add(val){
         this.data[(this.pos ++) % 150] = val;
     }
   },{
     data : [],
     pos : 0,
     add(val){
         this.data[(this.pos ++) % 150] = val;
     }
   },{
     data : [],
     pos : 0,
     add(val){
         this.data[(this.pos ++) % 150] = val;
     }
   }   
];
for(let i = 0; i <  150; i += 1){
    stats[0].add(0);
    stats[1].add(0);
    stats[2].add(0);
}
setupContext(ctx1);
setupContext(ctx2);
setupContext(ctx3);
drawFrameTime(ctx1,0);
drawFrameTime(ctx2,0);
drawFrameTime(ctx3,0);
can1.addEventListener("click",()=>frameFunction = frame1);
can2.addEventListener("click",()=>frameFunction = frame2);
can3.addEventListener("click",()=>frameFunction = frame3);
load.addEventListener("click",()=>{
    if(drag){
        drag = false;
        load.value = "Add load.";
    }else{
        drag = true;
        load.value = "Remove load.";
    }
});
loadPlus.addEventListener("click",()=>{
    if(loadAmount === 14){
        loadAmount = 28;
        loadPlus.value = "28ms";
    }else{
        loadAmount = 14;
        loadPlus.value = "14ms";
    }
});

function CPULoad(){
    if(drag){
        var stopAt = performance.now() + loadAmount;
        while(performance.now() < stopAt);
    }
}    
function setupContext(ctx){
    ctx.font = "64px arial";
    ctx.textAlign = "center";
    ctx.textBaseline = "middle";
}
function drawStats(ctx,stat){
    ctx.setTransform(1,0,0,1,0,64);
    ctx.strokeStyle = "red";
    ctx.strokeRect(-1,16.666,152,0);
    ctx.strokeStyle = "black";
    ctx.beginPath();
    var i = stat.pos + 149;
    var x = 0;
    ctx.moveTo(x,stat.data[(i++) % 150]);
    while(x ++ < 150 && stat.data[i % 150] !== undefined) {
        ctx.lineTo(x,stat.data[(i++) % 150]);
    }
    ctx.stroke();

}

function drawFrameTime(ctx,time){
    ctx.fillStyle = "black";
    ctx.setTransform(1,0,0,1,0,0);
    ctx.clearRect(0, 0, ctx.canvas.width, ctx.canvas.height);
    if(time > 0){
        ctx.fillStyle = drag ? "red" : "black";
        ctx.setTransform(1,0,0,1,ctx.canvas.width / 2,ctx.canvas.height *0.25);
        ctx.fillText(time,0,0);
        ctx.setTransform(0.4,0,0,0.4,ctx.canvas.width / 2,ctx.canvas.height * 0.75);
        
        ctx.fillText(Math.round(1000 /  Number(time)) + "fps",0,0);
    }else{
        ctx.setTransform(0.4,0,0,0.4,ctx.canvas.width / 2,ctx.canvas.height * 0.75);
        ctx.fillText("Click to Start.",0,0);
    
    }
    ctx.fillStyle = "black";
    ctx.setTransform(0.2,0,0,0.2,ctx.canvas.width / 2,ctx.canvas.height * 0.9);
    ctx.fillText(frameText,0,0);
    if(drag){
        ctx.fillStyle = "red";
        ctx.setTransform(0.2,0,0,0.2,ctx.canvas.width / 2,ctx.canvas.height * 0.5);
        ctx.fillText("Load " + loadAmount + "ms",0,0);
    
    }
    
}



function frame1(time){
     requestAnimationFrame(frameFunction);
     frameText = "Using rAF.";
     var frameTime = time - lastTime1;
     lastTime1 = time;
     stats[0].add(frameTime);
     drawFrameTime(ctx1,frameTime.toFixed(2));
     drawStats(ctx1,stats[0]);
     CPULoad()
}
    
function frame2() {
    setTimeout(function () {
        frameText = "Using rAF & setTimeout.";
        var time = performance.now();
        var frameTime = time - lastTime2;
        stats[1].add(frameTime);
        lastTime2 = time;
        drawFrameTime(ctx2, frameTime.toFixed(2));
        drawStats(ctx2,stats[1]);
        CPULoad();
        requestAnimationFrame(frameFunction);
    }, 1000 / 60);
}
function frame3() {
    setTimeout(frameFunction,1000/60);
    frameText = "SetTimeout by itself.";
    var time = performance.now();
    var frameTime = time - lastTime3;
    stats[2].add(frameTime);
    lastTime3 = time;
    drawFrameTime(ctx3, frameTime.toFixed(2));
    drawStats(ctx3,stats[2]);
    CPULoad();

}
requestAnimationFrame(frameFunction);
body {
    font-family : arial ;
}
canvas {
    border : 1px solid black;
}
div {
   text-align : center;
}

答案 1 :(得分:0)

功能目的不是有60 FPS,而是绘制框架时绘制并使性能更好。没有电脑可以保持完美的60 FPS。 另外,为什么你的requestAnimationFrame在超时?