kofhearts
kofhearts

Reputation: 3774

A simple 30fps game loop in javascript using requestAnimationFrame?

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!

Upvotes: 0

Views: 5477

Answers (2)

Qh0stM4N
Qh0stM4N

Reputation: 192

FPS stands for Frames Per Second.

1000 / 30 = 30 fps

Try this basic function:

function animate() {
  setTimeout(function() {
    requestAnimationFrame(animate);
  }, 1000 / 30);
}

Upvotes: 1

Emil S. Jørgensen
Emil S. Jørgensen

Reputation: 6366

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.

Upvotes: 2

Related Questions