笔记: 向XML框架添加行为, 框架模板

Posted on 2009-06-27 02:37  apex.Cliz  阅读(1051)  评论(0)    收藏  举报

只包含基本的框架、图形、字串的界面是静态的,无法和用户进行交互,因此,必须通过添加动态行为来编写真正可用的插件。


事件及脚本

用户界面通过游戏中的事件来改变其状态,并且通过框架的脚本同用户进行交互。

框架脚本

每一种框架都可以对一些特定的行为作出反应,例如,按钮可以对点击作出反应,而文本输入框则可以对输入文字作出反应。相对应的,在实际的XML代码中,就可以用<OnClick>和<OnValueChanged>标签携带的脚本来定义框架的反应行为。

游戏事件

游戏事件是客户端向用户界面发送的信息,通常由战斗、交互行为等触发。例如,当任何关注目标(包括玩家角色的目标、焦点、队伍或团队成员)生命值发生变动时,就会触发UNIT_HEALTH事件。

为了监听事件,框架需要使用:RegisterEvent()方法注册到客户端。在注册后,每当事件被触发,客户端都将呼叫框架的<OnEvent>脚本。每个框架都可以监听一个或以上的事件,但只能有一个<OnEvent>脚本。游戏客户端会将事件名称传递给<OnEvent>脚本并由其应对不同类型的事件。


通过脚本应对事件

上述的事件脚本都放置在每个XML Frame的<Scripts>元素当中。对于每种事件,都可以定义一个脚本。

<OnEnter> / <OnLeave>

任何一个可以对鼠标事件作出反应的框架(即是说,没有将enableMouse属性字段设为false的框架)均可以对<OnEnter>及<OnLeave>事件作出反应。下面的代码,对两种事件作出反应,并且在聊天窗口输出信息。

<Frame name="EnterLeaveTest" parent="UIParent">
  
<Size x="100" y="100" />
  
<Anchors>
    
<Anchor point="CENTER" relativePoint="CENTER"
     relativeTo
="UIParent" />
  
</Anchors>
  
<Layers>
    
<Layer level="BACKGROUND">
      
<Texture name="$parentIcon"
       file
="Interface\Icons\Spell_Shadow_ShadowWordPain"
       setAllPoints
="true" />
    
</Layer>
  
</Layers>
  
<Scripts>
    
<OnEnter>
      ChatFrame1:AddMessage("++ Entered frame: " .. self:getName())
    
</OnEnter>
    
<OnLeave>
      ChatFrame1:AddMessage("-- Leaving frame: " .. self:getName())
    
</OnLeave>
  
</Scripts>
</Frame>


对不同事件的处理,均是通过这样的、<Scripts>中的元素来进行的。每一个<On...>元素都是一个处理器,其中是一段Lua代码,对指定的行为作出反应。每一个处理器均被传入一个self参数,即此框架本身。少数处理器可以接受一个以上的参数。

在这里,屏幕中央将显示一个100*100的暗言术痛图标。当鼠标移入或移出框架范围时,聊天窗口将相应输出提示信息。

<OnLoad>

所有框架都可以拥有一段<OnLoad>脚本,这段脚本将在框架被加载的时候自动运行。通常这里用来注册监听事件,以及进行其他的初始化行为。同样被传入self参数,即框架自身。

例如,下面的代码将框架注册到UNIT_HEALTH事件:

<Scripts>
  
<OnLoad>
    self:registerEvent("UNIT_HEALTH")
  
</OnLoad>
</Scripts>
 
<OnEvent>

对实际的游戏事件作出反应,必须借助<OnEvent>脚本。当事件被触发时,<OnEvent>将被传入两个以上的参数,第一个为self(框架自身),第二个为event,即事件名称。根据事件种类的不同,后面还将跟随数量不等的其他参数。

可以这样认为:如果框架在<OnLoad>时注册到UNIT_HEALTH事件,写在<OnEvent>标签内的代码就会被放到这样的代码中,并作为最后的OnEvent脚本。

function OnEvent(self, event, ...)
  
if event == "UNIT_HEALTH" then
    
-- 运行代码
  end
end


例如下面的代码: 

<Frame name="UnitHealthTest">
  
<Scripts>
    
<OnLoad>
      self:registerEvent("UNIT_HEALTH")
    
</OnLoad>
    
<OnEvent>
      local unit = ...
      ChatFrame1:AddMessage("UNIT_HEALTH: unit = " ... unit)
    
</OnEvent>
  
</Scripts>
</Frame>

 
UNIT_HEALTH的后续参数序列只有一个成员,即生命值受到影响的单位名称。上述代码将在每次UNIT_HEALTH事件被触发时,输出提示信息到聊天窗口。

