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等几个核心抽象,用最少的代码,
实现了极其灵活而强大的功能。

你可能感兴趣的:(doom3的UI系统)