HGE Tutorial 06

教學 06 – 選單製作

這個教學將展示如何製作自訂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;
};

組件的建構元(Constructor)需要初始化「hgeGUIObject」的成員資料 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 更新和動畫更新時呼叫。在這範例中,我們使用兩個計時器(timers),根據時間控制組件的顏色和位置:

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」方法是在組件剛出現於螢幕上時呼叫。組件的起始動畫應該會從這裡啟動:

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」是用來測試組件的(進入/離開)動畫是否已經完成。如果動畫已完結,則會回傳ture:

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」方法會再滑鼠游標(進入/離開)組件作用範圍時呼叫。當滑鼠移入組件範圍時,這裡我們只將GUI的目標設置在組件上:

void hgeGUIMenuItem::MouseOver(bool bOver)
{
  if(bOver) gui->SetFocus(id);
}

MouseLButton」方法會再滑鼠左鍵的狀態改變時呼叫。當組件改變狀態並且想要提醒呼叫者時,應該回傳ture:

bool hgeGUIMenuItem::MouseLButton(bool bDown)
{
  if(!bDown)
  {
    offset=4;
    return true;
  }
  else 
  {
    hge->Effect_Play(snd);
    offset=0;
    return false;
  }
}

KeyClick」方法是當某個按鍵被點擊(click)時會通知控制組件。當組件改變狀態並且想要提醒呼叫者時,應該回傳ture:

bool hgeGUIMenuItem::KeyClick(int key, int chr)
{
  if(key==HGEK_ENTER || key==HGEK_SPACE)
  {
    MouseLButton(true);
    return MouseLButton(false);
  }

  return false;
}

OK,現在我們定義好了組件行為。

使用GUI

這是精簡的一部份,首先我們需要一些資源手柄(resource handles):

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」方法來更新動畫和處理使用者輸入。如果有組件狀態改變,則會回傳該組件的訊息通知者(identificator)。如果所有組件都完成他們的離開動畫,則回傳-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);

完整的程式碼可以在「tutorials\tutorial06」資料夾底下找到。所需的資源檔在「tutorials\precompiled」底下。

 

※handle: 其實一直不知道要怎樣翻譯才好,對岸大多翻譯為「句柄」,總之就是用來代表某個東西的位置或編號之類的代號。在這裡我就翻成「手柄」比較直接的名稱,就是「啊~拉下這個手柄就會牽連到對應的裝置」的這種感覺吧!其實不用太在意這個翻譯啦~XD

發表迴響

在下方填入你的資料或按右方圖示以社群網站登入:

WordPress.com Logo

You are commenting using your WordPress.com account. Log Out / 變更 )

Twitter picture

You are commenting using your Twitter account. Log Out / 變更 )

Facebook照片

You are commenting using your Facebook account. Log Out / 變更 )

Google+ photo

You are commenting using your Google+ account. Log Out / 變更 )

連結到 %s