Every game engine requires some low-level support systems that manage
mundane but crucial tasks, such as starting up and shutting down the engine, configuring engine and game features, managing the engine’s memory
usage, handling access to file system(s), providing access to the wide range
of heterogeneous asset types used by the game (meshes, textures, animations,
audio, etc.), and providing debugging tools for use by the game development
team. This chapter will focus on the lowest-level support systems found in
most game engines. In the chapters that follow, we will explore some of the
larger core systems, including resource management, human interface devices
and in-game debugging tools.
5.1 Subsystem Start-Up and Shut-Down
A game engine is a complex piece of software consisting of many interacting
subsystems. When the engine first starts up, each subsystem must be configured and initialized in a specific order. Interdependencies between subsystems implicitly define the order in which they must be started—i.e., if subsystem B depends on subsystem A, then A will need to be started up before B
can be initialized. Shut-down typically occurs in the reverse order, so B would
shut down first, followed by A.
231
232 5. Engine Support Systems
5.1.1 C++ Static Initialization Order (or Lack Thereof)
Since the programming language used in most modern game engines is C++,
we should briefly consider whether C++’s native start-up and shut-down semantics can be leveraged in order to start up and shut down our engine’s subsystems. In C++, global and static objects are constructed before the program’s
entry point (main(), or WinMain() under Windows) is called. However,
these constructors are called in a totally unpredictable order. The destructors
of global and static class instances are called after main() (or WinMain())
returns, and once again they are called in an unpredictable order. Clearly this
behavior is not desirable for initializing and shutting down the subsystems
of a game engine, or indeed any software system that has interdependencies
between its global objects.
This is somewhat unfortunate, because a common design pattern for implementing major subsystems such as the ones that make up a game engine is
to define a singleton class (often called a manager) for each subsystem. If C++
gave us more control over the order in which global and static class instances
were constructed and destroyed, we could define our singleton instances as
globals, without the need for dynamic memory allocation. For example, we
could write:
class RenderManager
{
public:
RenderManager()
{
// start up the manager...
}
~RenderManager()
{
// shut down the manager...
}
// ...
};
// singleton instance
static RenderManager gRenderManager;
Alas, with no way to directly control construction and destruction order, this
approach won’t work.
0 Comments
Please Comment for any further query :