CEGUI初级入门
一 CEGUI渲染入门
不管你用的是什么引擎,至少要做如下所示最基础的三步,才能使CEGUI运行起来。
1. 创建 CEGUI::Renderer 对象。
2. 创建 CEGUI::System 对象。
3. 调用渲染函数。
当然,真正实现游戏GUI,你还需要装载相关的数据文件、进行一些最基础的初始化(这在 装载数据文件和初始化入门 一文中进行讲解),你也需要处理用户输入(这在 输入处理入门 一文中进行讲解)。
1. 创建 CEGUI::Renderer 对象
这一步相当直观,对于所支持的渲染组件(比如:Direct 3D9, OpenGL, Ogre3D等),这步应该没有问题。当然,要记得包含所使用渲染组件的头文件。还有,Ogre3D现在已经将它的渲染组件改为Ogre而不是CEGUI。(切记:在程序结束的时候,要删除此Renderder对象) 创建 CEGUI::Renderer 对象代码如下:
Ogre3D :
CEGUI::OgreCEGUIRenderer* myRenderer =
new CEGUI::OgreCEGUIRenderer( myRenderWindow );
2. 创建 CEGUI::System 对象来对系统进行初始化
这一步更简单。只需new一个CEGUI::System对象,并把刚创建的CEGUI::Renderer对象的指针传送给它即可。这时整个系统将自动初始化。
new CEGUI::System( myRenderer );
3. 调用渲染函数
这一步有点特殊,所使用的引擎不同,此步的代码也会不同。不过,说白了,你所要做的只是在每帧渲染后调用CEGUI::System::renderGUI而已。使用Ogre3D的同学该高兴了,因为它将由Ogre3D自动完成。
二 资源管理入门
如果你已经读了 CEGUI渲染入门 这篇文章,你已经知道:要想让CEGUI跑起来,至少要对CEGUI进行最基础的初始化,并调用System::renderGUI方法,这看起来很不错。可是,你仍然不能让程序画任何东西!
为了达到胜利的彼岸,下一步是设置资源管理器组(resource provider groups),我们将用它们装载一些文件供CEGUI在渲染的时候使用。
ResourceProvider 是什么?
CEGUI使用了一个工具对象,我们叫它“ResourceProvider”。此对象提供了一组接口负责CEGUI与其他的文件装载系统通信。
例如: Ogre 和 Irrlicht 都有它们自己的资源管理/文件装载子系统,通过实现特定的 ResourceProvider 对象,CEGUI 的渲染模块就可以和那些子系统无缝的组合起来。那样,CEGUI 的数据文件就可以通过那些子系统装载了。但是,更底层的库(Direct3D 和 OpenGL)没有那样的资源管理系统,所以,CEGUI 为它们提供了默认资源管理系统(default resource provider)。 DefaultResourceProvider 相关说明
CEGUI::DefaultResourceProvider ---- CEGUI 的默认资源管理系统,是为那些目前还没有的库提供基础帮助的系统。它不仅提供了 CEGUI 装载文件、数据时所需的函数,而且对“资源组(resource groups)”也提供了初步的支持。这里的“资源组”其实是一个标签,它代表系统的某个文件夹路径。这就使得我们可以将文件夹中的文件按其逻辑类型进行分组,然后可以通过一个简单的标签而不是硬编码的路径去指定它。也就是说:当数据文件的路径有改动的时候,只需更新资源组的路径而不必更改代码和XML文件中的路径信息。
指定资源组和路径
DefaultResourceProvider 允许你定义任意数目的资源组,并为每个资源组指定一个路径。也就是说:你可以创建一个资源组,比如“imagesets”,并为它指定一个路径,假设是
“./mygame/datafiles/gui/imagesets/”。然后,当你通过ImagesetManager装载Imageset的时候,就可以指定“imagesets”为它将要使用的资源组,这样,系统就会在预定义的路径中寻找资源。目前,每个资源组只能被赋予一个路径。
下面给一个小例子来说明。以前,在没用资源组的时候,你可能这么做:
Imageset* wlis = ImagesetManager::getSingleton().createImageset(
用了资源组以后,在初始化阶段,你可以用默认的资源管理器像这样创建资源组:
DefaultResourceProvider* rp = static_cast(
CEGUI::System::getSingleton().getResourceProvider());
rp->setResourceGroupDirectory(
然后,当你需要载入imageset的时候,你可以这样指定要使用的资源组:
Imageset* wlis = ImagesetManager::getSingleton().createImageset(
注意:你不需要提供任何路径信息,因为在你指定的资源组中已经包含了相关的路径信息。比如上面例子中的
另一个需要注意的是:数据文件不应该包含任何的相对路径信息,一般它们只需包含文件名。 默认资源组
系统定义的任何代表可装载资源的资源类,都有获取和设置默认资源组的静态函数。当需要载入数据文件的时候,它就用那个默认资源组。比如:对于Imageset类,默认的资源组应该指向一个存储imageset xml文件和材质文件的文件夹。
对于每个资源类,它们获取、设置资源组的静态函数的名字都是一样的(xerces是个例外,下面会解释):
const String& getDefaultResourceGroup();
void setDefaultResourceGroup(const String& groupname);
如下是资源类的一个列表,后面是它们各自管理的资源类型:
CEGUI::Imageset - Imageset xml and texture image files.
CEGUI::Font - Font xml and ttf font files.
CEGUI::Scheme - Scheme xml files.
CEGUI::WindowManager - Window layout xml files.
CEGUI::WidgetLookManager - LookNFeel xml files
CEGUI::ScriptModule - Script files in whichever scripted langauge.
上面提到过,xerces是个例外,它是“基于Xerces-C的XML解释器(Xerces-C based XML parser)”。对此,有一个特殊的资源组设置文件,它指明schema文件在哪里(它们是用来检查xml的.xsd文件)。对于这个特殊情况,它的静态成员如下:
const String& XercesParser::getSchemaDefaultResourceGroup();
void XercesParser::setSchemaDefaultResourceGroup(const String&
groupname);
需要注意的最后一点是:资源管理类也有一个默认的资源组。当一个资源类没有显示指定资源组的时候,系统就使用它。当你把所有的数据文件都放到一个文件夹里的时候,这点就很方便了。
一个完整的例子:配置 DefaultResourceProvider
作为结尾,我将用一个完整的例子向你展示CEGUI的例子框架是怎样初始化资源组、初始化它们的目标路径以及怎样为所有的资源类型设置默认资源组的。
像往常一样初始化CEGUI::System对象,我们设置一组资源组和它们的路径:
// 为DefaultResourceProvider设置所需的路径
CEGUI::DefaultResourceProvider* rp = static_cast
(CEGUI::System::getSingleton().getResourceProvider());
rp->setResourceGroupDirectory(
rp->setResourceGroupDirectory(
rp->setResourceGroupDirectory(
rp->setResourceGroupDirectory(
rp->setResourceGroupDirectory(
// 这步仅当你使用Xerces作为XML解析器的时候才需要
rp->setResourceGroupDirectory(
完成这步以后,我们就把资源组和它们的路径都设置好了。最后,为了让系统使用新设置的路径,我们来设置默认资源组:
// 设置默认资源组
CEGUI::Imageset::setDefaultResourceGroup(
CEGUI::Font::setDefaultResourceGroup(
CEGUI::Scheme::setDefaultResourceGroup(
CEGUI::WidgetLookManager::setDefaultResourceGroup(
CEGUI::WindowManager::setDefaultResourceGroup(
CEGUI::ScriptModule::setDefaultResourceGroup(
// 仅当你用xerces做XML解析器,并为它定义了一个资源组的时候使用
CEGUI::XercesParser::setSchemaDefaultResourceGroup(
三 装载数据文件和初始化入门
Ok。目前你已经学习了 CEGUI渲染入门 和 资源管理入门,下一步就要开始学习装载数据文件了。只有装载了相关数据文件,CEGUI才会产生想要的输出!
数据文件概述
CEGUI使用多种类型的数据文件。刚开始学的时候,可能搞不清:它们是什么,它们之间怎样联系以及它们怎样被载入。所以,我决定先介绍一下这些数据文件:它们是什么,它们将被用来干什么以及它们是如何被载入CEGUI的。
XML,XSD?都是XML! CEGUI所使用的数据文件绝大多数都是XML格式的,除了那些特殊的图片文件(.tag等)以及可载入模块文件(DLL等)。说到这里大家可能会想到那个特殊的.xsd文件。
尽管现在CEGUI的默认XML解析库是Expat XML解析库,可是CEGUI以前一直用Xerces-C++库作为它的默认XML解析库,Crazy Eddies本人也更喜欢这个库。这个库的优势是它提供模式验证(schema validation)。通过模式验证,我们可以在解析期间检测输入的文件是否包含需要的数据以及数据是否被正确的设置。系统需要一些额外的文件才能做这个,它们就是以.xsd作为后缀的模式文件 -- CEGUI的模式文件存放在cegui_mk2/XMLRefSchema/目录下。目前,对于.xsd文件,你只需知道:当把Xerce-C++用作XML解析器的时候,必须让ResourceProvider系统能够找到它们。要做到这点很简单,只需设置一个资源组,把此资源组关联到一个包含相关.xsd文件的文件夹,并把这个资源组设置为CEGUI::XercesParser装载.xsd文件时所使用的默认资源组。(相关方法参看 资源管理入门)
数据文件 正如前面提到的,CEGUI所用的数据文件除了少数的特殊情况外都是XML格式的。但是,为了清晰的说明数据文件代表什么类型的资源,数据文件并不是以“.xml”作为后缀,而是用更有意义的后缀名。比如,Imageset的后缀名是.imageset,Font的后缀名是.font,等等。下面将对每种文件类型做个简要的说明,以后将对它们进行更深入的讨论。
Imageset 这么说吧,通常,为了提到效率,我们需要把材质等图片组合成一个大的材质图片。在使用的时候就需要在它上面截取得到各个小材质。而这个Imageset就是保存各个小材质在整张源图片上的区域信息(源图片同样在Imageset中指定了)。每个区域信息都有一个独一无二的名字,当提到它们的名字的时候,系统就把它们当作图片处理。所以,也可以说Imageset中定义了一系列的图片。通过修改Imageset中的源图片名、各个小区域的位置和大小,就可以很轻松的改变所画GUI的外观。
Font 显而易见,Font文件定义了CEGUI使用的字体。所能定义的字体类型有两种:
FreeType Font 这是一种基于true-type(.ttf)的字体文件。从CEGUI 0.5.0开始,在.font文件中用Type=
Pixmap Font 这种字体就是众所周知的位图字体,这种字体基于一个定义了文字图片的Imageset。从CEGUI 0.5.0开始,在.font文件中用Type=
Scheme Scheme文件是把其他数据文件联系到一起的主要手段,同时它也是装载和定义各种控件最方便的方法。一个Scheme文件可以包含下面的一种或多种文件(当Scheme被载入的时候,它所包含的文件也将被载入并初始化):
Imageset (either a full Imageset via XML, or a single image via an image file) Font WindowSet WindowRendererSet WindowAlias
FalagardMapping
Imageset and Font已经被提到过了。这列出来仅仅为了说明scheme可以包含它们。
WindowSet 主要用来指明一个可载入模块(.dll,.so等)的名字,并列出它所使用的控件中你想注册的控件的名字。如果没有列出任何控件名,那么模块中所有的控件都将被注册。
WindowRendererSet 指明一个可载入模块(.dll等)的名字,并列出它所使用的窗口渲染器(window renderer)中你想注册的窗口渲染器的名字。如果没有列出任何窗口渲染器名,那么模块中所有的窗口渲染器都将被注册。“窗口渲染器”是一个可以控制基本窗口类型渲染的东西。所有的窗口渲染器都是利用'Falagard'蒙皮系统进行渲染的(尽管这不是非常必须的)。
WindowAlias 提供通过别名指定一个窗口/控件类型的方法。用此方法,也可以用另一个控件类型来“替代”一个已经注册的控件类型,这样就达到隐藏已注册控件的效果。
FalagardMapping 用来创建一个可用的WindowType类型。包含三个部分:TargetType --- 指定基类(具有相关功能);Renderer --- 指定窗口渲染器(可以控制渲染指定的TargetType);LookNFeel --- 指定要使用的皮肤(一般这个它们是通过XML格式的looknfeel文件指定)。
LayoutLayout文件用XML描述一个窗口的布局。每个镶嵌的“Window”元素定义一个要创建的窗口或控件,“Property”元素为每个定义的窗口设置相关属性。
Config CEGUI支持使用配置文件。这个文件允许你定义一些默认参数,比如:默认载入的Scheme,默认载入的Layout,初始化和终止脚本文件(ScriptModule中使用),以及其他没有提到的东西。
载入基本的文件
为了成功显示GUI,你需要载入一些文件。至少要载入下面这些:
Imageset
Font
Scheme
值得高兴的是,Scheme文件能自动的载入其他两个文件。介于本教程,我们将载入一个
scheme文件和一个font文件 --- scheme文件自动帮我们载入一个Imageset文件。代码如下: // 载入scheme文件,它将自动载入TaharezLook imageset
CEGUI::SchemeManager::getSingleton().loadScheme(
// 载入font文件。第一个被载入的字体文件将自动成为默认字体。
if(!CEGUI::FontManager::getSingleton().isFontPresent(
CEGUI::FontManager::getSingleton().createFont(
上面的代码假设资源组以及默认资源组都按照资源管理入门中描述的方法设置完了。 简单的默认设置
最后,你需要设置一些默认值。这么做是为了确保系统总是有可用的字体和鼠标指针,以防某个窗口或控件没有特别设置它自己的字体和鼠标指针。
实际上,我们不再需要指定一个默认字体,因为FontManager会自动的设置第一个被载入的字体为默认字体。如果它不是你想要的默认字体,你可以设置为其他的。设置的代码如下: System::getSingleton().setDefaultFont(
另一个你需要设置默认值的是鼠标指针。这么做是为了确保当鼠标位于那些没有设置指针的控件上时不至于消失。设置默认鼠标指针的代码如下(本示例使用由上面的scheme载入的TaharezLook imageset):
System::getSingleton().setDefaultMouseCursor(
如果你打算用工具提示,则需要指明你想用的基于ToolTip的控件类型名。尽管通常情况下是不需要这么做的,这也超出了这个基础教程的范围,其实可以在每个窗口基础上设置它。设置工具提示窗口类型代码如下:
System::getSingleton().setDefaultToolTip(
总结
目前,我们已经讨论了关于CEGUI所用的数据文件最基础的东西:它们是什么,它们是怎样被载入的,以及最少要做的初始化工作。以后将要对每种数据文件类型做深入的讨论,发掘关于它们更高级的用法。
四 创建CEGUI窗口入门
本课将讲述如何创建并显示一个简单的CEGUI窗口。在继续之前,请确保你已经通读并完全理解了前面的几篇教程:CEGUI渲染入门,资源管理入门和装载数据文件和初始化入门。这非常重要,因为本教程是建立在那些教程的基础之上的。
概念介绍:窗口和控件
在进行教程之前,你必须了解一些重要的概念。
所有的控件都是窗口这是最重要的概念。所有的控件类都是从Window这个基类派生出来的,所以,在此教程中,每当我提到一个窗口的时候,它可以是一个按钮也可以是一个滚动条控件。 很多的设置都会被继承下去 CEGUI中,窗口的很多的设置和属性都会按窗口父子等级向下传递。比如:如果你将一个窗口的alpha透明度设置为0.5,那么,默认情况下,所有附属于那
个窗口的子窗口/组件都将受到影响。同时要注意:子窗口的实际设置并没有改变 ---- 最终的属性值通常是由最顶层到当前窗口所有属性值组合而成的。好多东西都有这个规律,比如窗口的销毁:默认情况下,一个窗口在销毁的时候将销毁它的所有子窗口。这一机制最大的优点是:通过改变根窗口的alpha透明度、显示/隐藏、激活/禁用状态等可以很轻易的控制整个GUI;通过简单的销毁根窗口就可以轻松的清空整个GUI。当个别窗口需要更精确的控制或有更好的管理技术用的时候,可以更改这一默认继承机制。
创建窗口
聊够了!我们开始吧。有两种方法来创建窗口:通过C++硬编码实现和通过XML布局文件实现。下面将讨论这两种方法。
通过C++硬编码实现GUI CEGUI的所有窗口都是被WindowManager这一对象创建的。你可以通过getSingleton函数获得这个对象,代码如下: using namespace CEGUI; WindowManager& wmgr = WindowManager::getSingleton();
一般情况,你将用DefaultWindow(或者它的老名字:DefaultGUISheet)作为GUI的“root”窗口。这点并不是必须的,但它是使用CEGUI很好的习惯,而且可以帮助简化布局。
下一步,我们将创建一个DefaultWindow作为GUI的根窗口(这里sheet是root的意思)。 Window* myRoot = wmgr.createWindow(
System::getSingleton().setGUISheet( myRoot );
WindowManager::createWindow函数有两个string类型的参数。第一个参数,本例中为“DefaultWindow”,告诉系统你要创建的窗口类型或类。通常,你可以使用的窗口类型是那些当你载入scheme文件的时候注册的窗口类。而有些像DefaultWindow这样的是全局类型,它们总是可用的。第二个参数,本例中为“root”,是一个将要赋予这个窗口的独一无二的名字。以后可以通过这个名字获取指向这个窗口的指针。注意:并不是一定要给你的根窗口命名为“root”,但这是通常的命名约定。
System::setGUISheet函数用来指定一个窗口作为GUI的根窗口。这将替换掉当前的根窗口,但是要注意:之前的一系列窗口/控件并没有被销毁,只是从当前的显示链中摘除 ---- 通过使用setGUISheet函数,你可以轻松的在多个GUI中切换。
现在,你已经创建了第一个窗口并把它附着在GUI系统上,当系统画GUI的时候,它将把这个窗口作为GUI的根。但是,如果你编译运行这些代码,你依然不会看到任何东西。怎么了?你的程序没有任何问题,问题在:我们刚才创建的DefaultWindow是隐藏的!这就是为什么
DefaultWindow那么适合做根窗口的原因:它仅仅被用作供其他的窗口和控件依附的空画布。那么,我们再加把劲吧。。。
现在,我们将要创建一个框架窗口:这个窗口和你桌面上的窗口很类似,它有一个标题栏,也可以移动和改变大小。代码如下: FrameWindow* fWnd = (FrameWindow*)wmgr.createWindow(
此代码创建了一个“TaharezLook/FrameWindow”窗口。整个系统都使用这种命名约定:窗口类型以组件组名做为前缀(假如你载入了WindowsLook scheme,你就可以创建一个
“WindowsLook/FrameWindow”对象)。我们为这个新窗口命名为“testWindow”。需要注意的是:那个类型转换的使用,由于createWindow函数总是返回Window基类指针,尽管对于此例以及其他情况,返回这个Window指针就足够了,但是有时你需要访问子类的方法,所以使用CEGUI的时候示例中的类型转换是很常见的。
为了让系统能够用我们的新窗口做一些有用的事情,我们还需要做一些事情。
首先,我们必须把这个新窗口附着到我们上面指定的根窗口上,代码如下:
myRoot->addChildWindow( fWnd );
现在,我们可以设置它的位置和大小。CEGUI使用一个“统一的(unified)”坐标系,使得可以同时使用相对部分和绝对部分 ---- 这就是为什么你看到的每个坐标都由两个部分组成。想了解更多请看Introduction and overview of core
fWnd->setPosition(UVector2( UDim( 0.25f, 0 ), UDim( 0.25f, 0 ) ) );
// 设置其大小为其父窗口的一半
fWnd->setSize( UVector2( UDim( 0.5f, 0 ), UDim( 0.5f, 0 ) ) );
最后,我们为这个框架窗口的标题栏设置一个标题: fWnd->setText(
XML布局上面的方法看起来很不错,但是,它有一个很大的弊端:每次你想要调整GUI布局的时候,你就得去修改代码,然后重新编译。这样还不累死了?你真正想要做的是能够把布局保存在文件中,然后在代码中调用那个布局文件。
系统支持XML格式的布局文件,可以通过WindowManager::loadWindowLayout函数载入此文件。此函数为你创建所有的窗口并返回一个指向根窗口的指针。调用setGUISheet设置GUI的根窗口的时候用此指针再适合不过了。
所以,首先我们需要一个布局文件。下面这个XML文件所描述的窗口和我们上面用C++创建的窗口是一样的:
Window元素的属性和WindowManager::createWindow函数的参数是一一对应的。参见上面讨论过的 createWindow 函数。
镶嵌的Window元素用来表明窗口的父子关系。注意:在一个布局文件中,你仅可以拥有一个“根”级别的窗口,这也是通常情况下把DefaultWindow用作根窗口的另一个原因。
Property元素是用来设置当前窗口的属性的。每种窗口/控件类都有很多属性,而且每个类都从它的父类中继承所有的属性。可以在这里查看所有硬编码属性以及它们的格式。由于
“Falagard”蒙皮( 'Falagard' skins)可以创建自己的属性,你所使用的窗口可能会有更多的属性 ---- 对于那些“软编码”属性,你需要参阅你所使用的蒙皮的相关文档(参看样品蒙皮TaharezLook和WindowsLook)。
如果保存此布局文件为“test.layout”,你可以按如下方法加载并设置GUI根窗口: using namespace CEGUI; Window* myRoot =
WindowManager::getSingleton().loadWindowLayout(
System::getSingleton().setGUISheet( myRoot );
编译运行后得到的结果和前面用C++写的程序是一样的。不过,现在你可以轻松修改、增强GUI布局而不用修改、重新编译代码啦。
总结现在,你已经学习了:基本窗口的创建,多窗口的创建,以及如何修改窗口设置。你学习了如何通过C++硬编码实现和如何通过XML布局文件实现。有很多更先进的方法可以同时使用那两种方式,但这最好留给更高级的教程去讲解。
五 输入处理入门
通过学习前面的教程,你现在可以渲染GUI,载入文件甚至创建窗口。这时,你也许想给你的GUI添加用户交互了。本教程将讲述这些。
CEGUI 输入处理简介
坏消息令人吃惊的是:CEGUI不会自动捕获任何用户输入,由程序决定CEGUI需要处理哪些输入。这意味着:每当产生按键或鼠标移动等消息时,你就得把它们传递给CEGUI。尽管初次看起来很奇怪,这其实也给了你更多的控制权 ---- 我们并没有把输入绑定到特定的系统,你也可以筛选、传递CEGUI需要获得的输入。
传递你的输入!我们创建了一个接口用来向CEGUI传递输入信息。它由CEGUI::System类的一组成员函数组成 ---- 对于每种基本的输入类型都有一个函数与之对应:
bool injectMouseMove( float delta_x, float delta_y );
bool injectMousePosition( float x_pos, float y_pos );
bool injectMouseLeaves( void );
bool injectMouseButtonDown( MouseButton button );
bool injectMouseButtonUp( MouseButton button );
bool injectKeyDown( uint key_code );
bool injectKeyUp( uint key_code );
bool injectChar( utf32 code_point );
bool injectMouseWheelChange( float delta );
bool injectTimePulse( float timeElapsed );
是的,看起来很多。你可能注意到,它们中有些看起来有点重复了,比如
(injectKeyDown/injectKeyUp)以及字符(injectChar)消息,这样做有两个原因:第一,不是所有的按键都产生字符消息(比如shift,alt等等);第二,这允许你自定义按键映射和自动重复按键(CEGUI目前不提供那些函数)。
另外要说明的是函数的布尔返回值。它是用来告诉程序CEGUI是否处理了此消息。如果返回false,说明CEGUI没有处理此消息,此时你的程序可能需要另外做一些处理。
那些函数是用来干什么的下面,我们将要简要的讨论每个函数的作用,它需要的参数,它用输入做了什么。
bool injectMouseMove( float delta_x, float delta_y )
此函数用来传递鼠标移动信息。参数“delta_x”和“delta_y”分别用来指明鼠标在X轴和Y轴上所移动的位移(有方向)。这将导致鼠标做相应的移动(具体移动的大小可以通过
CEGUI::System::setMouseMoveScaling设置比例因子改变)。如果你用此函数,基本上就不需要使用injectMousePosition函数了。
bool injectMousePosition( float x_pos, float y_pos )
此函数用来传递鼠标当前的绝对坐标。参数“x_pos”和“y_pos”用来指明鼠标的绝对坐标,坐标系的原点(0, 0)在CEGUI窗口的左上角(所以,在窗口模式下,原点在窗口左上角而不是整个
桌面的左上角)。这将把鼠标移动到新的位置。如果你用此函数,基本上就不需要使用injectMouseMove函数了。
bool injectMouseLeaves( void )
此函数用来告诉系统鼠标已经离开程序窗口了。在窗口模式下,此方法很有用。
bool injectMouseButtonDown( MouseButton button )
此函数用来告诉系统某个鼠标按键被按下了。参数“button”可以取如下所示的
CEGUI::MouseButton枚举值: enum MouseButton {
LeftButton,
RightButton,
MiddleButton,
X1Button,
X2Button,
MouseButtonCount,
NoButton
};
如果你的输入库和上面的不吻合,你必须自己转化。另外,要注意:NoButton的值不是0。 bool injectMouseButtonUp( MouseButton button )
此函数用来告诉系统某个鼠标按键被释放了。它的参数和injectMouseButtonDown是一样的。 bool injectKeyDown( uint key_code )
此函数用来告诉系统某个按键被按下了。它的参数“key_code”是那个按键的扫描码(不是ASCII码或其他的字符编码)。所有的扫描码在CEGUI::Key::Scan枚举变量中有定义。微软的DirectInput库输出的扫描码和我们的是一样的,如果使用其他的库,你可能需要做一些转换。目前看来,并不需要传递所有的按键按下/释放消息,一般需要这些消息的是:backspace,delete,回车,shift以及方向箭头按键。
目前没有实现按键到字符的映射,也没有提供自动重复按键的函数(这些也许在以后会实现)。如果你需要自动重复按键功能,你可以使用输入库提供的函数或者自己编码实现。你肯定需要输入字符,请看下面的injectChar函数。
bool injectKeyUp( uint key_code )
此函数用来告诉系统某个按键被释放了。和injectKeyDown的参数一样,“key_code”是按键的扫描码。
bool injectChar( utf32 code_point )
此函数用来告诉系统一个字符按键被按下。在输入文本的时候,你需要用此函数。参数
“code_point”是一个字符的Unicode UTF32编码(欲了解Unicode请参看此 Unicode 网站)。怎样获得这个值取决于你所使用的输入库。对于ASCII字符,你只需直接以ASCII码的形式传递给它即可,因为Unicode码和ASCII码的0到127部分是相同的。对于其他字符,你使用的输入库应该会有相关的API(比如:通过微软的Windows消息泵可以获得字符的UTF32编码),具体怎么做不属于本教程的范围。
bool injectMouseWheelChange( float delta )
此函数用来告诉系统鼠标滚轮的使用情况。正数表示向前滚动(远离用户方向),负数表示向后滚动(接近用户方向)。
bool injectTimePulse( float timeElapsed )
此函数用来告诉系统相关时间信息。参数“timeElapsed”表示自上次调用此函数或运行CEGUI以来所经过的秒数。此函数越来越重要了,它被用来控制控件的消失,为工具提示、菜单以及鼠标按键的自动重复提供计时等。
总结目前,我们学习了CEGUI获取输入的函数,以及那些函数的作用、参数等。
与前面几篇教程不同,此篇并未提供具体的示例代码。主要是想使教程适当的简短,以防因对特定的输入库编码而杂乱无章。将特定输入库的使用放在单独的文章中讲述更合理。
如果想看一些具体的例子,这里提供两个:
我们在wiki上有一些很牛的例子
o Using CEGUI with SDL and OpenGL
o Using CEGUI with Producer and OpenGL
使用示例框架代码写成的例子被保存在cegui_mk2/Samples/common/src:
Win32AppHelper.cpp - contains a windows message procedure injecting mouse and character inputs into CEGUI, and using DirectInput for keyboard up and down messages. CEGuiOgreBaseApplication.cpp - contains an example Ogre::FrameListener class which takes inputs generated by the OIS Library and injects these into
CEGUI.CEGuiOpenGLBaseApplication.cpp - registers callback functions with glut to process inputs, and also shows the use of a key translation table.
此外,使用Irrlicht的同学可以使用Irrlicht自带的event pusher ---- 每当一个输入产生的时候你可以使用Irrlicht Renderer的OnEvent函数处理它(你只需用injectTimePulse处理时间)。
六 统一度量系统使用指南
注意:此教程仅适用于CEGUI >= 0.4.0
统一度量系统使得我们可以用相对部分和绝对部分共同来表示一个坐标或大小。这给窗口布局带来了极大的方便。比如:你可以用相对尺寸来表示高度,使用绝对大小表示宽度,或者混合使用。
统一度量系统共有三种形式:
* UDim : 简单的一维
* UVector2 : 由两个UDim组成的二维向量
* URect : 用四个UDim表示一个矩形,依次为:左,上,右,下
UDim UDim是最简单的了格式为 :{scale, offset} 例如 :{1,0}
格式为 :{scale, offset} 例如 :{1,0} 父窗口的值乘以“scale”然后加上offset就是最后的结果(单位都是像素)。例如:假如上面的例子是窗口的UnifiedWidth属性值,我们将得到和其父窗口一样宽的窗口。另一个例子: {0.5, 25} 这将使得到的窗口宽度为其父窗口的一半加上25像素。
使用单一UDim作为其值的属性有:
* UnifiedXPosition
* UnifiedYPosition
* UnifiedWidth
* UnifiedHeight
UVector2 UVector2是用来表示位置和大小的。格式为 :{{x-scale, x-offset}, {y-scale, y-offset}} 例如 :{{1, 0}, {1, 0}}
UVector2中包含的两个UDim很像。还是用例子说明吧:假如上面的例子代表窗口的UnifiedSize属性,我们将得到和它的父窗口一样大小的窗口。 {{1,0},{0,100}} 上例将产生一个和其父窗口一样宽,但是高度固定为100像素的窗口。
使用Uvector2作为其值的属性有:
* UnifiedPosition
* UnifiedSize
* UnifiedMinSize
* UnifiedMaxSize
URect 最后的一种是URect。它有点特殊,它定义了左,上,右,下四个坐标,而不是大小或位置。由于参数很多,我将用“ls”代替“left-scale”,用“to”代替“top-offset”等等。格式为 :{{ls,lo},{ts,to},{rs,ro},{bs,bo}} 例如 :{{0,0},{0,0},{1,0},{1,0}}
上述代码是DefaultWindow类型窗口的默认矩形。它将覆盖其父窗口的整个区域。只有一个属性使用URect值 ---- UnifiedAreaRect。我们定义矩形区域而不是其大小的做法是很聪明的。比如:假如我们想使一个窗口覆盖它的父窗口,但要为父窗口的四边分别留出10像素的大小,代码可以这样写: {{0,10},{0,10},{1,-10},{1,-10}}
此例可以看出,绝对部分可以取负数。
XML中的应用举例
X-position:父窗口宽度的10% + 10像素 Y-position:父窗口的高度 - 30像素
Width:父窗口宽度的60% + 5像素 Height:父窗口高度的30% + 20像素
X-position:父窗口宽度的25% - 5像素
Value=
X-position:父窗口宽度的10% Y-position:父窗口高度的10% Width:父窗口宽度的80% Height:父窗口高度的80%