CEGUI学习笔记七--如何扩展CEGUI控件库(二)

CEGUI学习笔记七

--如何扩展CEGUI控件库(二)


本文是扩展CEGUI控件库的第2部分,对FalButton.CPP文件的分析记录。
目的是探究下如何去控制 Renderer,从而彻底实现一个CEGUI没有的控件类型。

但是分析未完成,希望这些半成品能对大家有用。

PS:由于公司的网络限制,所以Blog很长时间未更新。家里还没电脑,上班偷偷写的....
下次将整理下单独编译CEGUI,并给出CEGUI与Ogre结合的详细步骤。


//=====================================================================================================================
// 2008.4.18
//=====================================================================================================================
本来打算今天探究下如何去控制 Renderer,从而彻底实现一个CEGUI没有的控件类型。

首先从FalButton开始切入,但是发现问题远没有想象中那么简单。
在先前对Falagard的翻译中得知,如果要绘制一个控件,需要定义规定的StateImagery。
所以我期望在FalButton中看到这些状态的集合,否则手册里规定的状态从哪来呢?
实际上我看到的是 "Pushed" : "PushedOff"; "Disabled" "Hover"之类的 魔字符串!
现在知道这些状态是哪来的了:在CEGUIfalagardWRBase项目里相关文件里这样定义的!这可真要命,太不可接受了。

OK,既然已经知道如何定义需要实现哪些状态,再找找和显示相关的代码,在这个对象中,只有一句跟这个相关:
wlf.getStateImagery(actualStateName(state)).render(*w);
1、wlf是WidgetLookFeel,
2、getStateImagery返回StateImagery,
获取WidgetLookFeel的唯一对象,然后得到一个叫actualStateName(state)的StateImagery,并调用它的render()方法来进行绘制。
以w为参数。w是什么?
w是d_window,继续查找这个成员变量,发现是WindowRenderer类的成员变量,回头看H文件的定义,FalButton继承自WindowRenderer。
跟WindowRenderer类似的还有很多***Renderer的文件,所以推测Button,Frame等简单控件类型的绘制都继承这个WindowRenderer。
而List等复合控件类型需要另外特别的***Renderer来实现绘制。

***Renderer里实现绘制,确实是这样的吗?如果是这样的话,为什么要调用StateImagery对象的render()方法。
如果要绘制什么东西,一定需要绘制它的参数信息,包括数据和规则,既然***Renderer是作为参数传递给StateImagery的render(),
那么***Renderer要么给出了绘制方法,要么给出数据,要么给出规则。现在虽然不能确定,但是应该是提供规则的可能性大一些。
[后面将证明是错误的,它提供的不是规则而是数据。]
现在又有2条路了:
1、WindowRenderer是什么东西。
2、StateImagery的render()到底做了什么事。

先查看WindowRenderer类的介绍:
// Base-class for the assignable WindowRenderer object
这是什么,可支持的窗口渲染器对象的基类?显然不太好对付。
从类视图中可以看到它的派生类,找个复合控件,EditboxWindowRenderer类的介绍:
// Base class for the EditboxWindowRenderer class
OK,现在知道如果不看***Renderer的代码的话,是不知道这个类做什么用的了。
所以还是顺着render()看。
有2个版本,但结构差不多,所以只盯着一个看先。
先看这个函数的介绍,没什么很详细的信息....这说明如果我们了解整个设计目的的话,这个函数是理所当然的。
没什么好说的,RTFS。
void StateImagery::render(Window& srcWindow, const ColourRect* modcols, const Rect* clipper) const   
{
 float base_z;

 // render all layers defined for this state
 for(LayersList::const_iterator curr = d_layers.begin(); curr != d_layers.end(); ++curr)
 {
  // TODO: Magic number removal
  base_z = -0.0000001f * static_cast<float>((*curr).getLayerPriority());
  (*curr).render(srcWindow, base_z, modcols, clipper, d_clipToDisplay);
 }
}

又冒出很多新鲜玩意出来了,就知道!
LayersList?估计就是 XML里的Layer关键字定义的那段东西,在CPP文件里的对应对象,暂时不管它的细节。
getLayerPriority?优先权?为什么要和-0.0000001f相乘?请教老大之后得知,这个Z值是给3D渲染器用的。因为
getLayerPriority返回的是整数,而3D渲染器的Z值是0到1,所以才需要这么乘一下。
再看看 *cuur.render() 接受的参数:1个窗口的引用,1个Z值,1个顶点颜色矩形,一个裁减矩形,还有一个d_clipToDisplay?

bool d_clipToDisplay; //!< true if Imagery for this state should be clipped to the display instead of winodw (effectively, not clipped).
如果为true,就是相对于显示器进行裁减,而不是窗口(实际上没裁减)?
构造函数里的初始化为d_clipToDisplay(false),搜索操作这个变量的方法,发现跟XMLHelper扯上关系,
而整个CEGUI里没有调用这个方法来设置这个变量,看来是放出去给外部进行设置的,那就不管了先,等以后在某个例子里发现的时候再回头看。