<OnClick>

按钮和复选框可以通过<OnClick>脚本对鼠标点击作出反应。此事件传入三个参数:

  self: 被点击的按钮
  button: 按下的鼠标键
  down: 鼠标点击的方向(按下时为true,松开为false)

魔兽世界一共可以处理5种鼠标按键,分别名为:LeftButton, RightButton, MiddleButton, Button4和Button5。默认情况下,所有按钮都被设定只监听LeftButtonUp事件(即左键松开)。需要监听其他事件,必须通过:RegisterForClicks()方法修改。

例如,在<OnLoad>脚本中添加下列语句,可以使该按钮同时监听左右键的按下和释放事件:

  self:RegisterForClicks("LeftButtonUp", "LeftButtonDown",
  "RightButtonUp", "RightButtonDown")

一种简化的写法是用AnyUp代替所有的按钮释放事件,用AnyDown代替所有的按钮按下事件。下面是一段示例代码:

 1 <Button name="OnClickTest" parent="UIParent">
 2   <Size x="100" y="100" />
 3   <Anchors>
 4     <Anchor point="CENTER" relativePoint="CENTER"
 5      relativeTo="UIParent" />
 6   </Anchors>
 7   <Layers>
 8     <Layer level="BACKGROUND">
 9       <Texture name="$parentIcon"
10        file="Interface\Icons\Spell_Shadow_ShadowWordPain"
11        setAllPoints="true" />
12     </Layer>
13   </Layers>
14   <Scripts>
15     <OnLoad>
16       self:RegisterForClicks("AnyUp", "AnyDown")
17     </OnLoad>
18     <OnClick>
19       ChatFrame1:AddMessage(self:GetName() .. " - Button: " ..
20       tostring(button) .. " Down: " .. tostring(down))
21     </OnClick>
22   </Scripts>
23 </Button>

 
按钮在被按下的同时,将向聊天窗口发送“相应鼠标按键按下为true”的信息,松开时也会发送“相应鼠标按键按下为false”的信息。

<OnUpdate>

每次魔兽世界客户端重绘框架时均会呼叫<OnUpdate>脚本。实际上,“重绘框架”只意味着框架被“显示”,但并不代表框架会被用户“看见”。一个没有任何可见元素的框架,依然可以触发OnUpdate事件。此事件用来执行一些特殊的任务。

其他常见事件

  OnChar(self, text)
  当一个框架的enableMouse属性字段为true时,任何的键盘输入均会触发OnChar事件并将输入的字符传入。支持所有框架类型。
  
  OnClick(self, button, down)
  见上。只支持按钮和复选框类型的框架。

  OnDoubleClick(self, button)
  与OnClick类似。只有当按钮或复选框监听某一按钮的Up事件时,双击该按钮才会触发OnDoubleClick事件。

  OnDragStart / OnDragStop (self, button)
  拖曳相关。需要先用:RegisterForDrag()注册到鼠标拖曳事件(参数为鼠标按键名, 支持一个以上)。开始拖曳时呼叫OnDragStart,松开时呼叫OnDragStop。

  OnEnter / OnLeave (self, motion)
  当框架可接受鼠标事件时,鼠标进入/离开框架时触发。motion参数为true时,表示是鼠标移动导致鼠标指针进入/离开框架;反之,则可能是框架变为可见时鼠标恰好位于可见区域内(即进入/离开并非由鼠标指针移动引起)。

  OnHide / OnShow (self)
  显示/隐藏框架时触发。可以用OnHide来取消对特定事件的监听,以减少系统资源消耗。

  PreClick(self, button, down) / PostClick(self, button, down)
  对OnClick的一种补足。只支持按钮和复选框类型的框架。


常用的框架方法

      :GetAlpha()
  返回此对象的透明度(即alpha值)

  :GetName()
  返回此对象名称。若未在XML代码中指定名称,则返回nil。

  :GetObjectType()
  返回此对象的类型。

  :IsObjectType("type")
  若此对象的类型为参数给定类型,返回1,否则返回nil。

  :SetAlpha(alpha)
  设定此对象的透明度。参数取值范围0.0~1.0。

  :Show()
  设定此对象为可见。若该对象处于屏幕范围外,依然无法被看见。

  :Hide()
  隐藏此对象。

  :IsShown()
  当此对象没有被:Hide()隐藏时,返回1,否则返回nil。

  :IsVisible()
  若此对象及其所有前代对象均未被隐藏,返回1,否则返回nil。

以上的方法对所有类型的对象均适用。其他的一部分方法则只对一部分的对象有效,例如:RegisterForClicks()就只对Buttons和CheckButtons类型的对象有效。


