Programming Platformers

Game Loops and Render Frames

I always found articles written about game loops to be strange- why would you need to overthink something seemingly so trivial? A game loop to me was simply some code that ran every so often to read input, update the game state and draw to the screen. Could you not simply run game logic and rendering at some given frame-rate?

Of course, this would be a perfectly fine idea if it were not for one flawed assumption: it assumes that you are the one who gets to pick the frame-rate. But unless you plan to target only one specific device and that device happens to have a fixed frame-rate, this condition does not hold. Heck- even one device can run at different frame-rates if it chooses to throttle frames: eg in low battery mode.

This variable frame rate is an issue for many games- and platformers are no exception. For platformers we would like to design movement in such a way that once we tune the motion and the jumping and all the other variables, the game logic should behave the same way independent of what frame-rate our game runs at.

The naive solution would be to run game logic at a regular timestep (eg at 60FPS or every 16ms) and simply render the latest game state whenever the render loop kicks in. Unfortunately, rendering the last game state at a variable frame-rate causes smooth motions to turn stuttery. Luckily there is a way to keep our game logic on a fixed time-step while still keeping our rendering running nice and smooth.

The main idea is that when it comes time to render, we render not the last game state but a mix of the last game state and the one before that. Because we have two valid game states we only need to interpolate between them to get a valid looking result. While this does mean that our render state “lags behind” our game state, it only does so by at most one fixed timestep from our “real” game state. Between render frames however, things appear visually smooth and consistent. And all this without changing any game logic.

Implementing the game loop is as follows: Each render frame we keep track of the time that’s passed. Every time we hit our fixed timestep, we save our now previous game state and run our game logic to generate the latest game state. Then, whether or not we ran our game logic this frame, we interpolate between the latest two game states. We do so by using the elapsed time from the previous fixed timestep. Note that our interpolation factor is guaranteed to be anywhere from 0 to 1 (though not inclusive of 1). We do this for every object we render, interpolating their position, rotation and scale between the two game states.

And just like that, game logic remains on a fixed time-step all the while rendering smoothly at any frame-rate.


Home