在这个纷乱的世界中,还是有不少的高手,强人为迷路的小鸟指明方向。其中就包括引导我进入3D渲染世界大门的OGRE开发小组。其实,大家首先要知道的是:OGRE的本意,是一个以教学为目的进行的开源3D引擎项目。很多人对其推崇至极(当然,我也很喜欢OGRE,并对其开发者抱有崇高的敬意!),不过有些地方,OGRE很明显的在易懂和高效方面,选择了利于教学的方面……
加上没有合适的编辑器,以及诸如此类的种种原因,使用OGRE开发游戏的实例,还是到了2008~09年才慢慢出现。当然现在发展是迅猛的!!另外,著名的网络游戏,由搜狐开发的天龙八部就是基于它的(据小道消息说OGRE的开发组成员 谢某 就在天龙开发团队中!可惜我没有办法证实)
下面我先简单的介绍一下OGRE的框架,这个框架由两个文件引入:
ExampleApplication.h
ExampleFrameListener.h
这两个文件很重要,他们替我们做了很多底层的工作(其实是导入了很多有用的C++库),让我们来看看其中的 ExampleApplication 类,在这个类中,有几个比较重要的数据成员:
// 指向Root对象的指针
Root *mRoot;
// 指向程序中摄象机的指针
Camera* mCamera;
// 指向场景管理器的指针
SceneManager* mSceneMgr;
// 指向“帧监听器”的指针
FrameListener* mFrameListener;
// 指向渲染窗口的指针
RenderWindow* mWindow;我们如果要创建3D游戏,需要用到这的主要工具就是这几个了。Root负责所有程序的总调配,而Camera像词义一样负责作为摄像机,SceneManager用来管理游戏场景,FrameListener负责游戏的控制,最后RenderWindow负责将游戏“画”到屏幕(窗口)上。
OK,现在我们开工!这几个工具,可以让我们在屏幕上创建出像CS,魔兽世界那样的游戏画面。但是我总觉得OGRE这群家伙更加喜欢的是星球大战风格的东西,它们的标志就像兽人哦!这是因为其简写ogre的意思就是“食人魔”,/(^o^)/~挺搞的吧?
上面列举了数据成员,让我们来看一下 ExampleApplication 主要的成员函数:
virtual void go(void)
virtual bool setup(void)
就两个?对!就两个,两个就替我们做了很多事情,让我们看看他们是怎么写的:
其中go函数比较简单,内容如下:
virtual void go(void)
{
if (!setup())
return;
mRoot->startRendering();
}
实际上在其中调用了我们列举的另外一个函数 setup() ,他会负责进行判断,如果setup()函数执行正确,就会用mRoot调用startRendering()进行渲染,那setup()又做了什么呢?
virtual bool setup(void)
{
mRoot=new Root();
setupResources();
bool carryOn=configure();
if(!carryOn) return false;
chooseSceneManager();
createCamera();
createViewports();
TextureManager::getSingleton().setDefaultNumMipMaps(5);
createScene();
createFrameListener();
return true;
}
可以看出来,实际上setup做了很多重要的准备工作,让我们挨个看看。
首先,实例化了全局的mRoot,我们之前说过,mRoot和他的名字一样,是所有功能的起点,他负责调配所有功能。
之后我们执行setupResources(),他负责加载游戏需要用到的资源,例如,纹理,骨骼和网格。
接下里,执行configure(),他就是我们需要对ogre进行的配置,如果配置不成功(例如,用户选择了取消按钮),那么整个程序就退出。
继续往下执行,chooseSceneManager()实际上执行了
mSceneMgr = mRoot->getSceneManager(ST_GENERIC);
通过mRoot的getSceneManager()函数获得场景管理器并赋值给mSceneMgr,实际上场景管理器是非常重要的,他负责替我们进行场景的渲染,在这里的ST_GENERIC参数代表普通场景类型,其实他有四个选项可供选择,甚至他有一个场景类型枚举定义:
enum SceneType
{
ST_GENERIC; //普通场景
ST_EXTERIOR_CLOSE; //室外封闭场景
ST_EXTERIOR_FAR; //室外无限
ST_INTERIOR; //室内场景
}
如果我们只是做个简单的例子,选择第一项就好了。
接下来程序接着往下走,要执行createCamera()了,因为鉴于我们后面要做的工作,所以首先我们要创建一个摄像机来代表我们的眼睛。
然后程序执行createViewports(),这个函数的作用是创建一个视口,他是做什么的?因为游戏渲染并不是直接在窗口上,而是要在视口中渲染,所以要创建这个东西。
virtual void createViewports(void)
{
Viewport* vp=mWindow->addViewport(mCamera);
vp->setBackgroundColour(ColourValue(0,0,0));
}
侯捷老师说:源码之前,了无秘密!看代码吧!第一行,我们将我们之前创建的摄像机作为参数传递给视口创建函数,好让视口知道我们的眼睛到底在往哪里看,第二行,这个Viewport又绑定到了mWindow这个渲染窗口上来进行渲染。
后面程序执行到了createScene() 函数,他就是需要我们往场景中加入自己元素的函数了,如果你希望将一辆汽车或者飞机放到虚拟的世界里,就可以使用它了,如果你没有设置它,那么除了ogre在前面调用的 chooseSceneManager() 中创建的简单场景之外,一片荒芜。
其实到这里,整个游戏画面部分就已经结束了,画面已经渲染到了屏幕上,接下来我们要干什么呢?当然是让画面动起来,不然我们就只是费劲画了一幅画面而已,这又有什么稀罕的呢?
createFrameListener()就是来帮助我们实现与游戏的交互,我们可以看出他实际上是加入了一个监听器,来监视玩家到底做了什么操作,然后会根据玩家的操作来具体修改每一帧的画面。搞了这么一大通,上面的setup()如果成功,那么最后go()函数的最后一行,就会把我们设定好的程序进行渲染。当然还是由最主要的mRoot来干。
这里只是最简单的说一下这个东西的初步应用。想要知道得更多,下载OGRE代码,准备好开发环境,还有几包咖啡。接下来的几天跟着我一起进去遨游一下吧!(准备趁着国庆,多写几章整理这么久以来的思路和感悟)