7 min read

Time Steps

Introduction

A couple of tutorials ago, I mentioned that MonoGame gave us quite a bit of control over how the game loop ran. Now that we’ve covered how the GameTime and TimeSpan classes work, we’re ready to dig into the details of how to control this.

Specifically, we want to talk about what your game will do when it runs into the waiting stage of the game loop. There are two general approaches to this. The first is to use a fixed time step for your game loop so that each cycle through the loop takes the same amount of time as the others. This is called a fixed time step , and it is what MonoGame uses by default.

The second alternative is to just blast through the waiting stage and jump right back into the next cycle without waiting. This means that each cycle through the loop happens as fast as it possibly can, but it also means that each cycle will take a different amount of time than the rest. This is called a variable time step ".

In this tutorial, we’ll look at a little more detail about fixed vs. variable time steps, and take a look at how to choose between the two in MonoGame. We’ll also look at some of the extra work that you’ll need to do in your game to use a variable time step. It requires a bit more work than what we’ve done in most of the other tutorials, but generally speaking, that extra work will pay off in the long run.

Fixed Time Step

If you are using fixed time steps, this means that every update and draw should happen regularly, with even spaces between updates or draws. This is what MonoGame defaults to.

Assuming all goes well, this means you can count on every update taking place the same amount of time apart from another. As we saw in the last tutorial, this can fall apart if your game is running slow. Interestingly, if it does, some strange things happen, which we’ll talk about in the next tutorial.

If things aren’t running slow, then after your game gets done with a single call to Update, and a single call to Draw, MonoGame will pause for the appropriate amount of time before starting the next update and draw stage. This regulates the frame rate.

By default, MonoGame tries to run at 60 frames per second.

Variable Time Step

The alternative to a fixed time step is a variable time step, where as soon as the update and draw stages of the game loop are complete, the next one starts immediately. The waiting stage is bypassed. This happens regardless of whether the game is running fast or slow. As such, with a variable time step, there’s no such thing as running slowly, and GameTime’s IsRunningSlowly will always be false. (I guess that makes it like a wizard.

It is interesting to note that while a fixed time step is the default, a variable time step is preferred. I’ll explain why, partly in the rest of this tutorial, and also partly in the next tutorial.

Configuring Time Steps in MonoGame

MonoGame gives us the control we need over time steps.

We have the ability to choose whether we’re running with a fixed time step or a variable time step. This is done by setting a value for your game’s IsFixedTimeStep property. This property is defined in the Game class and inherited by your main game class. Typically, the place to set this is in your game’s constructor, but it can be set anywhere in your class.

public Game1()
{
    graphics = new GraphicsDeviceManager(this);
    Content.RootDirectory = "Content";

    IsFixedTimeStep = false; // Setting this to true makes it fixed time step, false is variable time step.
}

If you’re running with a fixed time step (the default) you can indicate how long the time step should be. This is done by assigning a value to your game’s TargetElapsedTime property. This property is also defined in the Game class and inherited. This, too, can be assigned anywhere, but is usually done in the game’s constructor:

public Game1()
{
    graphics = new GraphicsDeviceManager(this);
    Content.RootDirectory = "Content";

    IsFixedTimeStep = true;
    TargetElapsedTime = TimeSpan.FromMilliseconds(20); // 20 milliseconds, or 50 FPS.
}

While you can call one of TimeSpan’s constructors, it makes sense to use one of the factory methods, like the FromMilliseconds one shown above, to create your desired time span, simply because it is more readable.

Accounting for Variable Time Steps

So let’s say you’ve got an object moving around in your game. Perhaps it’s a spaceship, and it’s moving at 14.8 units per second. (Yes, like a good 3D artist (which I’m not) I’m using generic units.)

When we were operating under the assumption of fixed time steps, we might assume a frame rate of 60 frames per second, and move the ship 14.8/60 = .24667 units every frame. (Though in all honesty, most people don’t think about this moving at 14.8 units per second, but .246667 units per update. They think of it as having a speed of 0.246667, not 14.8. For what it’s worth, that’s kind of the way we’ve done things in all of the tutorials so far, so it’s partly my fault.)

However, things get more complicated when you’re working with a variable time step. The truth is, the amount of time that has passed is unpredictable, so we can’t make any assumptions about how far something will move every frame.

To compensate for this, our updating code will end up being somewhat more complicated. For starters, we need to think about things in terms of movement per second (or hour, or millisecond, or whatever time unit you prefer). Movement per frame won’t be reliable anymore. So as you’re storing a player’s speed, instead of storing it in units per frame, or units per update, you’ll store it in units per second.

Secondly, in any particular update, we’ll want to move the object the correct amount, regardless of how long it has been. It may have been 1 millisecond, or it may have been 14 seconds. Though, honestly, both of those would be pretty extreme…

As an example, take a look at the following code. This first sample assumes a fixed time step with 60 frames per second.

// Defined as instance variables in a class.
private Vector2 position = new Vector2(0, 0); // The player's position
private Vector2 velocity = new Vector2(0.246667, 0); // The player's speed, assumed to be in units per frame.

// Meanwhile, in the //Update// method...
protected override void Update(GameTime gameTime)
{
    // The update method will likely contain much more than just this.
    position += velocity; // move the player forward a bit.
}

You can see that our velocity variable, which keeps track of the player’s speed, is measured in units per frame. When we update, we simply add the velocity to the position, moving the position the correct amount for a single frame.

To modify this to work for variable time step, we’d need to do the following:

// Defined as instance variables in a class.
private Vector2 position = new Vector2(0, 0); // The player's position
private Vector2 velocity = new Vector2(14.8, 0); // The player's speed, assumed to be in units per second.

// Meanwhile, in the //Update// method...
protected override void Update(GameTime gameTime)
{
    // Moves the player forward by an amount that is weighted by the amount of time that has passed.
    // Note that if you were using units per hour, or per millisecond, you would have used TotalHours or
    // TotalMilliseconds instead.
    position += velocity * gameTime.ElapsedGameTime.TotalSeconds;
}

You can see that this code is a bit more complicated. Everything you do will have to be factored against the amount of time that has passed. But on the other hand, this allows things to work much smoother, based on the frame rate that is actually achievable, given the setup of your game.

One final thing that is worth mentioning is that this type of code also works for a fixed time step. It won’t break anything if the time steps happen to be even, but this also works now if the time steps are uneven. Because of this, this is the way I recommend structuring your code, regardless of whether you’re using fixed or variable time steps.

Let me repeat that: this second approach for structuring your updating code, by factoring in the elapsed time and storing things in terms of units per second (or another time unit) instead of units per frame/update, is the preferred approach, and you’ll save yourself a lot of time if you begin using it right from when you begin developing a game. (If you’re already well on your way in a particular game, you can consider refactoring it to follow this pattern, or you can wait until your next game. But when you start the next game, use this approach.)

In the final tutorial about timing, we’ll cover some strange behaviors with fixed time steps in MonoGame that seem to catch nearly every beginner off-guard.