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