Realmscape

The Basics: Game Loop

20 May 2015  -  Jon Hall

The game loop is the single most simple, yet complex part of the Game Engine. Without it, you have nothing more than an inanimate output.

It is sometimes considered the defining feature of a Game Engine over a library; A library is utilised from within your game loop, but a game engine makes calls to your code from within it's game loop.
It's purpose is to complete 3 tasks in a repeating loop, while retaining a constant ratio between real time and game time. In JavaScript, we have a unique way of maintaining this loop, so I'll cover the basics of a 'generic' game loop, before explaining why we do things differently in JavaScript.

What is a game loop?

A game loop is the God of Time within your Game Engine. By definition, it is a continuously running loop which processes user input, updates the game state, and renders the game output.
By decoupling processor speed from the progression of game state, the game loop maintains a constant ratio between the two by increasing or decreasing the frequency of repetition. Simply put, if you want your game time to progress faster, you run your game loop more often, and to slow your game down run your loop less often.

The order of tasks within the game loop makes a difference to how the game functions. The most ideal ordering can be different depending on the environment your engine will be running in, as well as many other contributing factors. In the case of a JavaScript game engine, we render the game state first, then handle user input, then update our game state.

How does it work?

Back in the not-so-distant past, the hardware specifications of the target device were known when the game was being developed. You'd be running the game loop as fast as possible, and computing as much as possible each loop to maintain a steady framerate.

Fast forward to modern day, and we have no idea if our game is going to be run on a high-end gaming PC, a mid-range laptop or tablet, or even a mobile device. The hardware capabilities can vary wildy, and it is up to the developer to cater for as many devices as possible while retaining a 'pleasant' user experience. The inclusion of end-user settings helps put some control into the hands of the gamer, but it is still the developer's responsibility to make a consistent experience available across all target devices.

Sometimes you need to restrain your game loop, either to conserve battery life (by decreasing the frequency of computations - giving the CPU a rest) on a mobile device, or to increase the time available between loops so more expensive calculations can be done each cycle (realistic physics may require two or three times longer to calculate than simple collisions).

The most obvious approach—one I'd not recommend implementing yourself—is a while true loop. It repeats indefinitely, at the fastest possible speed, but in most cases will eventually freeze or crash because it has no time to stop and take a breath. As soon as it completes it's task, it begins at the start once more, leaving no room for other events or actions to take place. It's like an excited dog running from one tree to another, never pausing in-between.

while(true){
 /* Game Loop logic */
}
A loop that continues endlessly

Building on that a little more, replace the boolean true with a variable - now we can command the loop to cease when the game has reached completion. The dog can be whistled back for a rest when we decide he has sniffed enough trees.

var play = true;
while(play){
 /* Game Loop logic */
}
A loop that will terminate when play is no longer true

We want to be able to leash the dog though, there may be a thousand trees to sniff, but we still need to find time for lunch!
This is where different programming languages and environments have differing methods to allow other processes to get some CPU time. Given that we are developing in JavaScript, I'll focus on how we can break up that single run of a thousand trees, into one tree at a time until we reach the end of the park.

Non-blocking loops in JavaScript

