Ogre 1.7 SDKTRAY 初探

Ogre 1.7 这个版本比以前有了很多的变化.其中之一就是在Ogre官方不再包含CEGUI的组件.但是尽管没有使用CEGUI,在官方的demo中仍然可以看见有gui的功能,那么Ogre又是怎么实现的呢?

根据官网的说明,新的gui系统使用的是一个叫"tray"的系统.这个系统的主要代码在SdkTray.h这个文件之中.sdktray使用的Ogre overlay来实现gui,并且Ogre 1.7比以前版本似乎也多了一些gui相关的组件.这大概预示,Ogre即将开发自己的gui组件,而cegui也许将被抛弃.

不管未来如何,研究下sdktray还是很有意义的一件事情.使用overlay来实现gui,最大的特点在于可以灵活利用Ogre强大的material系统,实现很多不可思议的效果.比起使用cegui来,减少了学习周期.但是比较遗憾的是,目前Ogre 1.7的sdktray还不能作为一个完整的gui组件,

有些功能还很不完善.

现在来研究以下sdktray.h文件的源代码.

首先是2个enum;ButtonState就如同其名字一样,指的是鼠标按下 悬浮以及释放的情况下的gui对应3种状态.不过一般的说,这3种状态所有gui应该都具备.接下来是一个sdkTrayListener的基类,从接口来看,应该是实现gui对应的功能.包括点击 选择 移动等等.

Widget是所有窗口组件的基类.一个Widget包含一个OverlayElement,和一个sdktraylistener,分别实现外观和对应的事件响应.这个基类还包括几个static的工具函数.nukeOverlayElement()的作用是删除掉OverlayElement(包括子element),isCursorOver ()用来判断鼠标是否悬浮在gui的上方.需要注意的是这个函数判定包括了边框的判断.并且仅仅实现了矩形的判断.这个大概是因为目前所有的OverlayElment都是矩形的缘故.cursorOffset()获取鼠标离OverlayElement的中心有多少像素远.getCaptionWidth()用来获取一个textAreaOverlayElement中的caption的长度.caption应该指的是textAreaOverlayElment保存的文本.fitCaptionToArea()这个函数用来对具体的caption实现简单的格式化.,包括空格的长度以及根据gui长度实现裁剪.但是这个函数没有实现string太长时候的自动换行处理.接下来的4个virtual函数是回调函数, 用来实现鼠标对应事件响应处理.有意思的是这里没有keyboard对应的callback 这大概意味着目前的sdktray还没有实现响应键盘的功能.

接下的是一个继承自Widget的Button类.如同Ogre许多的部件,Ogre注释中说明,要求使用sdkmanager来创建具体的button.在button的构造函数中可以看到widget的OverlayElement 是使用OverlayManager::createOverlayElementFromTemplate()函数创建的.这个函数的参数是硬编码.这意味着如果我们要使用自己定义的外观,需要更改button的源代码.也许copy一个多出2个参数的构造函数是个不错的选择.button使用了一个有限状态机来实现了基类的4个callback.注意_cursorRelease()中调用了listener的buttonHit()函数,说明button响应点击的逻辑判断在鼠标释放的时候,并且没有对鼠标按下进行逻辑事件的处理.这也是sdktray不够完善的地方.

TextBox继承自Widget,注释说明是"Scrollable Text box",这个类实现了string超出gui宽度时候的换行处理.并且实现了一个下拉条.

为了实现这些功能,这个类包含了一个显示标题的borderPanel,包含标题的textArea,包含caption的textarea,2个实现下拉条效果的Borderpanel和panel.TextBox的构造函数主要就是读取各个子OverlayElement并设置对应的的属性.同button一样,许多属性的值都是硬编码.最后构造函数调用了一个refitContents的子函数.这个函数的功能是根据新设置的字体属性调用setText()重新排列文本.要复用的话,setText()是很好用的一个对外接口,这个函数设置需要显示的文本,并且将文本自动换行.setTextAlignment()实现文本对齐,appentText()实现字体追加.如果要自己实现打字输入,这个函数估计会有很大用途.但是这个函数每次都调用setext(),每次都对所有string重新计算位置,用来实现输入的话应该不是很有效率.setScrollPercentage()根据下拉的百分比来设置显示的字体.函数调用了filterLines(),这个子函数的功能是决定下拉的时候,哪些行的字体应该被显示._cursorPressed()响应了对下拉条点击的处理.需要注意的是 函数使用基类cursorOffset()来进行范围判定,当鼠标在设定的范围内的时候,下拉条才开始响应对应的事件.从demo来看,下拉条是一个圆形,我估计这个圆形的半径是9像素,所以函数中判断使用的数值是81(9的平方).

