Keeping Track of Time
„I wish it need not have happened in my time,“ said Frodo. „So do I,“ said Gandalf, „and so do all who live to see such times. But that is not for them to decide. All we have to decide is what to do with the time that is given us.“
— J.R.R. Tolkien, The Fellowship of the Ring
A Theoretical Game Loop
As seen in the tutorial about real-world Windows applications, the game loop controls the overall flow of the entire
game. In gaming terminology, each iteration of the game loop is called a frame. Many games aim at running at
Our traditional, traditional in the sense that it comes from a world without multithreading, game loop looked like this:
Now what does a game do while it runs? Forgetting about Windows for a moment, an abstract pure game loop might read like this:
Thus, there are three things happening in each frame. This tutorial will give a brief overview over all three of them, but the details will be covered in later tutorials.
Acquiring the Input
Traditional input may come from various sources, such as the mouse, keyboard, or joystick. Furthermore, for online based games, another input source is any data received via the internet. Many modern mobile games also feature camera or GPS-information as input.
Updating the Game Universe
Updating the game world means appropriately updating every single object that is active in the current frame, based on the input acquired in the previous step. This might involve heavy mathematical computations to simulate a world with physical laws.
Generating the Output
After having updated the game world, the resulting output must be generated. The most computationally expensive output, in most cases, is the graphical representation of the game objects. Furthermore, audio, such as sound effects and music, but also dialogue, and occasionally, force feedback, or rumble effects, must be generated as well. For online based games, the data sent to the client, the player’s computer, would constitute additional output.
Imagine that a student is trying to walk over the University campus without being seen by any of the professors, as he has not refreshed his knowledge on algebraic geometry lately and is terribly afraid of being drawn into a conversation with them. What would such a game loop look like?
While the game is running, there are only two classes of objects that must be updated in each frame: the player object is updated based on acquired keyboard data and the professors are updated based on the position of the player, just imagine evil mathematicians trying to start a conversation about mathematics with everyone on sight (yes, a highly unrealistic scenario).
After having acquired the keyboard input, the game updates the position of the player. Should the position of the player now coincide with the position of a professor, the student faints. Else, the AI controlled professors adjust their position based on the new position of the player, with the goal of intercepting him.
Once the player and professor objects are updated, the rest of the campus must be updated as well (time of the day, trees, lights, …). Finally, the resulting output is drawn to the scene and the game plays some appropriate audio.
This is rather straightforward, and while many mobile and Indie games might still use this traditional game loop
pattern, modern games have evolved out of sheer necessity. With the amount of objects per scene ever-increasing and the
graphical detail becoming more and more photo-realistic, displaying
We will discuss an actual implementation of a single-threaded game loop in the next tutorial.
Time
In games, just as in real life, unfortunately, it is always important to keep track of time progression. For example, to
correctly animate game characters or to simulate the rules of physics, the time that passes between frames must be known
exactly. As games usually run at a high frame rate, it is essential that time measurement be very accurate, for example,
a game running at
One thing to consider is that the time passed in the real world might not coincide with the time passed in the game
world, for example, games might be paused (and surely time does not flow in a paused game universe). In other cases,
game designers might alter the flow of time deliberately, such as to implement slow-motion or, oppositely, to reduce the
length of a half-time in a football (real football, as in European football — not that it matters) game, as obviously
the players, or most of the players, do not want to sit through a
The High-Precision Timer implemented in this tutorial will make keeping track of time seem like child’s play.
Game Logic and Delta Time
While keeping track of the total elapsed time since the game was started, the time elapsed between two frames, the
so-called delta time, or
Imagine that in the game from above, the student is trying to just run over the campus in a straight horizontal line:
The problem with this absolute movement is that it depends on the fps the game is running on. If the game runs on
This new code works as desired, independent of the frames per second. At
This example shows that it is a good idea to update most game objects based on the elapsed time between two frames, especially when movement is involved, that is, if a translation, or a rotation, is applied to a game object. When higher mathematics or physics are evolved, a constant rate of update becomes even more important, as will be seen in the next tutorial.
The High-Precision Timer implemented later in this tutorial has the computation of
Another question to ponder is whether to limit the frames per second of a game, or to simply let it run as fast as possibly possible. Usually, allowing the game to run with as many frames as possible causes all kinds of problems. Most of those difficulties stem from the instability of numerical analysis, but that is the topic of the next tutorial.
A High-Precision Timer
Thankfully Windows offers a high-performance timer, or performance counter, out of the box, which appears to be significantly better than the C++11-timer available through std::chrono.
QueryPerformanceCounter
The QueryPerfomanceCounter retrieves the current value of the performance counter in counts, which is a high
resolution (
A LARGE_INTEGER represents a 64-bit signed integer value. In Visual Studio, those are equivalent to __int64 or long long int.
QueryPerformanceFrequency
The QueryPerfomanceFrequency retrieves the frequency of the performance counter, that is, the counts per second of the performance timer. This value is fixed at system boot and is consistent across all processors. Therefore, the frequency need only be queried once upon application initialization. Here is an example taken from the MSDN again:
Obviously, to get the seconds per count, it is enough to compute the reciprocal of the number of counts per second.
Thus, assuming
This is very straightforward, and this bit of information is already enough to create a high-precision timer for a game.
Note that both functions return
Time Between Two Frames
As discussed above, to animate game objects, it is very important to know the exact time that has elapsed between
frames. Let
Total Time
When trying to keep track of the total time
Now to get the total running time
- Inquiry when the game is paused:
where holds the count value at the time the game was paused last. - Inquiry when the game is running:
where is the count value at the moment of the inquiry.
Please note that all computations are done in
Here are two numerical examples.
-
Suppose that the game started at
, that the game was paused between and and again at . Now if the total time is requested while the game is still paused, the above formula yields: which makes sense, since the game ran for counts twice, between and as well as and . -
Suppose that the game started at
, that again the game was paused between and and additionally between and . Then , and if the total time is requested at , the above formula yields: which again makes sense as the game ran for counts between and , for counts between and , and for counts between and , which leads to a total running time of counts.
Timer Class
Now with the theory out of the way, it is time to implement the actual timer class.
The times, measured in counts, match those described above, with slightly different names.
Constructor
The constructor initializes most member variables to
Start, Reset and Stop
Those three functions very much behave like an ordinary stopwatch.
Start
The Timer::start function starts the timer (crazy stuff, I know). If the timer was already running, then nothing
happens. Else — if the game was paused — the function queries for the current time
Since at the next frame, the current frame will be the previous frame, the previous time is set to now. Then the last
time the game was paused is set to
If there was an error with the performance timer, the function returns a runtime_error.
Stop
The Timer::stop function stops the timer (yeah, even more crazy stuff!). If the timer is already stopped, then nothing happens. Else — if the timer is running — the function queries for the current time, stores the returned value as the time the game was paused and sets the isStopped flag to true.
If there was an error with the performance timer, the function returns a runtime_error.
Reset
The Timer::reset function resets the timer (okay, no more stupid jokes, I promise). It queries for the current time
and sets both the starting time of the application and the time of the previous frame to the returned value from the
query. Then it sets the time the game was last paused to
On encountering an error, the function returns a runtime_error.
Time between frames
To keep track of the elapsed time
Note that
To retrieve
Total Time
The Timer::getTotalTime function returns the total time elapsed since the start of the application, minus the total idle time, as discussed above.
To use the new timer in a game, the DirectXApp class is updated with members to hold a pointer to an instance of the timer class, the frames shown per second and the milliseconds elapsed per frame, as well as methods to calculate those frame statistics and to update the game objects based on the elapsed time.
Obviously, for now, the DirectXApp::update function does absolutely nothing, but it will definitely be useful later on.
To compute the frames per second, the DirectXApp::calculateFrameStats function updates the number of frames it has seen since the start of the game each time it is called during the game loop using a static variable. Using a second static variable, it keeps track of the elapsed time since it was last evoked. The lifetime of such static variables begins the first time they are encountered, and it only ends as the program terminates. Live long and prosper.
Now then, once per second, the fps variable can simply be set to the number of frames counted in this way, and the
milliseconds it took to render a frame, on average, is
Now when the game runs, the frame statistics are computed each frame and the game is updated based on the elapsed time:
Finally, the only thing left to do is to update the message procedure function in the windows class to actually start and stop the timer whenever the game is paused or unpaused, for now, that means, whenever the game window becomes inactive.
Exercises
Exercise 1
Which events must be updated in the message procedure of the windows class to appropriately start and stop the timer?
Exercise 2
What happens if in the DirectXApp::init() function the window is created before the timer?
Exercise 3
Change the source code in such a way, that the current fps will always be shown as caption of the window. (Hint: Use the SetWindowText function.)
Exercise 4
In the next tutorial, we will discuss timing in game loops. Can you figure out what is wrong with the game loop from this tutorial?
Putting It All Together
The sourcecode is available here.
And here is the log file after having minimized and restored the window:
In the next tutorial we will discuss different designs for game loops, in particular, we will show why the game loop used in this tutorial, which also features in many prominent books and other online tutorials, leads to very unstable and non-deterministic behaviour!
References
(in alphabetic order)
- Game Programming Algorithms and Techniques, by Sanjay Madhav
- Game Programming Patterns, by Robert Nystrom
- Introduction to 3D Game Programming with DirectX 11, by Frank D. Luna
- Microsoft Developer Network (MSDN)
- Tricks of the Windows Game Programming Gurus, by André LaMothe