doom3的UI系統

doom3的UI系統是純數據驅動的,例如

windowDef TextTitle2
{
     rect     20,341,600,55
     visible     1
     text     "#str_00073"
     forecolor     0.6,1,1,0
     textscale     0.8
     font     "fonts/micro"
     textalign     1
     notime     1

     onTime 0 {
          transition "forecolor" "1 1 1 0" "1 1 0.8 1" "500" ;
     }

     onTime 1000 {
          transition "forecolor" "1 1 0.8 1" "1 1 1 0" "500" ;
     }

     onTime 2000 {
          resetTime "0" ;
     }
}
這段文本定義了一個簡單的窗口。windowDef說明這是一個普通窗口,後面跟着這個窗口的名字。
其他的窗口類型定義還有editDef,choiceDef,sliderDef,listDef,fieldDef等等,都是在普通窗口的
基礎上增加了一些行爲和屬性,本文不打算詳述。

後面緊接着是一些窗口屬性值,這些屬性可以分爲兩類,InternalVarWinVar
InternalVar是一些常量,定義後就不能改變,例如textalignx,texaligny,bordersize,font等
而WinVar屬性,是在運行時可以被改變的屬性,例如

visible,
backColor,
matColor,
foreColor,
hoverColor,
borderColor,
textScale,
rotate,
text
等。這些值可以在後面要提到的GUI腳本中被改變。

onTime是一個event,event會在某種條件下被調用。例如onTime event是在時間到達後面的值(毫秒)時被調用。

調用執行的內容是中括號中描述的GUI腳本

event的類型有
onTime,
onMouseEnter ,
onMouseExit ,
onAction ,
onActivate ,
onDeactivate ,
onESC ,
onEvent ,
onTrigger ,
onActionRelease ,
onEnter ,
onEnterRelease

transition是一個GUI腳本命令,它描述了一個WinVar跟據時間被平滑的插值的過程

transition "forecolor" "1 1 1 0" "1 1 0.8 1" "500" ;
表示前景色forecolor在500毫秒內從 "1 1 1 0"過渡到"1 1 0.8 1"
到這裏你應該已經能夠看出來,這段文本數據描述了一個以兩秒爲週期不斷變換顏色的文字lable。


好,我們再來看一個複雜一點的例子
windowDef Desktop
{
     rect     0 ,0 ,640 ,480
     backcolor     0, 0, 0, 0.9
     windowDef CircleClamp
     {
          rect     7, 7, 626, 466
          visible     1
          windowDef Circle0
          {
               rect     -95,-174,820,820
               visible     1
               background     "gui/spin1"
               matcolor     0.5, 0, 0, 0.5
          }
     }
     windowDef TextButton2
     {
          rect     0,0,0,0
          text     "#str_00010"
          textscale     1.2
          forecolor     1,1,1,0
          visible     1
          textalign     1
          font     "fonts/micro"
          noevents     1

          onMouseEnter {
               transition "forecolor" "1 1 1 0.6" "1 1 1 1" "300" ;
               transition "btn2_top::matcolor" "1 1 1 1" "0.8 1 1 0.7" "300" ;
               transition "btn2_corner1::matcolor" "1 1 1 1" "0.4 1 1 0.7" "300" ;
               transition "btn2_corner2::matcolor" "1 1 1 1" "0.4 1 1 0.7" "300" ;
               transition "btn2_bottom::matcolor" "1 1 1 1" "0.4 1 1 0.7" "300" ;
               transition "btn2_corner3::matcolor" "1 1 1 1" "0.4 1 1 0.7" "300" ;
               transition "btn2_corner4::matcolor" "1 1 1 1" "0.4 1 1 0.7" "300" ;
               transition "btn2_right::matcolor" "1 1 1 1" "0.4 1 1 0.7" "300" ;
               transition "btn2_left::matcolor" "1 1 1 1" "0.4 1 1 0.7" "300" ;
               transition "btn2_fill::backcolor" "1 1 1 0.465" "0.4 1 1 0.32" "300" ;
          }

          onMouseExit {
               transition "forecolor" "1 1 1 1" "1 1 1 0.6" "200" ;
               transition "btn2_top::matcolor" "0.8 1 1 0.7" "1 1 1 0.5" "300" ;
               transition "btn2_corner1::matcolor" "0.4 1 1 0.7" "0.4 1 1 0.5" "300" ;
               transition "btn2_corner2::matcolor" "0.4 1 1 0.7" "0.4 1 1 0.5" "300" ;
               transition "btn2_bottom::matcolor" "0.4 1 1 0.7" "0.4 1 1 0.5" "300" ;
               transition "btn2_corner3::matcolor" "0.4 1 1 0.7" "0.4 1 1 0.5" "300" ;
               transition "btn2_corner4::matcolor" "0.4 1 1 0.7" "0.4 1 1 0.5" "300" ;
               transition "btn2_right::matcolor" "0.4 1 1 0.7" "0.4 1 1 0.5" "300" ;
               transition "btn2_left::matcolor" "0.4 1 1 0.7" "0.4 1 1 0.5" "300" ;
               transition "btn2_fill::backcolor" "0.4 1 1 0.32" "0.4 1 1 0.232" "300" ;
          }

          onAction {
               if ("gui::gui_parm4" == 1) {
                    set     "cmd" "play guisounds_error" ;
                    resetTime "AccessDenied" "0" ;
                    set "noevents" "1" ;
               } else {
                    set     "cmd" "activate ; play guisounds_click" ;
                    resetTime "AccessLocked" "0" ;
                    set "noevents" "1" ;
               }
          }

     }
     windowDef Static
     {
          rect     -10 ,-10 ,-660 ,500
          visible     1
          background     "gui/static"
          matcolor     1, 1, 1, pdhalffade[ time * 0.001 ] / 8
     }
}
首先,ui的定義是嵌套的,實際上,遊戲中的用到的ui都是比較複雜的層次結構。任意多層的各種組件嵌套組合起來,理論上
可以形成任意複雜的UI效果。
然後我們看到了更多的event的使用,onMouseEnter,和onMouseExit實現了鼠標移入移出時的變化效果。
在onAction event中我們看到GUIScript中是可以帶邏輯的,不過GUIScript中也只有“if”這一種邏輯。

GUI腳本中的命令有
set,
setFocus,
endGame,
resetTime,
showCursor,
resetCinematics,
transition,
localSound,
runScript,
evalRegs,

set命令可以直接設置某個WinVar。比如

set "Circle7::visible" "0" ;

將窗口Circle7的WinVar visible的值設爲0,即隱藏這個窗口。
但如果set的參數是cmd,則其功能是讓觸發這個GUI event的對象執行後面的GUI command,可謂“腳本套腳本”,例如
一個player對象(某個玩家或NPC),觸發了這個onEvent,event中調用了

set     "cmd" "activate ; play guisounds_click" ;
則這個player對象執行GUI command

activate
play guisounds_click

效果是,這個player作爲activator激活了這個GUI所在的entity,並播放音效guisounds_click

常用的GUI command有
activate
runScript
play
setkeyval
setshaderparm等

最後.....

matcolor     1, 1, 1, pdhalffade[ time * 0.001 ] / 8
WinVar的值可以是一個表達式並且還能查表有木有


總結
doom3引擎的UI系統通過WinVar,GUI event,GUI Script,GUI Command等幾個核心抽象,用最少的代碼,
實現了極其靈活而強大的功能。

發表評論
所有評論
還沒有人評論,想成為第一個評論的人麼? 請在上方評論欄輸入並且點擊發布.
相關文章