这个教程显示如何创建通用的GUI控制和菜单。
创建通用控制
首先我们定义控制类,从hgeGUIObject继承:
class hgeGUIMenuItem : public hgeGUIObject
{
public:
hgeGUIMenuItem(int id, hgeFont *fnt, HEFFECT snd,
float x, float y, float delay, char *title);
virtual void Render();
virtual void Update(float dt);
virtual void Enter();
virtual void Leave();
virtual bool IsDone();
virtual void Focus(bool bFocused);
virtual void MouseOver(bool bOver);
virtual bool MouseLButton(bool bDown);
virtual bool KeyClick(int key, int chr);
private:
hgeFont *fnt;
HEFFECT snd;
float delay;
char *title;
hgeColor scolor, dcolor, scolor2, dcolor2, color;
hgeColor sshadow, dshadow, shadow;
float soffset, doffset, offset;
float timer, timer2;
};
构造函数初始化数据成员id, bStatic, bVisible, bEnabled 和rect:
hgeGUIMenuItem::hgeGUIMenuItem(int _id, hgeFont *_fnt,
HEFFECT _snd, float _x, float _y,
float _delay, char *_title)
{
id=_id;
fnt=_fnt;
snd=_snd;
delay=_delay;
title=_title;
color.SetHWColor(0xFFFFE060);
shadow.SetHWColor(0x30000000);
offset=0.0f; timer=-1.0f; timer2=-1.0f;
bStatic=false; bVisible=true; bEnabled=true;
float w=fnt->GetStringWidth(title);
rect.Set(_x-w/2, _y, _x+w/2, _y+fnt->GetHeight());
}
Render函数是重点,每个控制都要定义它。
void hgeGUIMenuItem::Render()
{
fnt->SetColor(shadow.GetHWColor());
fnt->Render(rect.x1+offset+3, rect.y1+3, HGETEXT_LEFT, title);
fnt->SetColor(color.GetHWColor());
fnt->Render(rect.x1-offset, rect.y1-offset, HGETEXT_LEFT, title);
}
所有其他方法都是可选的,你可以不定义。
Update在每次GUI更新并需要渲染时调用。在这例子中我们有两个定时器根据时间调节颜色和位置,
void hgeGUIMenuItem::Update(float dt)
{
if(timer2 != -1.0f)
{
timer2+=dt;
if(timer2 >= delay+0.1f)
{
color=scolor2+dcolor2;
shadow=sshadow+dshadow;
offset=0.0f;
timer2=-1.0f;
}
else
{
if(timer2 < delay) { color=scolor2; shadow=sshadow; }
else {
color=scolor2+dcolor2*(timer2-delay)*10;
shadow=sshadow+dshadow*(timer2-delay)*10;
}
}
}
else if(timer != -1.0f)
{
timer+=dt;
if(timer >= 0.2f)
{
color=scolor+dcolor;
offset=soffset+doffset;
timer=-1.0f;
}
else
{
color=scolor+dcolor*timer*5;
offset=soffset+doffset*timer*5;
}
}
}
Enter在GUI将要出现在屏幕上时调用。一个控制动画应该在这里进入:
void hgeGUIMenuItem::Enter()
{
hgeColor tcolor2;
scolor2.SetHWColor(0x00FFE060);
tcolor2.SetHWColor(0xFFFFE060);
dcolor2=tcolor2-scolor2;
sshadow.SetHWColor(0x00000000);
tcolor2.SetHWColor(0x30000000);
dshadow=tcolor2-sshadow;
timer2=0.0f;
}
Leave在GUI要从屏幕消失时调用。
void hgeGUIMenuItem::Leave()
{
hgeColor tcolor2;
scolor2.SetHWColor(0xFFFFE060);
tcolor2.SetHWColor(0x00FFE060);
dcolor2=tcolor2-scolor2;
sshadow.SetHWColor(0x30000000);
tcolor2.SetHWColor(0x00000000);
dshadow=tcolor2-sshadow;
timer2=0.0f;
}
IsDone用来检测这个控制是否完成Enter/Leave动画。完成返回true。
bool hgeGUIMenuItem::IsDone()
{
if(timer2==-1.0f) return true;
else return false;
}
Focus当控制获得或失去键盘输入焦点时调用。
void hgeGUIMenuItem::Focus(bool bFocused)
{
hgeColor tcolor;
if(bFocused)
{
hge->Effect_Play(snd);
scolor.SetHWColor(0xFFFFE060);
tcolor.SetHWColor(0xFFFFFFFF);
soffset=0;
doffset=4;
}
else
{
scolor.SetHWColor(0xFFFFFFFF);
tcolor.SetHWColor(0xFFFFE060);
soffset=4;
doffset=-4;
}
dcolor=tcolor-scolor;
timer=0.0f;
}
MouseOver用来通知鼠标指针进入或离开它的区域。这里我们只设置输入焦点到控制当鼠标经过它时。
void hgeGUIMenuItem::MouseOver(bool bOver)
{
if(bOver) gui->SetFocus(id);
}
MouseLButton当左键状态改变时调用。如果控制改变它的状态并希望通知调用者,应该返回true:
bool hgeGUIMenuItem::MouseLButton(bool bDown)
{
if(!bDown)
{
offset=4;
return true;
}
else
{
hge->Effect_Play(snd);
offset=0;
return false;
}
}
KeyClick用来通知控制一个键被点击。如果控制改变它的状态并希望通知调用者,应返回true:
bool hgeGUIMenuItem::KeyClick(int key, int chr)
{
if(key==HGEK_ENTER || key==HGEK_SPACE)
{
MouseLButton(true);
return MouseLButton(false);
}
return false;
}
现在我们有通用的控制行为定义了。
使用GUI:
这里是简单部分。首先我们需要资源变量:
HEFFECT snd;
HTEXTURE tex;
hgeGUI *gui;
hgeFont *fnt;
hgeSprite *spr;
WinMain中,在初始化过程中载入资源:
snd=hge->Effect_Load("menu.wav");
tex=hge->Texture_Load("cursor.png");
fnt=new hgeFont("font1.fnt");
spr=new hgeSprite(tex,0,0,32,32);
现在创建GUI并添加菜单部件。GUI控制在内部处理,你不需要关心。
gui=new hgeGUI();
gui->AddCtrl(new hgeGUIMenuItem(
1,fnt,snd,400,200,0.0f,"Play"));
gui->AddCtrl(new hgeGUIMenuItem(
2,fnt,snd,400,240,0.1f,"Options"));
gui->AddCtrl(new hgeGUIMenuItem(
3,fnt,snd,400,280,0.2f,"Instructions"));
gui->AddCtrl(new hgeGUIMenuItem(
4,fnt,snd,400,320,0.3f,"Credits"));
gui->AddCtrl(new hgeGUIMenuItem(
5,fnt,snd,400,360,0.4f,"Exit"));
现在我们设置GUI模式,鼠标图象和默认键盘焦点,然后开始进入动画:
gui->SetNavMode(HGEGUI_UPDOWN | HGEGUI_CYCLED);
gui->SetCursor(spr);
gui->SetFocus(1);
gui->Enter();
现在看看我们如何更新菜单和接收通知。在FrameFunc中我们调用hgeGUI::Update来更新状态,它返回控制的鉴定值。如果所有的控制都完成离开动画,它返回-1。如果没有事情发生返回0。
int id;
static int lastid=0;
float dt=hge->Timer_GetDelta();
id=gui->Update(dt);
if(id == -1)
{
switch(lastid)
{
case 1:
case 2:
case 3:
case 4:
gui->SetFocus(1);
gui->Enter();
break;
case 5: return true;
}
}
else if(id) { lastid=id; gui->Leave(); }
在RenderFunc中调用hgeGUI::Render来渲染菜单:
hge->Gfx_BeginScene();
gui->Render();
hge->Gfx_EndScene();
菜单出现并运行。现在来到WinMain。程序结束时删除GUI并释放资源。
delete gui;
delete fnt;
delete spr;
hge->Texture_Free(tex);
hge->Effect_Free(snd);