简体   繁体   中英

Improving Canvas Game Performance by Finding average FPS

Take a look at the movement function of my game:

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

Now, I will explain: My actual game is a game where the velocity of the player changes as the game progresses. The speed is proportional to the window's height, because the player's goal is to move up. I'm using this technique in order to better control how many player-draws I perform. We will get to the problem with that later.

I'm also using this technique (see the answer)to measure the timings in case there was a lag in the last frame, it will just cover the distance it was supposed to cover, it also means that the target Y will always be reached on time on all devices.

The FPS average is the biggest issue I am running into. Different devices have different specs. in order to create a good game for all devices I must master this function:

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

}, 1000 / fps);

I have tested my game on many devices, on some the fps needs to be 40, 50, 30 or something else in order for the game to run smoothly(which means each player "move" will be equal to it's previous, otherwise they will experience lag). So I was thinking, either secretly running the game as the game "loads" for the first time and finding out the average FPS, or maintaining some kind of a learning curve in order to adjust how many times this function runs. Either way I do not really know how to achieve it perfectly, I know it may be hard but please I need some help with this.

canvas.getContext("2d") - is slow.

You need use webgl . For work with webgl use Pixi.js . It's pretty good library.

Loot at this DEMO ( without source )

Frame Rates

To control frame rate using requestAnimationFrame (rAF) you need to monitor the time each call to the rAF callback is made. This can be done by using the first argument passed be rAF to your function.

Example gets mean frame rate for 60 frames.

 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) 

This example waits for 4 seconds (approx) befor sampling frame rate.

 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) 

To control the frame rate you need to ignore early frames if they come in. There is a little kludging because the frame time is not perfect but because you know the frames will not come faster than 1/60th it is easy to deal with.

 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) 

Good numbers?

It is very hard to get a good frame rate estimate with javascript, especially at startup as there is a lot going on that can cause frames to be dropped. Depending on what is going on on the page you may need several seconds or more before you can reliably estimate the load of your game.

The best way to deal with the frame rate is to first allow some stabilization time. Monitor the frame rate and while you see many frames in a row dropped, wait. Once you have a consistent rate use the min rate as the base rate, throttling frame render as shown in the second snippet.

You can only have consistent frame rates at rate = 60/n where n is an integer >= 1. Frame rates 60, 30, 20, 15, 12, 10, 8.87... and so on.

The technical post webpages of this site follow the CC BY-SA 4.0 protocol. If you need to reprint, please indicate the site URL or the original address.Any question please contact:yoyou2525@163.com.

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