requestAnimationFrame循环不正确FPS [英] requestAnimationFrame loop not correct FPS

查看:104
本文介绍了requestAnimationFrame循环不正确FPS的处理方法,对大家解决问题具有一定的参考价值,需要的朋友们下面随着小编来一起学习吧!

问题描述

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

I have a javascript function that my game loops through (hopefully) 60 times a second that controls input, drawing, etc.

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

The way it is currently coded it seems to be always be around 52, noticeably lower than 60 fps, and it even dips to 25-30 fps even when nothing else is happening

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?

if I remove the setTimeout and the first requestAnimationFrame from the top and uncomment the reuqestAnimationFrame at the bottom and remove the other setTimeout things, the FPS improves to 58 but rapidly changes between 58 and 62, again, not statically 60. Does it have something to do with 1000/60 is not a whole number? How would people using requestAnimationFrame achieve 60 fps if this was true?

推荐答案

不要使用setTimeout或setInterval来制作动画。



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

Don`t use setTimeout or setInterval for animation.

The problem is that you are calling a timer event from within the request animation event. Remove the timeout and just use requestAnimationFrame.

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

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

RequestAnimationFrame (rAF) is always in sync (unless the browser has vertical sync turned off). The next frame will be presented in 1/60th, 2/60th, 3/60th etc... of a second. You will not get 52frame per second using rAF, rather 60fps, 30fps, 15fps, etc...

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

The Demo below show the difference in use.

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

Because requestAnimationFrame uses some smarts to time the animation they can not both run at the same time so click on the canvas to start it.

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

You can also add a load to simulate rendering. There is a 14ms load and a 28 ms load. The 28ms load is design to mess up rAF as it will on many machines flick between 30 and 60 frames per second. The point is to show that rAF can only have 60, 30, 20,.. etc frames per second.

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;
}

<div><h2>RequestAnimationFrame (rAF)</h2>
rAF V rAF & setTimeout V setTimeout<br>
<canvas id = can1 width = 150></canvas>
<canvas id = can2 width = 150></canvas>
<canvas id = can3 width = 150></canvas><br>
Click the frame to set the current test.<br>
The left frame is using rAF alone, the middle using setTimeout and rAf, and the rigth frame uses setTimeout alone.<br>
Click <input type="button" id=load value="add Load"></input> to simulate a rendering load of around <input type="button" id=loadPlus value="14ms" title="click to change CPU load between 14 and 28ms"></input> <br>
   Try draging and selecting this text and see how it effects the different methods.<br>
rAF is by far the most stable of the 3.<br>
</div>

这篇关于requestAnimationFrame循环不正确FPS的文章就介绍到这了,希望我们推荐的答案对大家有所帮助,也希望大家多多支持IT屋!

查看全文
登录 关闭
扫码关注1秒登录
发送“验证码”获取 | 15天全站免登陆