0

I am writing a card game so i need to setup a basic game loop using window.requestAnimationFrame. I need to make the fps set to 30fps and since it is a slow paced card game i just need a simple workable game loop. My game loop is as follows.

function loop(timestamp) {
          var progress = timestamp - lastRender

          update(progress)
          draw()

          lastRender = timestamp
          window.requestAnimationFrame(loop)
        }
        var lastRender = 0
        window.requestAnimationFrame(loop)

How can i make it so that the fps is set to 30fps? I appreciate any help! Thanks!

kofhearts
  • 3,607
  • 8
  • 46
  • 79
  • please take a look at this thread: http://stackoverflow.com/questions/19764018/controlling-fps-with-requestanimationframe – lukaszkups Feb 03 '17 at 07:56
  • Why does it need to be 30 fps? The trick with `requestAnimationFrame` is that it will always give the best frame rate possible to any given monitor, so limiting to 30 fps seems weird. – Emil S. Jørgensen Feb 03 '17 at 08:15
  • @EmilS.Jørgensen ok thanks! it turns out that the above code is fine. It is giving 60 fps in my system and i think that is fine for games. – kofhearts Feb 03 '17 at 08:46

2 Answers2

1

FPS stands for Frames Per Second.

1000 / 30 = 30 fps

Try this basic function:

function animate() {
  setTimeout(function() {
    requestAnimationFrame(animate);
  }, 1000 / 30);
}
Luke B
  • 2,075
  • 2
  • 18
  • 26
Qh0stM4N
  • 182
  • 1
  • 9
1

You should keep simulating and drawing separate.

Below is an example where i make a simulation every millisecond (1000 times in a second). Each simulation cancels the previous' request for an animation frame.

If the browsers refresh has "ticked" it will have drawn our update (incremented our counter).

after 1 second i stop and we should see approximately your monitors refresh rate as our count.

//counter
var count = 0;
//draw function
function draw() {
    count++;
  }
  //Animation frame handle
var animationFramHandle;
//Run every millisecond
var interval = setInterval(function() {
  //Cancel requestAnimationFrame
  cancelAnimationFrame(animationFramHandle);
  //request new requestAnimationFrame
  animationFramHandle = requestAnimationFrame(draw);
}, 1);
//Wait 1 second
setTimeout(function() {
  //Stop simulation
  clearInterval(interval);
  cancelAnimationFrame(animationFramHandle);
  console.log("ticks in a second:", count);
}, 1000);

Edit - Why separation of concerns

Drawing on the canvas is an expensive operation that you normally wouldn't want to run on every frame if you can avoid it.

Basically only call for a redraw when something changes.

In my example below i create a big amount of boxes, simulate them and draw them:

//DOM elements
var canvas = document.createElement("canvas");
canvas.width = 400;
canvas.height = 400;
document.body.appendChild(canvas);
var ctx = canvas.getContext("2d");
ctx.fillStyle = "rgba(0,0,0,0.1)";
var drawNode = document.createElement("p");
document.body.appendChild(drawNode);
//Variables
var simulations = 0;
var simulationTime = 0;
var requestAnimationFrameHandle;
//Boxes to simulate and draw
var boxes = [];
while (boxes.length < 10000) {
    boxes.push({
        x: Math.random() * canvas.width,
        y: Math.random() * canvas.height,
        s: 5
    });
}
//Draw function
function draw() {
    var t = Date.now();
    //Clear
    ctx.clearRect(0, 0, canvas.width, canvas.height);
    //Draw
    for (var bIndex = 0; bIndex < boxes.length; bIndex++) {
        var box = boxes[bIndex];
        ctx.fillRect(box.x, box.y, box.s, box.s);
    }
    //Log
    drawNode.innerHTML = ("New draw after " + simulations + " simulations<br>Drawing took " + (Date.now() - t) + " ms<br>Simulation time is " + simulationTime + " ms");
    simulations = 0;
}
//Simulate function
function simulate(force) {
    if (force === void 0) { force = false; }
    simulations++;
    if (Math.random() * 1000 > 800) {
        var t = Date.now();
        for (var bIndex = 0; bIndex < boxes.length; bIndex++) {
            var box = boxes[bIndex];
            box.x = Math.abs(box.x + (Math.random() * 3 - 1)) % canvas.width;
            box.y = Math.abs(box.y + (Math.random() * 3 - 1)) % canvas.height;
        }
        simulationTime = Date.now() - t;
        cancelAnimationFrame(requestAnimationFrameHandle);
        requestAnimationFrameHandle = requestAnimationFrame(draw);
    }
}
setInterval(simulate, 1000 / 120);

Notice how simulating them is way faster than drawing them.

As a rule of thumb, you only have 1000/60 ~ 16 milliseconds between each frame, so if we can spare the milliseconds a draw would take, then we would gladly do so and focus such a frames calculation time on bigger operations (like pathfinding or collision detection or whatever would be heavy in your game).

This way we can also run our simulation at another rate than the draw rate, which is handy when working with asynchronous tasks.

Emil S. Jørgensen
  • 6,216
  • 1
  • 15
  • 28
  • 1
    can you elaborate a little more on why simulating and drawing should be kept separate rather than together like in my example above? Thanks! – kofhearts Feb 03 '17 at 09:49