The game loop behavioral pattern is essential in games and simulations to make the actions proceed at the proper speed regardless of what the user is doing or how fast the computer is.
A game loop will repeatedly do the following three things:
- Check for input and process it if available. Event-driven operating systems should provide some way to check for events without returning control back to the operating system. This might be called peeking at an event or something like that. With this, you have the ability to check if the user pressed any buttons or moved the mouse, and if nothing happened, then continue doing the work of the game.
- Update game objects. This means you can move things around, create or destroy things, and change any other game object properties.
- Render the display. This means that you clear what used to be drawn on the display and draw the game objects in their new positions or state.
All games should have a loop like this.
This episode explains how to avoid blocking progress while waiting for user input. And it also explains how to manage the game speed.
Old games from the 1980’s and probably into the 1990’s thought they knew exactly how fast the computer would run and tuned their game loops to fit as much as possible into the loop. But as computers became faster, the loop finished updating and rendering faster too. And what did the loop do? It went right through the loop and updated and rendered again. The game loop had one idea of what it thought of as game time and when it used the speed of the game loop to measure that, it caused problems. The solution then was to just slow down the computers. But that wasn’t a good solution then and you just can’t buy a computer these days with a “Slow” button anymore.
One very simple solution is to check the time at the beginning of the loop and check the time again after processing input, updating, and rendering. Then you can add a delay or a sleep to the end of your loop for whatever time is left over. Maybe you want your game to run at 60 frames per second. That means each iteration of the loop should take 1 second divided by 60 which is about 16 milliseconds. If your loop runs in 10 milliseconds, then just go to sleep for 6 milliseconds before starting the loop again. You check the time each iteration because sometimes the loop might run faster or slower. By adjusting how much time you sleep, you can keep the loop running at a fixed rate.
This works great if your computer is fast enough to have time left over each game loop. But it won’t help with a slow computer that can’t keep up at all. For a slow computer that can’t keep up with the game loop, one way to solve the problem is to just not do so much work during the game loop. Think of a sinking ship and the crew throwing cargo overboard in an attempt to lighten the load. That’s exactly what you can do. Let’s say you want the game to draw to the screen at 60 frames per second, what if we sometimes skipped the drawing? By skipping the rendering, we can avoid doing some work and can start another iteration of the game loop. And maybe this time, there’ll be enough time left over to render the changes that were updated.