17.1 UI库
Ogre中提供给我们的二维处理能力还不足以方便我们构建出一套完美的UI界面,而目前很多成熟的GUI库都可以很好的整合到Ogre3D中,这样很大程度上节省了我们定制UI界面的时间,每一种GUI库都有它存在的理由,我们不可能很明确的说那一个最好,或者说哪一个最适合用于Ogre3D中,我们只能根据自己的所求在适当时候选择适合自己所需的GUI库。
目前存在着很多流行的GUI库,下面我们来简单了解一下:
(1)CEGUI
CEGUI是一个免费的GUI库,它基于LGPL协议,使用C++实现,采用完全面向对象的方式设计。在作者写这本书期间,CEGUI的版本已经由0.7.5过渡到了0.7.6。CEGUI开发者的目的是希望能够让游戏开发人员从繁琐的GUI实现细节中抽身出来,以便有更多的开发时间可以放在游戏性上。CEGUI也是官方Ogre官方推荐使用的界面库,Ogre1.6及以前的版本都是内置支持的。使用它的商业游戏业非常多,比如天龙八部、火炬之光、仙剑四等。
(2)Ogre SDKTray
从Ogre1.7开始,Ogre3D就不再内置支持CEGUI了,转而使用Ogre自己的SDKTray,这也是为什么我们使用1.6以前的版本不需要配置CEGUI,而使用1.7之后的版本还需要配置CEGUI环境的原因。SDKTray是在Ogre的Overlay和material的基础上实现的,很容易理解和使用,不过目前来说它还是个半成品,无法应用到商业游戏中。虽然Ogre3D不再内置CEGUI了,不过Ogre3D和CEGUI的整合很简单,这不是我们学习CEGUI的障碍。
(3)QuickGUI
QuickGUI的最新版本是10.01,专为Ogre3D写的UI库,它被设计一个易于使用、有效且强大的GUI库,QuickGUI的更新速度很快,每个几个月就会发布一个新的版本。
(4)Hikari
Hikari也是完全免费的,而且允许用户使用目前广泛流行且方便使用的Flash来制作界面,它通过Flash.ocx将swf渲染成Ogre的Texture,然后我们就可以任意操纵这个Texture了。另外,Hikari支持Flash的所有版本,不存在兼容性问题,由于Flash本身就支持多国语言(包括中文),因此使用Hikari来实现中文的显示自然是轻而易举的一件事,不过Hikari效率比较低,而且它使用的是Flash的 ocx,先将动画内容渲染到DC上,然后拷到Texture中,所以很运行速度会比较慢,而且将动画内容渲染到DC上这一步是造成其运行速度比较慢的主要原因,这使得Flash优秀的脏矩形优势无法发挥,根本无法应用到商业游戏中。
(5)Scaleform
Scaleform跟Hikari有类似之处,都是用Flash来制作游戏界面,不过它的效率要比Hikari高很多,可以说是游戏界面的最强工具了,而且它对亚洲语言显示和输入的支持都堪称完美。据统计,有超过600多款的游戏使用Scaleform制作界面,比如:StarCraft II,Crysis,Fable II,CivilizationIV等。唯一遗憾的是, Scaleform是收费的,且费用很高。
(6)MyGUI
MyGUI的最新版本是3.2.0,它的优点是:高效、轻便、灵活等;它的缺点是:注释和文档不是很完善,相关资料比较缺乏,效果有时不太理想,目前还没有太多商业游戏使用这个库,但是MyGUI目前越来越受到广大游戏开发者的关注,相信不久后也会有良好的发展。
了解了这么多UI库,其实就像笔者前面说的,每一种库都有各自的优缺点,我们在选择的时候只有选择适合自己的才是对的,由于我们本书是一本介绍Ogre3D知识的书,所以不可能过多的把重点放到每个GUI的具体讲解上,因为上面我们提到的每一款GUI库都完全可以当做一本书来写,所以如果读者朋友想深入了解某一款GUI,还需多搜集一些它们相关的资料。
由于Ogre3D和CEGUI的结合已经算是老搭档了,而且CEGUI在各方面的使用不管是从个人还是商业引擎方面都已经比较成熟,学习的人比较多,而且读者朋友在学习的时候遇到麻烦也会有很多热心朋友的帮助,所以本书主要采用CEGUI作为我们的界面库。本章我们主要讲解一些CEGUI的知识,让大家对CEGUI有个逐步的了解,希望在本章结束的时候读者朋友能做出一些比较复杂的界面来。
17.2 CEGUI环境的配置
在Ogre中使用CEGUI的第一步当然是需要我们首先配置CEGUI环境。
笔者提示: CEGUI下载地址: https://sourceforge.net/projects/crayzedsgui/files/CEGUI%20Mk-2/0.7.5/CEGUI-0.7.5.zip/download CEGUI依赖库下载地址: |
解压CEGUI压缩文件到任意目录,如我们这里:
然后进行CEGUI依赖库的添加,添加到如下目录:
然后进入到如下目录:
用文本编辑器(为了更好的显示,建议用UltraEdit等功能比较强的文本编辑器打开)打开目录下的config.lua,找到下面几行:
改为:(注意:具体目录根据自己Ogre安装目录确定)
找到CEGUI_OLD_OIS_API变量,设置为false(我们这里用的这个版本默认已经是false了)
找到OGRE_RENDERER变量,设置为true
找到SAMPLES_OGRE变量,设置为true
如下所示:
保存此文件!
然后进入目录D:\CEGUI-0.7.5\projects\premake,运行build_vs2008.bat(或对应的其他版本),你将看到CEGUI.sln,继续运行build_samples_vs2008.bat(或对应的其他版本),你将看到CEGUISamples.sln;
然后向我们以前添加OGRE_HOME环境变量一样,这里我们可以添加一个环境变量CEGUI_HOME用来代替CEGUI根目录。
打开VS2008,选择菜单栏:工具---选项(当然,我们也可以在每个工程属性页中进行包含路径的添加,具体请参看Ogre环境配置一节)
在 包含文件 一项中,配置CEGUI相关的如下一些路径:
(1)$(CEGUI_HOME)\cegui\include
(2)$(CEGUI_HOME)\Samples\common\include
(3)$(CEGUI_HOME)\dependencies\include
同理,在 库文件 一项中,配置如下:
(1)$(CEGUI_HOME)\lib
(2)$(CEGUI_HOME)\dependencies\lib\dynamic
然后到如下目录(根据自己CEGUI安装目录而定)打开CEGUI.sln选择Debug和Release两种模型,分别编译
编译成功后,再对CEGUI Samples.sln的Debug和Release分别编译,编译成功后,可以查看CEGUI.sln里面的实例,可以选取其中一个工程,点击右键设为启动项目,如下所示:
然后运行,正常情况下会弹出这样的对话框:解决方法如下:
到D:\CEGUI-0.7.5\dependencies\bin(根据自己CEGUI的安装目录决定) 这个目录会看到如下一些文件:
为了使用方便,可以把这个目录下的所有dll文件拷贝到:D:\CEGUI-0.7.5\bin(根据自己CEGUI的安装目录决定)这个目录下,同时到D:\ogre\ogre_src_v1-7-2\bin\debug和D:\ogre\ogre_src_v1-7-2\bin\release(根据自己Ogre的安装目录决定)两个这个目录下,分别拷贝:OgreMain_d.dll和OIS_d.dll以及OgreMain.dll和OIS.dll,到D:\CEGUI-0.7.5\bin(根据自己CEGUI的安装目录决定)这个目录下,然后再次运行示例工程,可以看到如下启动界面,选择OpenGLRenderer或者DirectX运行:
到了这里还是会弹出无法运行的对话框,如下:
我们需要到解决方案一栏全部选择所有工程,点击右键----属性:
然后选择配置属性----调试:
在工作目录一栏中选择下拉菜单,选择浏览,找到CEGUI对应的datafiles文件夹:
点击确定,重新运行,如果按照上述方式,应该可以成功运行了~~~~
效果如下:
这样总算是把CEGUI也算是配置好了。
17.2 CEGUI简介及入门
前面我们已经简单介绍过CEGUI,它的功能是非常强大的,而且使用也非常的灵活,可以和脚本配合。可以通过配置文件自定义窗口外观。通过布局文件实现窗口布局等等特性,使得游戏的界面开发更加方便。另外,和Ogre3D一样,CEGUI的开源特性让我们在开发过程中可以随时查看源代码,更好的了解整个软件的工作流程。
CEGUI的担当的职责是UI功能,它在软件中提供一些用户界面的组件,如按钮、列表框、文本框、滚动条等等。
CEGUI设计了许多接口,程序可以通过实现接口来为CEGUI提供服务。比如说渲染接口定义好了以后,就使得CEGUI与具体的渲染平台无关。不管是OpenGL还是Direct3D,无论是Windows还是Linux,只要在那个平台上实现了CEGUI的渲染接口。当然CEGUI库也必须在那个平台下编译才行。
本章假设你已经编译好了CEGUI库,如果读者还没有成功编译CEGUI,请参照CEGUI配置的相关部分编译配置成功再继续往下学习。好了,下面我们就通过一个例子来看一下如何编写CEGUI程序。
第一步,我们新建一个Win32控制台应用程序的空项目,名叫CEGUITest(名字任意);
第二步,新建一个cpp文件,名叫Main(名字任意);
记得我们在写Ogre3D程序的时候为了方便,我们经常继承一个ExampleApplication的类,CEGUI也一样,为我们提供了一个这样的类,省去了我们不少工作,不过如果读者朋友想一探究竟,看看这个类到底为我们做了一些什么工作的话,就需要查看实现代码了;
第三步,加入我们需要的头文件:
#include "CEGuiSample.h" #include "CEGUI.h" |
第四步,加入对应的库文件:CEGUISampleHelper.libCEGUIBase.lib,并设置工作目录为(你的CEGUI根目录)\bin:
笔者注: 如果你在编译程序之前,请确保你正确配置了CEGUI的各个包含目录和库目录,否则会遇到找不到头文件或者其它类似的错误提示,具体配置方法请参看CEGUI配置一节。 |
第五步,定义一个新的类MyFirstWindowExample,让它继承自CEGuiSample类:
class MyFirstWindowSample : public CEGuiSample { |
第六步,重新CEGuiSample中的两个方法:
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 21 |
bool initialiseSample() { using namespace CEGUI; SchemeManager::getSingleton().create("TaharezLook.scheme"); System::getSingleton().setDefaultMouseCursor("TaharezLook","MouseArrow"); WindowManager& winMgr = WindowManager::getSingleton(); DefaultWindow* root = (DefaultWindow*)winMgr.createWindow("DefaultWindow","Root"); System::getSingleton().setGUISheet(root); FrameWindow* wnd = (FrameWindow*)winMgr.createWindow("TaharezLook/FrameWindow","Demo Window"); root->addChildWindow(wnd); wnd->setPosition(UVector2(UDim(0.25f,0),UDim( 0.25f,0))); wnd->setSize(UVector2(UDim(0.5f,0),UDim( 0.5f,0))); wnd->setText("Hello IMedia!"); return true; } void cleanupSample() { } |
第七步,编写main函数:
void main() { MyFirstWindowSample app; app.run(); } |
到此,我们的所有工作都做完了,编译并运行我们的程序,你将会看到如下效果:
选择Direct3D或者OpenGL渲染(不要选择Ogre),你将会看到如下效果:
代码分析:
我们可以看到,通过简单的几行代码,我们就可以实现这么棒的效果,其实大部分工作都在CEGuiSample这个类里给我们封装好了。
不管我们使用的是什么引擎,在使用CEGUI时,至少要做如下所示的最基础的三部,才能使CEGUI运行起来。
第一步,创建CEGUI::Render对象。这其中会根据我们选择的是OpenGL还是Direct3D或者是Ogre渲染,选择我们最终要使用的渲染选择器,比如说这里我们选择了Direct3D方式渲染,那么在CEGuiSample中会为我们生成一个CEGuiD3D9BaseApplication对象的实例,在这个类的构造函数中会创建对应的Direct3D9Renderer渲染器然后这个实例对象会通过它的一个execute函数反过来调用我们重写的这个initialiseSample函数,这样我们的代码就能被调用到了,初始化工作也就完成了;
第二步,创建CEGUI::System对象。在一些初始化工作完成之后,CEGUI会调用下面这样的代码来完成System对象的创建:
CEGUI::System&guiSystem =CEGUI::System::getSingleton(); |
第三步,调用渲染函数进行渲染。现在我们有了System对象,因此就可以开始进行渲染了,方法很简单,我们可以调用System的renderGUI函数开始我们的渲染,这样我们的程序就真正工作起来了。
虽然上面仅仅是简单的三步,其实要想是CEGUI真正运作起来还需要一些其它的操作,但是上面三步是一个主要的流程,详细读者朋友理解了这三步,查看源代码的时候就会很容量理解整个流程了。下面我们通过一个简单的流程图再来理一下我们的思路。
17.3 CEGUI中的资源管理及其相应组件的创建
理解了基本的渲染流程,现在让我们来看一下CEGUI中的资源管理及其相应组件的创建。
17.3.1 资源管理
CEGUI中用资源组管理器装载一些文件共其在渲染的时候使用,它使用到了一个工具对象,我们把它叫做“ResourceProvider”,此对象提供了一组接口负责CEGUI与其它的文件装载系统通信。例如,我们知道Ogre也有自己的资源管理/文件装载子系统,通过实习特定的ResourceProvider对象,CEGUI的渲染模块就可以和那些子系统无缝的组合起来,那样CEGUI的数据文件就可以通过那些子系统装载了。但是我们知道更底层的库(Direct3D和OpenGL)没有那样的资源管理系统,所以,CEGUI会为它们提供默认的资源管理系统(Default Resource Provider)。
由于现在我们还没有把CEGUI和Ogre结合,所以我们先来了解一下CEGUI的默认资源管理系统:CEGUI::DefaultResourceProvider,它是为那些目前还没有的库提供基础帮助的系统。它不仅提供了CEGUI装载文件、数据时所需的函数,而且对“资源组(Resource Groups)”也提供了初步的支持。这里的“资源组”其实是一个标签,它代表系统的某个文件夹路径,这就使得我们可以将文件夹中的文件按其逻辑类型进行分组,然后可以通过一个简单的标签而不是硬编码的路径去指定它。也就是说,当数据文件的路径有改动的时候,只需要更新资源组的路径而不必更改代码和XML文件中的路径信息。
DefaultResourceProvider允许你定义任意数目的资源组,并为每个资源组指定一个路径。也就是说:你可以创建一个资源组,比如“imagesets”,并为它指定一个路径,假设是“datafiles/imagesets”,然后,当你通过ImagesetManager装载Imageset的时候,就可以指定“imagesets”为它将要使用的资源组,这样,系统就会再预定义的路径中寻找资源。目前,每个资源组只能被赋予一个路径。
这里,我们举一个小例子来说明,以前,如果我们在没使用资源组的时候,你可能这么做:
Imageset* imset = ImagesetManager::getSingleton().createImageset("datafiles/imagesets/TaharezLook.imageset"); |
用了资源组以后,在初始化阶段,我们可以用默认的资源管理器像下面这样创建资源组:
CEGUI::DefaultResourceProvider*rp =static_cast<CEGUI::DefaultResourceProvider*>(CEGUI::System::getSingleton().getResourceProvider()); rp->setResourceGroupDirectory("imagesets","datafiles/imagesets/"); |
然后当你需要载入imageset的时候,你可以这样指定要使用的资源组:
Imageset* imset = ImagesetManager::getSingleton().createImageset("TaharezLook.imageset", "imagesets"); |
这里你不需要提供任何路径信息,因为在你指定的资源组中已经包含了相关的路径信息,一般它们只包含文件名。
系统定义的任何代表可装载资源的资源类,都有获取和设置默认资源组的静态函数,当需要载入数据文件的时候,它就用那个默认资源组。比如,