All game engines need the support of the underlying systems in order to do trivial but necessary things. Loading and unloading subsystems, configuring engine and game features, managing engine memory, access to various resources used in the game, debugging tools for game development, etc. So from now on, let’s take a look at some of the underlying systems in game engines and try to implement them.


Subsystem loading and unloading

A game engine is made up of a bunch of interacting systems, so when the engine starts, the subsystems must be configured and initialized in a certain order (because of the dependencies between subsystems). Dependencies between subsystems determine this particular loading order. Let’s take a look at how to solve this problem.

C++

Since we have decided to use C++ as our language, let’s first examine whether C++ features can solve this loading order problem for us.

In Windows, objects of global and static types in C++ are constructed before the main() call, but they are constructed in an unpredictable order, and their corresponding destructors are called in an unpredictable order after main().

class RenderManager
{
public:
    RenderManager() {
        //start up the manager
    }

    ~RenderManager() {
        //shut down the manager
    }
};
//singleton instance
static RenderManager gRenderManager;

soglobal,staticno


Build by command

Since the problem cannot be solved by using C++ features directly, we have to go around in circles. As we know, a static variable in a function is never constructed before main() and the function in which it is called. So we can control our global singleton by declaring a static variable in the function.

To improve the previous code!

class RenderManager
{
public:
    static RenderManager& get() {
        static RenderManager sSingleton;
        return sSingleton;
    }

    RenderManager() {
        //start up the manager
    }

    ~RenderManager() {
        //shut down the manager
    }
};

Will it be all right this time? If we can control when the get() function is run, we can control the construction order. But the question is, how do you control the order of destructions? And it’s hard to predict exactly when RenderManager will be constructed.

So that’s not very good

A simple way to work

We still stick to the subsystem singleton pattern, which allows us to explicitly define the start-up and shut-down functions to load each subsystem singleton instead of the constructors and destructors. At this point, we can explicitly specify when submodules are constructed and destructed in the main() function, so that they are loaded and unloaded in a certain order. The constructors and destructors do nothing.

class RenderManager
{
public:
    RenderManager() {
        //do nothing
    }

    ~RenderManager() {
        //do nothing
    }

    void StartUp() {
        //start up the manager
    }

    void ShutDown() {
        //shut down the manager
    }
};
RenderManager gRenderManager; PhysicsManager gPhysicsManager; / /... int main(int argc, const char* argv) { gRenderManager.StartUp(); / /... gPhysicsManager.StartUp(); / /... gPhysicsManager.ShutDown(); / /... gRenderManager.ShutDown(); return 0; }