SelectMenu实现了下拉菜单的功能.getItems(),setItems(),clearItems(),是对所有下拉的组操作,addItem(),removeItem(),selectitem(),getSelectedItem()实现子操作.其中selectItem调用了listener的itemSelected()函数.

label大概是最简单的一个组件.需要注意的这个label在鼠标pressed的时候调用了listener的labelHit()函数.

separator没有响应任何逻辑事件和鼠标事件.也许是用来显示一些把什么东西分开的东西.

slider实现基本的滑块功能.这个用来实现在一定范围内值的选取.setRange() 确定值的上下限,getvalue() setValue()获取设置value.注意value的类型是Real.从setValue()函数实现来看,设置的是滑块的left,也就是说slider类 只能实现横向的滑块移动.

paramsPanel的功能是显示一个基于string类型的<key,value>.从实现上看,没有响应逻辑,也没有响应基本的鼠标事件,他的唯一功能就是显示key的value.程序中可以使用setvalue()来改变key对应的值.

checkBox 选择框.类似于单选框.一个checkBox框对应一个条目.有多个条目要实现多个checkBox.toggle()是一个对外接口,这个函数调用了listener的checkBoxToggled()函数.

DecorWidget 注释说明是用来实现用户自己的widget.这个DecorWidget什么都不做...如果要实现自己的widget,自己写一个继承自widget的类大概比较简明.

ProgressBar 进度条.包含3个部分,标题,正在读取的内容,以及显示进度用的显示条.显示条使用了2个overlayelement,其中一个根据百分比改变宽度,从而实现进度条效果.进度条没有响应任何事件.

sdkTrayManager是widget的管理器.不过看上去为了实现示例的效果这个manager多了许多的额外功能.如果不是使用示例的那个的话,很多功能似乎可以去掉.

构造函数中sdktrayManager实现了4个显示层.backdrop trays priority 以及 cursor 层.zorder依次从低到高.这里可以看出sdktrymanager是如何显示鼠标的.在一个单独的overlay层中使用一个overlayElement跟踪鼠标的位置,并实现对应显示.priority层的用途是显示一个dialogShade的OverlayElement,这个看上去是个对话框.但是dialogShade不是一个widget.不明白为什么不单独把这个功能分离出来,要整合到manager中去.接下来的代码设置了tray层.应该对应的是sample demo中对应的那个gui.出于复用的目的,不讨论tray相关的内容.

几个静态的工具函数:Ogre::Ray screneToscrene()和Ogre::Vector2 screneToScrene(),实现2D坐标和基于相机视口的3D射线之间的互换.refreshCursor()这个函数实现鼠标的更新,并且更新是基于ois unbuffered模式.我个人认为与鼠标有关的都应该独立出来作为一个类.getCursorRay()是screentoScreen()的再封装.接下来是各种widget对应的create函数.需要注意的是create函数将一个自己的mListener成员赋予了widget.这意味着响应事件的逻辑可以集中到manager的listener中,而不需要针对每个widget都写一个listener.

showFrameStates() 这个函数的功能替代了以前debugOverlay的功能.showLoadingBar()显示一个资源读取进度的进度条.showOKDialog() 显示一个有OK button的对话框.showYesNodialog()显示一个有yes no的对话框.这些功能也许都应该独立出来作为一个工具类.

frameRedneringQueued()响应了每帧循环.主要功能是清除不需要的widget以及更新framestates.

injectMouseDown() injectMouseUp() injectMouseMove()处理鼠标事件.overlay zorder 在前的overlayElement先获得处理.

总的来说,各种widget复用程度还是很高的,但是如果需要改变他们的显示的话,需要更改构造函数.sdkmanager的功能比较杂乱,如果需要实现自己的gui系统,应该将一些功能分拆出来.

你可能感兴趣的:(sdk)