创建及使用模板

XML在定义框架内容上,相对较显繁琐。为缩短代码书写时间,实际上,暴雪提供了上百种模板用于简化用户界面的开发工作。使用模板,不但可以迅速地创建某一特定格式的框架(例如 32x32大小、处于OVERLAY层、并包含一部分预设代码的按钮),而且在需要对同一系列的按钮格式作出修改时,只需修改模板,极大的减少了工作量。

建立模板

建立模板,实际上就是建立一个框架,只是其拥有virtual字段,并且值为true。如前所述,这样的框架代码,在加载时不会生成实际的框架实体(因为它是“虚”的),而是作为模板存在。

例如,在插件的XML文档的<Ui>元素中可以放入如下的模板定义:

 1 <Button name="IconTestTemplate" virtual="true">
 2   <Size x="32" y="32" />
 3   <Layers>
 4     <Layer level="OVERLAY">
 5       <Texture name="$parentIcon"
 6        file="Interface\Icons\Spell_Shadow_ShadowWordPain"
 7        setAllPoints="true" />
 8     </Layer>
 9   </Layers>
10   <Scripts>
11     <OnLoad>
12       self.Icon = getglobal(self:GetName().."Icon")
13     </OnLoad>
14     <OnEnter>
15       self.Icon:SetDesaturated(true)
16     </OnEnter>
17     <OnLeave>
18       self.Icon:SetDesaturated(nil)
19     </OnLeave>
20     <OnClick>
21       ChatFrame1:AddMessage("You clicked on "..self:getName())
22     </OnClick>
23   </Scripts>
24 </Button>


上面的代码创建了一个32x32按钮的模板。该按钮在OVERLAY层有图案,图案为暗言术·痛技能的图标。

在处理OnLoad事件(即实际按钮被加载)时,通过getglobal()方法获得<Texture>对象。注意在定义<Texture>时,对其命名时所用的字串是"$parentIcon",这里的$parent是其父对象名称的通配符(实际上,也就是这个框架本身)。注意这个关系:

框架名为****** ----> Texture对象名为******Icon

self:getName()获得的是框架名(******)
   ----> self:getName().."Icon"即是"******Icon"

所以可以用getglobal()方法加上这样的参数获得这个图标<Texture>对象本身,并存在框架的Icon变量内,方便其他代码(在这里是OnEnter/OnLeave)取用它。

现在来看处理OnEnter/OnLeave事件的代码。:SetDesaturated()方法需要一个布尔型的参数,当其为true时,将使目标对象色彩变为灰度模式。再通过:SetDesaturated(nil)可以将其调回标准色彩模式。因此,鼠标移入按钮区域时,按钮的图标将变为灰色;在移出时,按钮将变为正常色彩。

OnClick事件处理模块在每次鼠标点击图标时输出提示信息到聊天窗口。


使用模板

在定义了上面的模板之后,就可以这样利用模板创建数个按钮:

<Button name="IconTest1" inherits="IconTestTemplate" parent="UIParent">
  
<Anchors>
    
<Anchor point="BOTTOMRIGHT" relativePoint="CENTER"
     relativeTo
="UIParent" />
  
</Anchors>
</Button>
<Button name="IconTest2" inherits="IconTestTemplate" parent="UIParent">
  
<Anchors>
    
<Anchor point="BOTTOMLEFT" relativePoint="CENTER"
     relativeTo
="UIParent" />
  
</Anchors>
</Button>
<Button name="IconTest3" inherits="IconTestTemplate" parent="UIParent">
  
<Anchors>
    
<Anchor point="TOPRIGHT" relativePoint="CENTER"
     relativeTo
="UIParent" />
  
</Anchors>
</Button>
<Button name="IconTest4" inherits="IconTestTemplate" parent="UIParent">
  
<Anchors>
    
<Anchor point="TOPLEFT" relativePoint="CENTER"
     relativeTo
="UIParent" />
  
</Anchors>
</Button>


这段代码会在屏幕中央建立2x2的方格,每个格子都有一个暗言术·痛的图标。点击图标时,在聊天窗口均会输出对应的提示信息。

一些常用的模板

UIPanelButtonTemplate: 长条状的按钮
UIPanelButtonTemplate2: 长条状的按钮,与UIPanelButtonTemplate稍有不同
UIPanelCloseButton: 关闭按钮(X)
InputBoxTemplate: 聊天窗口所用的输入框
UICheckButtonTemplate: 复选框模板
UIRadioButtonTemplate: 单选框模板
TabButtonTemplate: 标签模板
GameMenuButtonTemplate: 游戏系统菜单(按ESC出现)的按钮模板
OptionsSliderTemplate: 滑块条模板