GameTime
: A Game’s HeartbeatIn the previous tutorial, we took a look at the game loop.
This pattern is incredibly versatile, and nearly every game uses some form of it.
In this tutorial, we’ll dig into it a little bit more, and look at the GameTime
object that we’re given in every call to Update
or Draw
.
This class gives us insight into what’s going on in the game loop.
In a sense, this is like the game’s heartbeat, pulsing many times a second, driving your game forward.
In this tutorial, we’ll focus solely on understanding the GameTime
class, as well as the TimeSpan
class that it uses.
In the upcoming tutorials, we’ll look at how to configure the game loop to our liking.
In previous tutorials, we’ve generally ignored the gameTime
parameter.
We’ve assumed (correctly, for the most part) that each frame (cycle through the game loop) will take the same amount of time.
By default, MonoGame will regulate the game loop to try to enforce this, and it is able to do so, as long as the game isn’t running slowly.
But knowing how to use the GameTime
object that we’re given will be an important step in managing the frame rate of the game loop, which we’ll cover in more depth in the next tutorial.
By default, MonoGame will try to maintain a frame rate of 60 frames per second, making each frame take 0.0166667 seconds.
Again, in the next tutorial, we’ll look at how to configure this yourself, but for now, our focus is on understanding the GameTime
object.
GameTime
ClassIf you look at your Update
method and Draw
method, you will see that you’ve been given an instance of the GameTime
class in both cases.
The GameTime
class gives you three properties of interest: TotalGameTime
, ElapsedGameTime
, and IsRunningSlowly
.
Both TotalGameTime
and ElapsedGameTime
are object of type TimeSpan
, which is actually a part of the Base Class Library.
It isn’t unique to MonoGame.
You can use it in any C# application.
These two contain all of the interesting timing information that you may want to know about.
We’ll break down TimeSpan
objects in a second.
TotalGameTime
tells you how long your game has been running for since it was started.
This continues to run until the player quits the game.
ElapsedGameTime
, on the other hand, tells you how long it has been since the last Update
or Draw
(depending on if it is in Update
or Draw
).
In theory, this is how long a complete cycle through the game loop has taken.
Interestingly, there’s a couple of gotchas with this, but that’s a topic for an upcoming tutorial, not for right now.
For the moment, we’ll assume that your game is running fast enough that there are no delays, and that ElapsedGameTime
reflects how long it has been since the last update.
Speaking of running slowly, the third property of GameTime
, IsRunningSlowly
, is a boolean value indicating whether the game is running slower than it was told to run.
It’s kind of crazy, but even if IsRunningSlowly
is true
, and the game is running slowly, you won’t necessarily see ElapsedGameTime
slow down.
This is a little perplexing, but stick with me for a couple more tutorials, and I’ll explain why.
TimeSpan
StructBoth TotalGameTime
and ElapsedGameTime
are instances of the TimeSpan
struct.
This type gives you a couple of ways of accessing the timing information it contains, and it is important to understand these two distinctions.
If you look at the properties that TimeSpan
provides, you’ll see two matching sets of properties: Days
, Hours
, Minutes
, Seconds
, and Milliseconds
, as well as TotalDays
, TotalHours
, TotalMinutes
, TotalSeconds
, and TotalMilliseconds
.
These properties that start with Total
, combined with the word Total
in TotalGameTime
, is what leads to confusion in knowing what variables to use.
So it’s important to keep the two separated in your mind.
Always remember: GameTime
has TotalGameTime
and ElapsedGameTime
, both of which have their own set of properties that start with Total
, and a set that does not.
Now going back to these two sets, let’s take a minute and describe the difference between the set that starts with Total
, and the set that does not.
To help explain the differences, let’s say that you have a TimeSpan
object that represents a time span of 2 minutes and 17.3 seconds.
(Which could be a total game time or just an elapsed game time.
It doesn’t matter at this point.)
Days
, Hours
, Minutes
, Seconds
, and Milliseconds
are designed to be used together, to represent the whole time.
For this time span, Days
and Hours
would have a value of 0, Minutes
would have a value of 2, Seconds
would have a value of 17, and Milliseconds
would have a value of 300.
On the other hand, the set that begins with Total
represent the total length of the time span in whatever unit you choose.
So TotalDays
will be 0.001589, since that time span is equal to that many total days, TotalHours
will be 0.038138, TotalMinutes
will be 2.28833, TotalSeconds
will be 137.3, and TotalMilliseconds
will be 137300.
So here’s the key.
Generally speaking, you’ll use the set without Total
to build timing information that you want to show to the user, while the ones with Total
are used to do actual calculations for your game.
And you’ll use TotalGameTime
when you’re interested in the overall running time of the game, and ElapsedGameTime
when you’re interested in how long it has been since the last update.
When all is said and done, in a real game, you’ll use gameTime.ElapsedGameTime.Seconds
frequently (or gameTime.ElapsedGameTime.Minutes
, etc.) and gameTime.TotalGameTime
or gameTime.ElapsedGameTime.Seconds
very rarely.