zhouqijie

子系统概述

每个游戏都需要一些底层支持系统,管理一些例行却关键的任务。例如启动终止引擎、存取文件系统、存取游戏资产、提供调试工具等。




1、子系统的启动和终止

游戏引擎由多个互相合作的子系统组成。引擎启动时必须依次配置和初始化每个子系统。每个子系统之间的依赖关系,隐含地决定了它们的启动和终止次序。

不能使用C++的静态初始化次序

C++在调用main()或者WinMain()函数之前,全局的静态对象已经被构造,而我们完全不能预知这些构造函数的调用顺序。在main()函数结束返回时,这些对象的析构函数调用顺序也是不可预知的。

游戏引擎的子系统通常都是一些全局的单例类对象,不能直接控制构造析构次序。所以C++静态初始化机制不适合用来初始化和终止游戏引擎子系统。(这类含有互相依赖全局对象的软件都不适合用C++静态初始化次序)

CRE:静态初始化次序是编译器来决定的。

如果给这些类写一个get方法,在首次调用get方法时在构造静态对象返回,即”按需构造”,可以实现按依赖次序构造。但是析构顺序仍然不能控制。

最优解决办法:架空构造和析构函数

构造函数和析构函数不做任何操作。定义单独的StartUp函数和ShutDown函数,在外部按次序调用这些函数。

OGRE引擎解决办法: Root单例

OGRE定义了一个单例Root。包含指向其他子系统对象的指针,并负责启动和终止这些子系统。

缺点:违背了开放封闭原则,扩展引擎必须修改这个Root类。




补充:单例模式的几种实现

Lazy Singleton:

//多线程隐患:多线程方式可能生成多个。(可以使用互斥锁)     
class Singleton
{
public:
    static Singleton * inst;
    static Singleton * GetInstance()
    {
        if(inst == NULL)
        {
            inst = new Singleton;
        }
        return inst; 
    }
}

Eager Singleton(使用静态初始化):

//在main函数之前初始化,所以没有线程安全的问题
class Singleton
{
public:
    static Singleton * inst;
    static Singleton * GetInstance(){ return inst;  }
}
Singleton * Singleton::inst = new Singleton();

Meyers Singleton

//Jianshu:Scott Meyers在《Effective C++》(Item 04)中的提出另一种更优雅的单例模式实现,使用local static对象(函数内的static对象)
//Jianshu:C++0x之后是该实现线程安全的。
class Singleton
{
public:
	static Singleton& GetInstance()
	{
		static Singleton instance;
		return instance;
	}
}

传统手动初始化方式

class Singleton
{
public:
    static Singleton * inst;
    Singleton(){ inst = this; }
}

void Init(){
    if(!Singleton::inst)
    {
        Singleton * newinst = new Singleton();
    }
}

int main()
{
    Init();
    //...
}

(END)