The previous example would be considered a blocking loop - no other actions can take place until the entire park of trees has been sniffed by the dog.
A non-blocking loop is one that has a clearly defined beginning and end to each action, and something else may be done between repetitions. This is the kind of loop you want to implement, because otherwise the device is trying to complete a single (possibly never-ending) task without a break (that's usually when your cursor changes into a 'busy' state).

So let's define our game loop as a function; A single unit of repetition that could be run once, or a thousand times.

Just as a clock 'ticks' once a second, our game loop function is executed on a regular basis, so we are able to define a rate of ticks per second (in the case of rendering a frame every loop, this would be the frames per second, or FPS).

function tick(timestamp){
 /* Game Loop logic */
 // Render output
 // Handle input
 // Update game state
}
Our single unit of game loop, the tick function.

There's two distinct methods of repeating that non-blocking function in JavaScript - setInterval and requestAnimationFrame - and both of them give us some control over the frequency of the tick.

Game loop using setInterval()

The window.setInterval()[1] method calls a function, or executes a code snippet repeatedly, with a fixed time delay between each call to that function. The syntax for this is

 var intervalID = window.setInterval(func,delay);

where the function we want to call is our tick function, the delay between calls is in milliseconds (thousandths of a second), and the intervalID can be used to stop the repetition with the window.clearInterval() method.

The delay can be used to 'lock' the loop into a given rate, which in milliseconds would be 1000/FPS. This is called a fixed time step[2].
Although this method of controlling the game loop is often used, there are several reasons why you shouldn't use it.

Game loop using requestAnimationFrame()

The recommended method for controlling your game loop in JavaScript is the window.requestAnimationFrame()[3] method, or rAF for short.

rAF informs the browser you wish to perform an animation (or change the visual state), and requests the browser to call a given function before the next repaint. Its syntax is

 window.requestAnimationFrame(callback);

where the callback is your tick function that you want to call. The callback will be given a single argument[4], as a DOMHighResTimeStamp[5] which has a precision of up to one microsecond (one millionth of a second!). In the instance of a HighResTimeStamp being unavailable, an argument with minimum accuracy of one millisecond (one thousandth of a second) will be given instead.

rAF must be called again once within your game loop, so the browser calls the next iteration of your loop.
A common mistake is to call rAF multiples times within your game loop logic with different callbacks. If both requestAnimationFrame(tick) and requestAnimationFrame(render) were called somewhere within the game loop logic, browsers may handle multiple queued requests differently - one or more of your callbacks may be ignored, or alternated on redraw.
To avoid unexpected behaviour or bugs, it's best practise to ensure there's only a single call to rAF each frame. (Ideally at the end of your loop, because you are literally requesting the callback be called right before the next frame is drawn)

function tick(timestamp){ // The game loop function
 /* Game Loop logic */
 // Render output
 // Handle input
 // Update game state
 window.requestAnimationFrame(tick); // Every tick, inform the browser to call tick before the next repaint
}

// Inform the browser to call tick for the first time, before the 'first' repaint
window.requestAnimationFrame(tick);
The tick function, being called by the browser via rAF

Although we lose the ability to explicitly define the tickrate ourselves, we can derive a very specific value of time passed since the last loop. This is a non-blocking variable time step[6] loop, and from this the FPS can be calculated if we need it.

When focus is lost (for instance, the user changes browser tab) the tick function is not called, because the browser does not need to repaint and therefore doesn't call our tick function. When focus is restored, our tick function is called before the first repaint occurs, and we will be informed that a large period has elapsed via the callback argument. It is up to you to decide whether or not to act on that information - essentially our logic was paused during the loss of focus.

The benefit of leaving the timing up to the browser, is that it will only call your tick function as often as it is required - so if the PC monitor refreshes at 75hz it may tick 75 times per second, if it's running on a mobile device with a refresh rate of 30hz you'll likely only execute your loop 30 times per second.

The added complexities of variable time steps over fixed ones may be becoming apparent - you could shift an object by one unit every loop in a fixed time step running at 16.6̅6ms and assert the movement speed as 60 units per second. With a variable time step you will be interpolating positions over time. (Time for a quick math lesson?)
Sound complicated? Well, it's not. In fact it's probably an advantage to you, as now you can make animations appear smooth, regardless of tickrate.

Comments, Feedback, and Discussion can be found on our subreddit

 

[1] MDN WindowTimers.setInterval() documentation
[2] A fixed time step model increments your game simulation time by a fixed period each loop. It allows for foresight and pre-calculation of values because you are asserting equal discreet time steps, regardless of how much real-time has passed.
[3] MDN Window.requestAnimationFrame() documentation
[4] An Argument, in a mathematical and programming context is a specific input in a function. More formally, the argument is an actual parameter rather than a formal parameter. Parameter and Argument are often (incorrectly) used interchangeably [Wikipedia Link]. I generally informally refer to both cases as an argument, for the sake of simplicity in these blog posts.
[5] MDN DOMHighResTimeStamp documentation
[6] A variable time step model simulates continuous time, by using the time difference between loop calls to calculate the precise value of changing variables at an exact given time. X sec may have passed between the previous two loop calls, but Y sec may pass before it is called again.