子系统的启动与终止
游戏引擎是复杂软件,有多个相互合作的子系统结合而成。当引擎启动的时候,必须依次配置及初始化每个子系统。各子系统间相互形成的依赖关系,隐含地定义了每个子系统所需的启动次序。
朴素方法
明确地为各单例管理器类定义启动和终止函数,这些函数取代构建和析构函数,让构建和析构函数完全不做任何事情。
class RenderManager { public: RenderManager() {} ~RenderManager() {} void startUp() {} void shutDown() {} }
class PhysicsManager {} class AnimationManager {} class MemoryManager {} class FileSystemManager {}
RenderManager gRenderManager; PhysicsManager gPhysicsManager; AnimationManager gAnimationManager; MemoryManager gMemoryManager; FileSystemManager gFileSystemManager;
int main(int argc, const char* argv) { gMemoryManager.startup(); gFileSystemManager.startup(); gAnimationManager.startup(); gPhysicsManager.startup();
gSimulationManager.run(); gPhysicsManager.shutDown(); gAnimationManager.shutDown(); gFileSystemManager.shutDown(); gMemoryManager.shutDown(); }
|
此方法总是优于其他方法,因为:
- 此方法简单又容易实现。
- 此方法是明确的,看看代码就能立刻得知启动次序。
- 此方法容易调试以及维护,若某子系统启动时机不够早或者过早,只需移动一行代码。
内存管理
内存对效能的影响有两个方面。
- 以
malloc()
或 C++ 的全局 new
运算符进行动态内存分配,是非常慢的操作。要提升效能,最佳方法是尽量避免动态分配内存,不然也可利用自制的内存分配器来大大减低分配成本。
- 许多时候在现代 CPU
上,软件的效能受其内存访问模式主宰。将数据置于细小连续的内存块,相比把数据分散至广阔的内存地址,CPU
对前者的操作会高效很多。
堆栈分配器
用 malloc()
,全局
new
,或者是全局字节数组分配一大块连续内存,然后用指针指向堆栈的顶端,指针以下的内容是已分配的,以上的是未分配的,对于每次分配请求,将指针往上移动请求所需的字节数量即可。