好吧,现在继续LayersList的render()。
 // render all sections in this layer
 for(SectionList::const_iterator curr = d_sections.begin(); curr != d_sections.end(); ++curr)
  (*curr).render(srcWindow, base_z, modcols, clipper, clipToDisplay);
又是个遍历,不过这次遍历的是SectionList。

再次回忆上次LookNFeel里的定义:
StateImagery
 layer
  section = ImagerySection

而ImagerySection的层次是:
ImagerySection
 ImageryComponent
  Area
  Image
  VertFormat

那在这里预测,如果查找(*curr).render(),一定是遍历一个ComponentList,然后调用 (*xx).render()。

不过找到的是获取ImagerySection的代码:
const ImagerySection* sect =
  &WidgetLookManager::getSingleton().getWidgetLook(d_owner).getImagerySection(d_sectionName);
然后sect->render(...);

结果与预测的不太一样,在这个地方已经开始使用try和catch,说明已经开始实际的绘制操作,马上就可以知道WindowRenderer提供
的到底是数据信息,还是规则信息了!
与预测不一样的地方有2个:
1、获取的不是Component,而是ImagerySection,为什么?
2、没有遍历,为什么?
回头查看SectionList,
typedef std::vector<SectionSpecification> SectionList;
说明Layer里可以有很多section,而section再来指出是哪个ImagerySection。所以应该先获取ImagerySection。
而section和ImagerySection是1对1的关系,所以没有遍历。

继续看ImagerySection的render(...),在里面发现了3个遍历:
FrameList:   FrameComponent   的render()
ImageryList: ImageryComponent 的render()
TextList:    TextComponent    的render()
FrameComponent   的render()里进行了一大堆的计算,然后分别绘制了Frame的4个角、4条边、1个背景。
虽然没看,但估计其他2个差不多。这就是实际的绘制工作了!

现在回头看看,Button有Frame吗?之前写Button的时候,只写了ImageryComponent。而我从FalButton开始切入,最后却看到了
FrameComponent的绘制,这说明了什么?

再从更大范围上整理一下:
我在XML文件里定义 Button的渲染器 FalButton,
FalButton里(通过d_window)判断当前按钮的状态(现在对于***Renderer到底是个什么还不清楚,等整理完就去看。),
再通过这个状态来(通过WidgetLookFeel对象)得到对应的StateImagery对象。
然后调用StateImagery对象的render()方法,以*w为参数。

然而现在来看,从调用StateImagery对象的render()方法开始,已经跟Button没关系了,一切都是按照规定好的流程在进行。
这说明如果我们要使用CEGUI的话,从这里开始就是不变的东西,如果要改变的话,要做的事情就比较多了。

在这之前,我们能控制的(自由度)就是(添加或减少一个)控件状态的定义。
但是在之前我写Button1的时候,没实现Hover状态下的代码,然后当我鼠标移到Button1的时候,就没图片显示出来。
如果按这个流程的话,得不到StateImagery对象,就不会调用render,所以就没图片显示出来,这就说的通了。

今天就到这里,下周顺着WindowRenderer开始查。

CEGUI(Crazy Eddie’s GUI http://www.cegui.org.uk)是一个自由免费的GUI,基于LGPL协议,使用C++实现,完全面向对象设计。CEGUI开发者的目的是希望能够让游戏开发人员从繁琐的GUI实现细节中抽身出来,以便有更多的开发时间可以放在游戏性上。 CEGUI的渲染需要3D图形API的支持,如OpenGL或Direct3D。另外,使用更高级的图形也是可以的,比如OGRE、Irrlicht和RenderWare等,关键需求可以简化为点: 纹理(Texture)的支持直接写屏(RHW的顶点格式、正交投影、或者使用shader实现) 本书截止日时,CEGUI的最新版本是0.6.0(本书的讨论也是基于此版本),本书光盘提供了SDK和全部源码的下载。 除此之外,CEGUI还同步提供了官方界面编辑器LayoutEditor和ImageSet编辑器,以方便UI和图像集的制作。作为界面编辑器,它需要系统级界面以提供编辑器操作,0.3.0版是基于MFC实现的;而在0.4.0版本以后,改为基于wxWidgets(跨平台的本地UI框架,这里的UI指Window操作系统底层,如:Windows、Unix和Mac,详见http://www.wxwidgets.org)实现。 目前将CEGUI作为游戏界面开发的游戏已经有好多种,国内的天龙八部,巨人等游戏就是很好的例子。 CEGUI的功能是非常强大的,而且使用也非常的灵活,可以和脚本配合。可以通过配置文件自定义窗口外观。通过布局文件实现窗口布局等等特性,使得游戏的界面开发更加方便。
评论 1
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值