2012年10月23日 星期二

關於GUI函式Bind及數值顯示處理…

常見的做法都是判斷GUI名稱來進行相關處理,比方…
m_pHPBar = GUIManager.GetUIObject("HPBar");
m_pHPBar->SetPos((CurrentHp/MaxHp)*BarRange);

OnButtonClieck(GUI* pGui)
{
  if(pGui->GetName() == "StartGame")
    DoStartGame();
}

好一點的話,只是把Name Bind到函式,但這都是寫死的方式,企劃如果要調整名稱或重新做新的UI,這些工作都要再來過,而且一定要透過程式才能進行相關測試,非常麻煩…

如果函式及顯示的綁定可以交由企劃做,就像魔獸的GUI可以讓User自行編輯一樣,或許狀況可以改善一些,以下是用MyGUI處理顯示方面的例子:

1.透過MyGUI的UserString來蒐集處理的GUI元件:
void onAddUserString(MyGUI::WidgetPtr _sender, const std::string& _key, const std::string& _value)
{
const std::string& strTypeName = _sender->getTypeName();
if((strTypeName == "TextBox") ||
(strTypeName == "ProgressBar"))
{
CMappingInformation Data;
Data.m_strValue = _value;
Data.m_pTargetWidget = _sender;
m_mapUserNameMapping[_key].push_back(Data);
}
}
2.規劃顯示處理的函式:

bool CGameGUIManager::SetReferenceGUICaption(const std::string& strReferenceName, const std::string& strValue)
{
assert(m_pImpl);
std::map>::iterator NameIt = 
m_pImpl->m_mapUserNameMapping.find(strReferenceName);
MyGUI::ProgressBar* pTargetProgressBar;
if(NameIt != m_pImpl->m_mapUserNameMapping.end())
{
std::vector::iterator InfoIt = (*NameIt).second.begin();
while(InfoIt != (*NameIt).second.end())
{
const std::string& strTypeName = (*InfoIt).m_pTargetWidget->getTypeName();
if(strTypeName == "TextBox")
(*InfoIt).m_pTargetWidget->castType(false)->setCaption(strValue);
else if(strTypeName == "ProgressBar")
{
pTargetProgressBar = (*InfoIt).m_pTargetWidget->castType(false);
size_t szRange = pTargetProgressBar->getProgressRange();
size_t szPos = strValue.find("/");
unsigned int uiCurrentValue, uiMaxValue;
uiCurrentValue = uiMaxValue = 0;
if(szPos == std::string::npos)
uiCurrentValue = atoi(strValue.c_str());
else
{
std::string strTempString;
strTempString.assign(strValue, 0, szPos);
uiCurrentValue = atoi(strValue.c_str());
strTempString.assign(strValue.begin()+szPos+1, strValue.end());
uiMaxValue = atoi(strTempString.c_str());
}
pTargetProgressBar->setProgressPosition(
(size_t)((float)uiCurrentValue/(float)uiMaxValue*(float)szRange));
}
++InfoIt;
}
return true;
}
return false;
}
3.實際運用的相關程式碼:

std::string CGameGUITestAPP::GetHPDataString(void)
{
char TempBuffer[64];
sprintf(TempBuffer, "%d", m_uiCurrentHP);
std::string strOutString = TempBuffer;
sprintf(TempBuffer, "%d", c_uiMaxHp);
return strOutString + "/" + TempBuffer;
}
SingletonGameGUIManager::Instance().SetReferenceGUICaption("ShowHP", GetHPDataString());

這樣一來,程式可以先把ShowHp的功能先寫好,至於企劃MyGUI的Layout檔什麼時候出來並不會影響到整合測試,企劃也可以利用程式提供的ShowHP這個字串,來綁定顯示功能到他想要的UI元件(此範例目前只支援TextBox及ProgressBar…),而不用透過程式。

相關參考資料:mygui与luaplus的绑定

夢想還在持續中…


2 則留言:

  1. 分享一下記憶中多年前的作法:

    用xml或lua檔案設定gui元件屬性:
    callback => the lua function name, 有click callback, mouse over callback等等
    params => parameters for the callback
    observer => 程式設定好有幾種subjects (比如說hp, mp), subjects改變時(hp, mp有變化時), subjects會通知他的observer, ex:

    subject:
    on change {subject.observers.each.notify(subject)}

    observer:
    on notify(subject) {... do something you want}

    以上都可以用lua做掉, 除了subjects有變化時(hp/mp...), 從c call lua function to notify all observer gui components

    ---
    Alan

    回覆刪除
  2. 感謝你的回應,的確用LUA幾乎什麼事都能做,不過可能要避免Per Frame Code的部份,畢竟Call LUA funciton也是有成本的。

    回覆刪除