![](http://static.blog.csdn.net/images/category_icon.jpg)
这里的界面是指菜单、HUD及提示信息框在内的综合体。
基本概念
Overlay
将被渲染在“普通”场景内容之上的层。Overlay是那些将在主场景被渲染之后才渲染的可视组件的容器。这些可视组件将构成HUD(heads-up-display)、菜单或其它在主场景内容之上的任何东西。
一个Overlay总是占满一个viewport的全部尺寸,尽管它包含的组件并没有那么大,那么多。Overlay并不代表任何可视组件,它只是一个可视组件的容器。
Overlay可以通过调用SceneManager::createOverlay函数来创建,或者把它们定义在一个脚本文件里(.overlay files),引擎会自动解析这些脚本,并创建它们。你可以定义无限多个Overlay。一个overlay被创建之后是不可见的,你必须调用show函数让它们显示出来。这样你就可以预先定义很多Overlay(比如菜单),在需要它们的时候才将它们显示出来。在同一时刻可以存在和显示多个Overlay,Overlay的zorder属性决定了谁在上谁在下。
缺省情况下,Overlay会被渲染到全部的viewport。当你只有一个全屏viewport的时候这非常好用,但是当你在程序中用几个viewport构成“画中画”的时候,你可能不想在小画面中也显示Overlay。你可以通过调用viewport的Viewport::setDisplayOverlays方法将某些viewport的Overlay显示状态关闭。
Overlay支持旋转、卷动和缩放。通过Overlay::scroll, Overlay::rotate and Overlay::scale这几个函数可以达到目的。
Overlay中可以包括2D元素,也可以包括3D元素。
GuiElement
将在Overlay 中被显示的2D元素。这个类抽象了在overlay中出现的2D元素的全部信息。事实上,并非所有的GuiElement的实例都可以被加入到Overlay中,只有GuiContainer类(GuiElement类的派生类)的实例才可以。GuiContainer对象可以包含GuiElement对象。也就是说一般情况下,Overlay包含一个或多个GuiContainer,而GuiContainer包含GuiElement。
GuiElements被GuiManager管理。GuiManager负责创建和删除GuiElements,并且负责从plugins接收新类型的GuiElements。
GuiElements属性中的位置和尺寸表达方式(标尺模式)有两种:Pixel Mode模式和Relative Mode模式。当采用Pixel Mode模式时,位置和尺寸用Pixel为单位。用这种方式屏幕元素的位置和大小将与屏幕分辨率有关。例如:800*600分辨率下,某个GuiElements的左上角坐标为10,10,大小为50,50。那么它将显示在屏幕左上方。但当分辨率变为1024*768的时候,它的位置将向左上移动一点,且看起来会变小。当采用Relative Mode模式时,GuiElements中的位置和尺寸是用0.0 - 1.0的参数表达的,是一个和屏幕宽度及高度的比值。这样做的方式是为了使这些值与显示分辨率无关,从而使显示结果可以适应任何分辨率。例如:0.5x0.5的尺寸代表屏幕宽和高的一半大,此外请注意,0.5x0.5的尺寸在屏幕上显示出来并不是一个正方形,因为屏幕本身就不是正方形。
GuiElements被设计成可扩展的,它是StringInterface类的子类,所以它的参数也是通用参数。
我们可以继承GuiElements类,实现自己的功能复杂GuiElements。一般情况下通过写plugin来扩展GuiElements。
GuiContainer
可以包含其它GuiElement实例的特殊GuiElement。GuiContainer类其实是GuiElement类的派生类。它也是可以被直接attach到Overlay上的最小元素。GuiContainers也由GuiManager管理。GuiManager负责创建和删除GuiElements,并且负责从plugins接收新类型的GuiElements。
向Overlay加入一个GuiContainer的方法很简单,调用Overlay的add2D函数就可以了。
向一个GuiContainer加入子元素的方法也很简单,调用GuiContainer的addChild函数就可以了。子元素可以是GuiContainer,也可以是GuiElements。通过一级一级加入子元素可以构成一个树状结构。需要注意的是,子元素的位置是相对于父元素的。
GuiElementFactory
GuiElementFactory:创建GuiElement的工厂类。该类是一个抽象类,任何一个具体GuiElement的工厂都必须继承它,并实现其中的接口。
GuiManager
它的任务是管理GuiElement(及其子类)的实例的生命周期,并从plugin模块中注册新的GuiElement类型。
TextAreaGuiElement
2D界面元素,GuiElement的直接派生类,定义界面中的文本区域。
PanelGuiElement
2D界面元素,GuiContainer的直接派生类,定义界面中的面板区域,在面板中还能包括其它界面元素。
BorderPanelGuiElement
2D界面元素,GuiContainer的直接派生类,定义界面中的带边面板区域,在带边面板中还能包括其它界面元素。
ButtonGuiElement
2D界面元素,BorderPanelGuiElement的直接派生类,定义界面中的按钮。
ListGuiElement
2D界面元素,PanelGuiElement的直接派生类,定义界面中的列表选择框。
GUI静态结构图
Overlay脚本
Overlay脚本为我们提供了使用脚本来定义可重用的overlays的手段。
载入脚本
Overlay脚本在系统初始化的时候被载入。缺省情况下,系统会把公共资源路径(参考Root::addResourceLocation)下所有扩展名为'.overlay'的文件都载入并解析。如果你的Overlay脚本使用了其它扩展名,你可以使用OverlayManager::getSingleton().parseAllSources函数载入它们,如果你想载入单个Overlay脚本,就使用OverlayManager::getSingleton().parseScript函数。
格式
可以在一个脚本文件中定义多个overlay。脚本采用伪C++格式,用{ }分块,注释采用‘//’(注意不支持注释嵌套),而且支持模板继承。以下是一个典型的实例:
// The name of the overlay comes first
MyOverlays/ANewOverlay
{
zorder 200
Container Panel(MyGuiElements/TestPanel)
{
// Center it horzontally, put it at the top
left 0.25
top 0
width 0.5
height 0.1
material MyMaterials/APanelMaterial
// Another panel nested in this one
container Panel(MyGuiElements/AnotherPanel)
{
left 0
top 0
width 0.1
height 0.1
material MyMaterials/NestedPanel
}
}
}
以上的例子定义了一个名为“MyOverlays/ANewOverlay”的overlay,其中包括2个嵌套的panels。它使用缺省的相对标尺模式(用0-1的数来表示长度和位置,0-1的数代表与屏幕宽度和高度的比值)。
脚本中的每一个overlay都必须有名字,且必须在“{”之前一行指定。名字不能重复。名字中可以包含“路径”格式(用/分割),用以区分层次和避免重名,但OGRE引擎并不把这些名字解析成一个层次结构,它仅仅是一个字符串。
在大括号中间是overlay的属性和其他嵌套的元素。本例中,overlay只有一个'zorder'属性,'zorder'用于为重叠的overlay区分覆盖关系,zorder值大的将渲染在上面。
在overlay中加入元素
在overlay里可以包括2D和3D元素。这些元素必须以如下关键字开头:
'element' 不能再嵌套其它元素的2D元素。
'container' 可嵌套2D元素和container的容器。
'entity' 3D元素,只能放在overlay里,不能嵌套在其它容器中。
通过'container'可以实现2D元素的层层嵌套。
'container' 和 'element' 块
以如下格式开头:
[container | element] <type_name> ( <instance_name>) [: <template_name>]
{ ...
type_name::GuiElement的类型名,必须在GuiManager中注册。OGRE引擎的Plugin_GuiElements.dll中已提供的类型有:Panel、 BorderPanel 、TextArea、Button和List。你可以自己写Plugin扩充其它类型。
instance_name:标识本元素的唯一的名称。可以通过GuiManager::getSingleton().getGuiElement(name)获取到指定名称的元素指针。
template_name::可选参数,指定模板名称。
块中的属性取决于每个元素类型,以下属性是每个元素都有的:
metrics_mode 标尺模式
horz_align 横向对齐
vert_align 纵向对齐
left 左边位置
top 上边位置
width 宽度
height 高度
material 材质
caption 标题
TextArea特有的属性:
font_name 字体名
char_height 字符高度
colour_top 上部颜色
colour_bottom 下部颜色
BorderPanel特有的属性:
border_size
border_material
border_topleft_uv
border_top_uv
border_topright_uv
border_left_uv
border_right_uv
border_bottomleft_uv
border_bottom_uv
border_bottomright_uv
Button特有的属性:
border_up_material 按钮弹起状态材质
border_down_material 按钮按下状态材质
List特有的属性:
item_template 选项文字模板
v_spacing 选项间的纵向距离
h_spacing
item_material_selected 选项被选中后的材质
'entity' 块
必须以以下格式开头:
entity <mesh_name> ( <entity_name>)
{ ...
mesh_name: .mesh文件名
entity_name: 唯一的entity名
你还可以定义如下属性
position
rotation
注意:材质名可以从.mesh文件中读出,当然你可以指定为.material中定义的其它材质。
Templates
你可以使用模板来定义大量具有相同属性的元素。模板是不能加入到overlay中的抽象元素。各种元素可以继承它并获得它定义的缺省属性。在元素(container, element, or entity)的定义前加'template'关键字就可以定义模板。模板元素一般定义在脚本文件的开头,而且不能定义在Overlay里面。建议把模板元素定义在独立的脚本文件中,这样可以很容易实现重用和个性化调整。
元素可以象C++那样用“:”符号继承自模板。“:”放在元素名称的右括号后。模板名称又放在“:”符号后。
A template can contain template children which are created when the template is subclassed and instantiated. Using the template keyword for the children of a template is optional but recommended for clarity, as the children of a template are always going to be templates themselves.
template container BorderPanel(MyTemplates/BasicBorderPanel)
{
left 0
top 0
width 1
height 1
// setup the texture UVs for a borderpanel
// do this in a template so it doesn't need to be redone everywhere
material Core/StatsBlockCenter
border_size 0.05 0.05 0.06665 0.06665
border_material Core/StatsBlockBorder
border_topleft_uv 0.0000 1.0000 0.1914 0.7969
border_top_uv 0.1914 1.0000 0.8086 0.7969
border_topright_uv 0.8086 1.0000 1.0000 0.7969
border_left_uv 0.0000 0.7969 0.1914 0.2148
border_right_uv 0.8086 0.7969 1.0000 0.2148
border_bottomleft_uv 0.0000 0.2148 0.1914 0.0000
border_bottom_uv 0.1914 0.2148 0.8086 0.0000
border_bottomright_uv 0.8086 0.2148 1.0000 0.0000
}
template container Button(MyTemplates/BasicButton) : MyTemplates/BasicBorderPanel
{
left 0.82
top 0.45
width 0.16
height 0.13
material Core/StatsBlockCenter
border_up_material Core/StatsBlockBorder/Up
border_down_material Core/StatsBlockBorder/Down
}
template element TextArea(MyTemplates/BasicText)
{
font_name Ogre
char_height 0.08
colour_top 1 1 0
colour_bottom 1 0.2 0.2
left 0.03
top 0.02
width 0.12
height 0.09
}
MyOverlays/AnotherOverlay
{
zorder 490
container BorderPanel(MyElements/BackPanel) : MyTemplates/BasicBorderPanel
{
left 0
top 0
width 1
height 1
container Button(MyElements/HostButton) : MyTemplates/BasicButton
{
left 0.82
top 0.45
caption MyTemplates/BasicText HOST
}
container Button(MyElements/JoinButton) : MyTemplates/BasicButton
{
left 0.82
top 0.60
caption MyTemplates/BasicText JOIN
}
}
}
以上的例子使用模板来创建一个按钮。注意到button模板继承自borderPanel模板,这减少了创建按钮时需要设置的属性。
还注意到Button的实例用模板名来设置caption属性。模板也可以被elements用于动态创建子elements(按钮创建了一个TextAreaElement来作为自己的caption)。
Overlay实例一
查看OGRE运行环境中的Ogre.overlay文件,在OGRE自带的DEMO中都使用了这个文件中定义的overlay。
FPS的数据是如何显示到overlay中的????
Overlay实例二
在Ogre.overlay的基础上,加两个新的overlay。其中包括一个OGRE的LOGO。另一个overlay包括一个3D元素。程序运行时包括LOGO的overlay来回运动。
思路
overlay中即可以包含2D元素也可以包含3D元素。通过overlay的setScroll函数可以其运动。
部分代码
Ogre.overlay文件中加入如下脚本,注意备份原文件。
// A silly example of how you would do a 3D cockpit
Examples/KnotCockpit
{
zorder 100
entity knot.mesh(hudKnot)
{
position 0 0 -50
rotation 0 0 0 0
}
}
// another logo overlay
Examples/MyLogo
{
zorder 100
container Panel(Examples/LogoPanel)
{
metrics_mode pixels
horz_align right
vert_align top
top 20
left -165
width 160
height 85
material Core/OgreText
}
}
这部分脚本定义了两个Overlay。一个带3D元素一个带2D元素。
以下是myapp.h文件
#include "ExampleApplication.h"
class myFrameListener : public ExampleFrameListener
{
protected:
Overlay * pMyOverlayLogo;
public:
myFrameListener(RenderWindow* win, Camera* cam)
: ExampleFrameListener(win, cam)
{
pMyOverlayLogo = (Overlay*) OverlayManager::getSingleton().getByName("Examples/MyLogo");
pMyOverlayLogo->show();
}
// 重新实现frameStarted函数,在这里控制Overlay的运动
bool frameStarted(const FrameEvent& evt)
{
static float angle = 0.0;
if( angle >= Math::TWO_PI)
angle = 0.0;
angle += 0.1;
// 在X轴按正弦函数方式运动
pMyOverlayLogo->setScroll(Math::Sin(angle),0);
// 不要忘了,调用基类的frameStarted函数,以实现用户输入控制(摄象机漫游控制)。
return ExampleFrameListener::frameStarted(evt);
}
};
class EnvMapApplication : public ExampleApplication
{
public:
EnvMapApplication() {}
protected:
// Just override the mandatory create scene method
void createScene(void)
{
// Set ambient light
mSceneMgr->setAmbientLight(ColourValue(0.5, 0.5, 0.5));
// Create a point light
Light* l = mSceneMgr->createLight("MainLight");
// Accept default settings: point light, white diffuse, just set position
// NB I could attach the light to a SceneNode if I wanted it to move automatically with
// other objects, but I don't
l->setPosition(20,80,50);
Entity *ent = mSceneMgr->createEntity("head", "ogrehead.mesh");
// Set material loaded from Example.material
ent->setMaterialName("Examples/EnvMappedRustySteel");
// Add entity to the root scene node
mSceneMgr->getRootSceneNode()->createChild()->attachObject(ent);
// 获取指定名称的Overlay指针
Overlay * pMyOverlayLogo = (Overlay*) OverlayManager::getSingleton().getByName("Examples/MyLogo");
// 缺省情况下Overlay被载入后是不显示的,让其显示。
pMyOverlayLogo->show();
// 获取指定名称的Overlay指针
Overlay * pMyOverlayKnot = (Overlay*) OverlayManager::getSingleton().getByName("Examples/KnotCockpit");
// 缺省情况下Overlay被载入后是不显示的,让其显示。
pMyOverlayKnot->show();
}
void createFrameListener(void)
{
mFrameListener= new myFrameListener(mWindow, mCamera);
mRoot->addFrameListener(mFrameListener);
}
};
OGRE会在程序运行时自动载入.overlay文件,缺省情况下是不显示的。在OGRE应用框架的ExampleApplication类中把DebugOverlay的显示开关打开了,其它的overlay必须由程序员手工打开。以上程序在createScene函数中通过OverlayManager::getSingleton().getByName()函数获取到overlay的指针,再通过show函数打开显示开关。
Overlay提供滚动、旋转和缩放函数,在FrameListener控制其滚动很好地达到了动画效果。
实现界面事件处理
OGRE引擎已经将一些界面元素的基本事件处理完成,例如:按钮被点击时的动作效果、List列表中选项被选择后的特殊显示等。但是按钮被按下后还要完成什么事情需要程序员自己完成,例如Exit按钮被按下后程序要退出、Option按钮被按下后要进入系统设置菜单等。
OGRE的事件处理目前还未全部完成,请暂时参考OGRE的Demo_Gui。
本文来自CSDN博客,转载请标明出处:http://blog.csdn.net/pizi0475/archive/2010/04/05/5451304.aspx