When building a game from scratch, one thing that you need in your game’s architecture is something called the game loop . The game loop is the driving force behind your game, and very few game architectures leave this out. We’ll take a look at the game loop in this tutorial, both in a general, high-level sense, as well as the game loop that XNA presents to you.
The game loop is a simple architectural pattern for organizing your game code, splitting apart when different events should occur. The primary feature of the game loop is a cycle of updating your game’s state, drawing your game’s state, and then waiting for a short amount of time until it is time to go again. You can think of the game loop as the heartbeat of your game, continually pushing through the cycle at regular intervals, until the game ends. Like most design patterns , the game loop is a conceptual idea that is applicable in many situations; nearly every video game will have some implementation of a game loop.
At a high level, you can think of the game loop as breaking down your game into five stages: startup, update, draw, wait, and cleanup.
In the startup stage, your game will perform any one-time setup that is required to get your game ready. This may include things like loading content, preparing input devices, and initializing the game state. This stage is only executed one time.
Once the startup stage has been completed, we jump into the heart of the game loop, which will be executed repeatedly, until the player quits the game. This means that the update stage will be executed repeatedly, and fairly frequently. (In general, the game loop usually runs anywhere from 24 to 60 times per second.)
The goal of the update stage is to make sure that the game’s state accurately reflects what it should be, given that some time has passed since the last update. If an object was in motion, we’ll want to update its position to reflect this, as time passes by.
Common tasks that are done during the update stage are:
The next step of the game loop is the draw stage. In the draw stage, we handle anything we need to get things actually drawn to the screen. For most modern games, much of this work is handled by the graphics card. We just need to tell it what things to draw and where.
It is important to keep drawing code separated from updating code. There are lots of reasons to do this, most of which is best saved for another tutorial. The draw stage should not involve any changes to the actual game state, just the work required to turn it into pixels on the screen.
The final step of the loop is the wait stage. In the wait stage, the game will… well… wait for a little bit.
It is often desirable for the game loop to proceed at a regular pace, regardless of how long it takes to actually update and draw things. Without this regulation, the game will update and draw as fast as it possibly can. (That may, in fact, be preferable, but requires some extra work, which we will talk about in another tutorial, soon.) The problem with this is that on fast computers, you’ll get more updates per second than slow computers, and the game will run noticeably faster (to the point of being broken and no fun).
So instead of running as fast as possible, we wait for a little bit until enough time has passed that we reach our pre-established regular interval before continuing on to the next update and draw. I should mention that while we’re talking about waiting here, this all happens so fast that the player will see it as a seamless and continuous flow through your game.
Nearly all game loop implementations, including the one MonoGame provides, give you a great deal of control over the wait stage, as well as providing you with information about how long it has been since the last update or draw stage. We’ll talk about those details in the next two tutorials.
One last thing that is of value here is a quick discussion of lag. Let’s say you’ve set up your game to loop 30 times a second. That means each loop will take 33.3 milliseconds for each loop. What if your updating code takes 40 milliseconds to execute? That’s longer than the time allowed for the whole loop.
For starters, we won’t be doing any waiting in the wait stage. We’ll just jump right back in. The game loop will run as fast as it can. If this happens, the player is likely to notice these delays, which show up as noticeable pauses between draw stages.
To the extent possible, you will want to manage your update and draw stages in a way that prevents or limits this from happening. I say that like it is easy to do, and should be second nature, but it’s not. In nearly every game that you make, simple or complex, you’ll eventually run into something that just makes your game run slow. The worst part is, aside from some general tips this problem is mostly something you’re going to need to resolve on your own.
Managing your game’s performance is one of the harder challenges of making a game. Impatience or frustration with this prevents a lot of really cool games from ever getting to the market, and plowing your way through this is what separates real game developers from hobbyists.
Once an update detects that the player wants to quit the game, the game loop is terminated, and the cleanup stage is reached.
The cleanup stage is a one-shot chance to intelligently clean up anything that you no longer need. This could include disposing of art assets that you loaded and passed to the graphics card, or potentially error logging if the game loop terminated abnormally.
It is worth mentioning that with MonoGame, you don’t need to stress too much about cleaning things up as your game exits because between the .NET and MonoGame itself, this is mostly taken care of for you.
The concept of a game loop can be implemented and used for virtually any game, on virtually any platform. MonoGame provides an implementation of the game loop for you to use. To be specific, here are the methods that MonoGame calls for your game as the game loop is running:
Startup Stage
Initialize
methodLoadContent
methodBeginRun
method (not overridden in the template)Update Stage
Update
methodDraw Stage
BeginDraw
method (not overridden in the template)Draw
methodEndDraw
method (not overridden in the template)Wait Stage
Cleanup Stage
OnExiting
method (not overridden in the template)EndRun
method (not overridden in the template)Dispose
method (not overridden in the template)UnloadContent
methodIt is also worth pointing out that MonoGame’s implementation of the game loop is a little trickier than this, in cases where you’re using a fixed time step (the default) and the game is running slowly. We’ll talk about the details of this in a couple of tutorials.