The "game loop" is the main component of any game. It separates the game logic and the visual layer from a user's input and actions.
Traditional applications respond to user input and do nothing without it - word processors format text as a user types. If the user doesn't type anything, the word processor is waiting for an action.
Games operate differently: a game must continue to operate regardless of a user's input!
The game loop allows this. The game loop is computing events in our game all the time. Even if the user doesn’t make any action, the game will move the enemies, resolve collisions, play sounds and draw graphics as fast as possible.
There are different ways to perform animation with JavaScript. A very detailed comparison of three different methods has already been presented in the W3Cx HTML5 Coding Essentials course. Below is a quick reminder of the methods, illustrated with new, short, online examples.
Syntax: setInterval(function, ms);
The setInterval function calls a function or evaluates an expression at specified intervals of time (in milliseconds), and returns a unique id of the action. You can always stop this by calling the clearInterval(id) function with the interval identifier as an argument.
Source code extract:
var addStarToTheBody = function(){
document.body.innerHTML += "*";
};
//this will add one star to the document each 200ms (1/5s)
setInterval(addStarToTheBody, 200);
WRONG:
setInterval(‘addStarToTheBody()’, 200);
setInterval(‘document.body.innerHTML += “*”;’, 200);
GOOD:
setInterval(function(){
document.body.innerHTML += “*”;
}, 200);
or like we did in the example, with an external function.
Reminder from the HTML5 Coding Essentials course: with setInterval - if we set the number of milliseconds at, say, 200, it will call our game loop function EACH 200ms, even if the previous one is not yet finished. Because of this disadvantage, we might prefer to use another function, better suited to our goals.
Syntax: setTimeout(function, ms);
The setTimeout function works like setInterval but with one little difference: it calls your function AFTER a given amount of time.
Try an example at JSBin: open the HTML, JavaScript and output tabs to see the code. This example does the same thing as the previous example by adding a "*" to the document every 200ms.
Source code extract:
var addStarToTheBody = function(){
document.body.innerHTML += "*";
// calls again itself AFTER 200ms
setTimeout(addStarToTheBody, 200);
};
// calls the function AFTER 200ms
setTimeout(addStarToTheBody, 200);
This example will work like the previous example. However, it is a definite improvement, because the timer waits for the function to finish everything inside before calling it again.
For several years, setTimeout was the best and most popular JavaScript implementation of game loops. This changed when Mozilla presented the requestAnimationFrame API, which became the reference W3C standard API for game animation.
Note: using requestAnimationFrame was covered in detail in the W3Cx HTML5 Coding Essentials course.
When we use timeouts or intervals in our animations, the browser doesn’t have any information about our intentions -- do we want to repaint the DOM structure or a canvas during every loop? Or maybe we just want to make some calculations or send requests a couple of times per second? For this reason, it is really hard for the browser’s engine to optimize the loop.
And since we want to repaint the game (move the characters, animate sprites, etc.) every frame, Mozilla and other contributors/developers introduced a new approach which they called requestAnimationFrame.
This approach helps the browser to optimize all the animations on the screen, no matter whether Canvas, DOM or WebGL. Also, if the animation loop is running in a browser tab that is not currently visible, the browser won't keep it running.
Basic usage, online example at JSBin.
Source code extract:
window.onload = function init() {
// called after the page is entirely loaded
requestAnimationFrame(mainloop);
};
function mainloop(timestamp) {
document.body.innerHTML += "*";
// call back itself every 60th of second
requestAnimationFrame(mainloop);
}
Notice that calling requestAnimationFrame(mainloop) at line 10, asks the browser to call the mainloop function every 16.6 ms: this corresponds to 60 times per second (16.6 ms = 1/60 s).
This target may be hard to reach; the animation loop content may take longer than this, or the scheduler may be a bit early or late.
Many "real action games" perform what we call time-based animation. For this, we need an accurate timer that will tell us the elapsed time between each animation frame. Depending on this time, we can compute the distances each object on the screen must achieve in order to move at a given speed, independently of the CPU or GPU of the computer or mobile device that is running the game.
The timestamp parameter of the mainloop function is useful for exactly that: it gives a high resolution time.
We will cover this in more detail, later in the course.