最近花费了大把的时间,努力的学习如何与OGRE这个名声远播的巨兽成为好朋友。到目前为止,总算是能够称得上稍微和它混熟了一点。关于OGRE,这里提出几点我的学习笔记、目前观察到的个性特质,以及与它相处的心得感想。
首先,对于稍具经验的程序设计者来说,见到OGRE的第一眼印象,应该莫过于各种设计模式的广泛应用了。在OGRE的框架设计与系统实现层面中,使用了Abstract Factory、Factory Method、Singleton、lterator、Observer以及Manager等等许多注明的设计模式。说实在的,我还是头一次看到这么多不同种类的设计模式,能够同时应用在一个游戏绘图引擎之中,正好能够与以前所学的理论知识相互印证,学习起来非常爽快也很感动,这是令我大开眼界、大呼过瘾!
而其中,最显而易见的设计模式就是Singleton与Manager的结合使用。OGRE利用这两项模式的组合,制造出了一位一位各有擅长的【专业经理人】,使各个子系统所负的权责,都能够划分的非常清楚仔细。另外,利用Observer的概念,让使用者能够集成FrameListener类别,自定义进行绘图程序前后的相关处理程序,也是一个非常优秀的设计模式应用实例。
回想自己当初刚学习ogre时,额外看了许多书籍网站的资料,最后终于利用Singleton与Manager模式写作出几项游戏专案的子系统组件。结果深入认识了OGRE以后,才发现原来这些观念已经如此广泛运用在游戏引擎的设计框架之中。正所谓【他山之石,可以攻错】,对于程式设计者来说,吸引新知识以及学习别人的长处,的确是十分关键而不可或缺的能力啊。
如果以程序语言的角度观察,OGRE可以说是一个拜拜纯天然的C++绘图引擎。与其它多数的游戏引擎绘图引擎不同,OGRE身上没有旧时代一流下来的沉重包袱,或者为了从C语言转换至C++的【怪味道】等等过期的设计与写作方法。举例来说,OGRE对于namespace语法的全面使用,就是一个非常正确而且便利的做法:
- namespace Ogre
- {
- class SceneManager
- {
- // INSERT useful code here.
- };
- }
从C语言时代逐渐演变的琢磨而来引擎的前辈们,由于担心发生命名冲突(Nameing Confliction)的问题,所以一般常见的做法,就是在原始码的类别与结构名称里,加上一个自定的前缀词(Prefix),形成OgreSceneManager、OgreSceneNode、OgreRenderSystem以及OgreXXX等等的类别名称。只要如OGRE般善用C++语言中的namespace语法,就能够拜托这些奇怪又累赘的前缀词,更加能够良好的切割类别名称的定义空间。
而在OGRE复杂庞大的内心世界中,除了独立运作的Root类别以外,其他的类别们主要可以分成场景管理、资源管理以及绘图系统三大类:
- Scene Management:用来管理游戏中的所有物件的外显行为,相关的类别有SceneManager、SceneNode、MovableObject等等。
- Resource Management:负责创建、分配于管理例如Font、Mesh、Texture、Material等等游戏中与实体档案相关的资料,相关的类别有ResourceManager、ArchiveManager、MaterialManager、TextureManager等等。
- Rendering:掌握绘图程序系统,相关类别有Renderable、RenderWindow、RenderSystem、RenderQueue等等。
然而,如果OGRE中仅有这三项系统,其实难以满足不同专案的不同需求,达到引擎本身应有的广泛应用性与延展性。在不需要修改源代码的前提下,如何让使用者达到最大的自由度,甚至能够自由扩充原有的引擎功能?插件(Plugin)系统,就是OGRE给程序设计者的答案。有了场景、资源与绘图系统三大基础套件,在加上非常具有弹性的插件架构,就形成了OGRE之中最关键的黄金四角。而OGRE的心脏命脉,就在于万能是SceneManager类中!SceneManager像是一座巨大的物件工厂,掌握了OGRE中多数物件与资源的生杀大权,能够生产SceneNode、Material、Light以及Animation等等许多类别物件并且加以管理。
对于反面的第一眼观察,我比较难以理解的是,为什么OGRE明明是以C++语言撰写的引擎,缺要使用Java式的命名规则,使所有的成员函数名称,都以首字字母小写的形式存在,例如个人getAnimation()与setAnimation()而非C++中常见的GetAnimation()与SetAnimation()形式。对于使用者来说,在配合自己所撰写的程序代码时,需要特别注意命名管理与编写风格上的统一。
另外,OGRE对于例外处理(Exception Handling)机制的大量使用,令我产生不少使用上的疑惑。举个例子来说,即使是在很寻常的getAnimation()函数中,只要使用者传入了目前不存在其中的字符串名称,OGRE就会立即抛出异常:
- String kAnimationName = "char_idle"; // 假設 char_idle 不存在於 mSceneMgr 中
- Animation* pkAnim = mSceneMgr->getAnimation(kAnimationName); // 這裡會拋出例外狀況!
- if (pkAnim == NULL) {
- // Create animation and/or report error
- }
所以为了正确的取得Animation物件,必须要使用这样的做法:
- Animation* pkAnim = NULL;
- String kAnimationName = "char_idle";
- // 先檢查存在性(不會拋出例外狀況)
- if (mSceneMgr->hasAnimation(kAnimationName)) {
- // 確認存在就可以取值
- pkAnim = mSceneMgr->getAnimation(kAnimationName);
- }
原来可以直接检查getAnimation()返回值是否为NULL的方式,在OGRE中需要多出一个检查步骤,对于许多程序员来说,可能一时也难以习惯这样的做法。而在OGRE中处处可见、随手可抛出的例外状况的情境之下,很容易出现以下这样非常糟糕的代码编写方式“:
- try {
- // Do something might throw exceptions!
- Animation* pkAnim = mSceneMgr->getAnimation(kAnimationName);
- }
- catch (...) {
- // 忽略,不做任何處理!
- }
为了能够顺利创建出Animation物件,不产生例外状况,程序员甚至会使用以下的做法:
- Animation* pkAnim = NULL;
- float fLength = 1.0f;
- try {
- // 如果 kAnimationName 不存在,就會拋出例外而進入 catch 區塊
- pkAnim = mSceneMgr->getAnimation(kAnimationName);
- }
- catch (...) {
- // 在 catch 區塊中創建所需的 Animation 物件
- pkAnim = mSceneMgr->createAnimation(kAnimationName, fLength);
- }
而至目前为止,在与OGRE的相处过程中,我所遭遇到的最重大的问题,就是资源物件名称的唯一性。在OGRE的资源管理系统中,几乎所有的资源都是以string类型作为索引键值,存放在std::map结构中。这种设计方法的有点,在于程序员能够很有效率的检查资源的存在性并且进行存取动作。问题是,资源管理系统完全不允许重复索引键值的物件产生。当程序员手动建立物件时,能够轻易的避免键值重负的问题;然而,当资源物件是由实体档案读取进来的时候,只要载入同一个档案两次以上,OGRE就会毫不留情的发出哀嚎,强制中断程序运行。
追根究底,原因是在于目前的OGRE并不存在完善的资源物件Clone机制,这个缺陷对于欲建立大型游戏专案的程序员来说,应该是一个十分头痛难缠的问题;目前只能够由引擎的使用者,在建立资源午间之前,自行传入独一无二并且保证绝不重复的Unique String值,以确保程序执行的正确性。最近在OGRE官网论坛中,看到有人提出了这项资源索引键值冲突的问题,OGRE坐着的解决方案是利用一个Callback函数,让使用者可以自订遇到键值名称冲突时的处理程序,相关的代码应该会于下个OGRE主要的版本更新中给出。
最后,在文件方面,OGRE官方的正式教学文件太过于贫瘠,SDK中附上的Manual里竟然只有寥寥无几的几页内容。反而是在官网Wiki上的教学文章内容比较丰富,也有许多Step by Step的主题教学与实际的源代码范例,使初学者能够学习到一个比较系统化的知识。值得安慰的是,OGRE源代码的注释内容写的非常不错!只要参考API Reference文件的说明,在单一类别的使用上就不会遇到太大的问题,同时也能够自行下载到源代码,仔细的研究探索其中的编写细节。
至于在实体书籍方面,目前仅有《Pro OGRE 3D Programming》唯一一本。这本书在OGRE的基本概念与设计理念概述,都有相当详尽的介绍,可惜在进阶的主题上着墨不深,但仍然是一本OGRE必读的入门书籍。另外在中国的确,有许多游戏都是使用OGRE开发完成的,因此对于OGRE的研究不少,在网络上也能够找到非常多关于OGRE的资料。对于有志使用OGRE开发游戏的程序员来说,多多浏览官方发Forums也是必不可少的日常工作。
总而言之,比起我之前曾经使用过的商业引擎,OGRE可以说是一个设计框架很完善的新时代绘图引擎,但是在许多使用层面上的细节,仍然有待更进一步的更新与加强。即使某些部分的功能还不够完善,但就算是对于自行编写绘图引擎的程序员来说,OGRE也绝对是一个非常出色的参考典范。期待新的OGRE版本出来以后,能有更具突破性的长足进步!
以上,是我目前对于OGRE的一点使用经验与心得感想。如有其它想法,也欢迎提出讨论。