The system engine component is responsible for a large variety of tasks. Its primary responsibilities are that of timing and multi-threading, but it is also responsible for keeping track of other settings such as what language the game is currently running in and determining when the game should begin the exit process. The sections that follow explain the operation of the various sub-systems in the system engine.
|Singleton Object Name||SystemManager|
|Main Header File||system.h|
|Purpose||Handles timing, process threads, and other miscellaneous tasks|
As with any game, timing is a very important but often overlooked aspect. The Allacrost engine uses time-based updates, which means that the state of the game is updated depending on how much time passed between the last update. The system engine uses the SDL timing system to keep a representation of time, specifically using the SDL_GetTicks() function, which returns the number of milliseconds that have expired since SDL was initialized. Therefore, the finest granularity of time visible to Allacrost is in terms of milliseconds. Just before the main loop in the Allacrost engine begins, the following function is called in main.cpp.
This call prepares the internal update timer by setting it to the current value returned by SDL_GetTicks(). The update timer is nothing more than a pair of two unsigned integer variables. The first variable (called _last_update) records the last value returned by SDL_GetTicks(). The second variable (called _update_time) records the difference between the first variable and the current value returned by SDL_GetTicks(), On each iteration through the main game loop, the following call is made.
When this call is made, the two timer variables are updated. _last_update is set to the current value returned by SDL_GetTicks() and _update_time is set to the difference between the new and previous values of _last_update. The value of _update_time is what the rest of the code in the game engine is interested in, since this records the number of milliseconds that have expired since the last timing update was made. Throughout the Allacrost code, you'll find many references to this time in the form of the member access function below.
SystemManager->GetUpdateTime() // returns a const uint32 with the number of milliseconds that expired from the last update
The play time is the number of hours, minutes, and seconds that have expired since the player started their game. The timing subsystem also keeps this information up-to-date as it continually updates the timing sub-system.
Timed functions are best explained by an example. Suppose the player takes a screenshot and we desire to display the message "Screenshot saved" at the top left corner of the screen for 3 seconds. In order to do this, we'd have to continually check:
- Did the player take a screenshot just now?
- Did the player take a screenshot in the last 3 seconds?
- How long has it been since the user took a screenshot, if they did so within the last 3 seconds?
It is rather silly to check for these conditions on every iteration of the game loop, since the occurence of taking screenshots is likely to be rare. Instead, what we wish to do is something like the following:
- When it is detected that the player has given the command to take a screenshot, print a message at the top left of the screen for 3 seconds and then self-terminate when that time has expired.
This is the concept behind a timed function. It is a piece of code that is allowed to execute for a specified amount of time before it expires. Timed functions are very useful in a variety of places. Some examples are:
- Diagnostic messages to display on the screen
- Certain effects in battle, such as poison or sleep
- Synchronized timing of events in a scene
However, timed functions are not as nice as they may seem at first. Certain functions (especially drawing functions) are order-dependent, meaning that they have to be executed at a specific time in order to guarantee a correct result.