简体   繁体   English

requestAnimationFrame循环不正确FPS

[英]requestAnimationFrame loop not correct FPS

I have a javascript function that my game loops through (hopefully) 60 times a second that controls input, drawing, etc. 我有一个javascript函数,我的游戏循环(希望)每秒60次控制输入,绘图等。

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 目前编码的方式似乎总是在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);
}

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? 如果我从顶部删除setTimeout和第一个requestAnimationFrame并取消注释底部的reuqestAnimationFrame并删除其他setTimeout事物,则FPS提高到58但在58和62之间快速变化,再次,不是静态60.它是否有一些东西到做1000/60不是一个整数? How would people using requestAnimationFrame achieve 60 fps if this was true? 如果这是真的,那么使用requestAnimationFrame的人如何实现60 fps?

Don`t use setTimeout or setInterval for animation. 不要使用setTimeout或setInterval来表示动画。

The problem is that you are calling a timer event from within the request animation event. 问题是您正在请求动画事件中调用计时器事件。 Remove the timeout and just use requestAnimationFrame. 删除超时并只使用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) is always in sync (unless the browser has vertical sync turned off). RequestAnimationFrame(rAF)始终保持同步(除非浏览器已关闭垂直同步)。 The next frame will be presented in 1/60th, 2/60th, 3/60th etc... of a second. 下一帧将以1 / 60th,2 / 60th,3 / 60th等呈现。 You will not get 52frame per second using rAF, rather 60fps, 30fps, 15fps, etc... 你不会使用rAF获得每秒52帧,而不是60fps,30fps,15fps等等......

The Demo below show the difference in use. 下面的演示显示了使用上的差异。

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. 因为requestAnimationFrame使用一些智能来为动画计时,所以它们不能同时运行,因此单击画布启动它。

You can also add a load to simulate rendering. 您还可以添加加载来模拟渲染。 There is a 14ms load and a 28 ms load. 有14ms的负载和28ms的负载。 The 28ms load is design to mess up rAF as it will on many machines flick between 30 and 60 frames per second. 28ms的负载设计是为了弄乱rAF,因为它会在许多机器上以每秒30到60帧的速度轻弹。 The point is to show that rAF can only have 60, 30, 20,.. etc frames per second. 关键是要表明rAF每秒只能有60,30,20,......等帧。

 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> 

The functions purpose isnt to have 60 FPS, but to draw while a frame is beeing drawn and make the performance better. 功能目的不是有60 FPS,而是绘制框架时绘制并使性能更好。 No computer will stay perfectly at 60 FPS. 没有电脑可以保持完美的60 FPS。 Also, why is your requestAnimationFrame IN the timeout? 另外,为什么你的requestAnimationFrame在超时?

声明:本站的技术帖子网页,遵循CC BY-SA 4.0协议,如果您需要转载,请注明本站网址或者原文地址。任何问题请咨询:yoyou2525@163.com.

 
粤ICP备18138465号  © 2020-2024 STACKOOM.COM