引用 | 编辑
imiss
2005-10-20 09:59 |
楼主
|
||
x0
难得一见的好文:) 为了感恩原作者大大的辛劳,所以特此转载此贴,让更多的玩家一起加入UI制作者行列:)原文将不做任何修改 作者:WOWAR。英雄 前言 22号基本写完。比预计的要快 这个不是数学书或者语文书。我不想也没有那个能力写成那样…… 所以请抱着看小说的态度来看。 因为ADDONS的编写是一个整体。我实在无法分清哪个是要先说,哪个是要后说 所以有看不懂的地方就跳过去。接着看下面 前几层楼写的相对详细些。后几层楼更多的是提示。我还是希望看官能自己分析,自己动手研究 最后。短短的6层楼包含了我半年的心血,而且我语文课真的是没好好去上 所以如果一时看不懂。请多读几遍 或者回帖指出,我会尽力解释的 Addons (Add-Ons) 中文直译:附件,附加 附加软体。 俗称:插件 解释:他们是一些附加的档,被放置在玩家 WOW 游戏目录下的 Interface 档夹中。插件 使用暴雪提供的LUA和XML代码(官方API函数介面)来扩充玩家可以使用的用户介面功能。 插件是通过(暴雪提供的)LUA和XML档(函数)构成的,并且也是通过暴雪的编译机所解释和执行的。因此,暴雪也不会封停任何使用插件的玩家。 StatusBar 在之前我们大概知道了ADDONS的一些基本概念 那么现在。在各种类型的框体中挑一个StatusBar来说说 基本概念 StatusBar:是WOW中用来定义类似施法条。进度条之类的一种框体, 说白了就是可以根据某个数值,即时的改变条条的长短 一个StatusBar有3个重要的参数 1、最大长度 maxValue 2、最小长度 minValue 3、当前长度 Value 要动态的改变StatusBar就需要用脚本(Scripts)中的事件即时的设置当前长度(Value) (这句话可能有点饶口。不过我就这点语文水准了…见谅见谅) 准备工作 首先根据我们之前的概念。一个TOC档是必不可少的,编个first.toc ## Interface: 1600 ## Title: 我的第一插件 ## Notes: 真的是我的第一插件哦 然后我们当然得用XML来定义StatusBar这个框体,那么编写个first.xml 不过WOW并不知道我们写了first.xml这个档。我们得告诉他。所以在first.toc中加一句(红色的部分) ## Interface: 1600 ## Title: 我的第一插件 ## Notes: 真的是我的第一插件哦 first.xml 好了 正式开始编写first.xml 按照基本的XML档格式 先写好基本的嵌套 <StatusBar> </StatusBar> 起个好名字 <StatusBar name="haomingzi"> </StatusBar> 注意红色的那句。现在我们名叫haomingzi的StatusBar 会根据变数SVALUE自动改变长短了 (这里我们用到了脚本。后面会详细解释的。先记得<Scripts>是脚本就可以了) <StatusBar name="haomingzi"> <Scripts> <OnUpdate> haomingzi:SetValue(SVALUE); </OnUpdate> </Scripts> </StatusBar> 现在我们把变数SVALUE设置为玩家的血量 那么: <StatusBar name="haomingzi"> <Scripts> <OnUpdate> haomingzi:SetValue( UnitHealth("player") ); </OnUpdate> </Scripts> </StatusBar> OK。一个玩家的HP条就写出来了 当然仅仅这几行还远远不够,继续完善下 先把最大和最小长度定义好 <StatusBar name="haomingzi" minValue="0" maxValue="100"> <Scripts> <OnUpdate> haomingzi:SetValue( UnitHealth("player") ); </OnUpdate> </Scripts> </StatusBar> 这里我们设置的是0到100。显然玩家的血量肯定不会在0到100之内的。 那么我们就要把他转换为百分比 <StatusBar name="haomingzi" minValue="0" maxValue="100"> <Scripts> <OnUpdate> local SVALUE=( UnitHealth("player") / UnitHealthMax("player") )*100 haomingzi:SetValue(SVALUE); </OnUpdate> </Scripts> </StatusBar> 设定下他的位置,比方把他放在萤幕的中间 (具体的如何设置位置。在楼下会讲。现在只要知道红色部分的代码是设定位置就可以了) <StatusBar name="haomingzi" minValue="0" maxValue="100"> <Anchors> <Anchor point="center" relativeTo="UIparent" relativePoint="center"/> </Anchors> <Scripts> <OnUpdate> local SVALUE=( UnitHealth("player") / UnitHealthMax("player") )*100 haomingzi:SetValue(SVALUE); </OnUpdate> </Scripts> </StatusBar> 设定一下大小 <StatusBar name="haomingzi" minValue="0" maxValue="100"> <Size> <AbsDimension x="70" y="8"/> </Size> <Anchors> <Anchor point="center" relativeTo="UIparent" relativePoint="center"/> </Anchors> <Scripts> <OnUpdate> local SVALUE=( UnitHealth("player") / UnitHealthMax("player") )*100 haomingzi:SetValue(SVALUE); </OnUpdate> </Scripts> </StatusBar> 当然他长什么样子我们还没弄呢~ <StatusBar name="haomingzi" minValue="0" maxValue="100"> <BarTexture file="Interface\TargetingFrame\UI-StatusBar"/> <Size> <AbsDimension x="70" y="8"/> </Size> <Anchors> <Anchor point="center" relativeTo="UIparent" relativePoint="center"/> </Anchors> <Scripts> <OnUpdate> local SVALUE=( UnitHealth("player") / UnitHealthMax("player") )*100 haomingzi:SetValue(SVALUE); </OnUpdate> </Scripts> </StatusBar> 最后给头尾加上最基本的<ui></ui>嵌套。就大功告成了 <ui> <StatusBar name="haomingzi" minValue="0" maxValue="100"> <BarTexture file="Interface\TargetingFrame\UI-StatusBar"/> <Size> <AbsDimension x="70" y="8"/> </Size> <Anchors> <Anchor point="center" relativeTo="UIparent" relativePoint="center"/> </Anchors> <Scripts> <OnUpdate> local SVALUE=( UnitHealth("player") / UnitHealthMax("player") )*100 haomingzi:SetValue(SVALUE); </OnUpdate> </Scripts> </StatusBar> </ui> 进阶 StatusBar除了SetValue这个重要的命令以外。还有个SetStatusBarColor命令。是用来改变颜色的 比如还是上面的例子 我们现在想当HP超过50%的时候为绿色 低于50%的时候为红色 那么先定义一个函数 就叫haomingzi_OnUpdate把 用来实现上面的功能 function haomingzi_OnUpdate() local SVALUE=( UnitHealth("player") / UnitHealthMax("player") )*100; if SVALUE > 50 then haomingzi:SetStatusBarColor(1,0,0); end end 然后我们在脚本中调用这个函数就可以了 <ui> <StatusBar name="haomingzi" minValue="0" maxValue="100"> <BarTexture file="Interface\TargetingFrame\UI-StatusBar"/> <Size> <AbsDimension x="70" y="8"/> </Size> <Anchors> <Anchor point="center" relativeTo="UIparent" relativePoint="center"/> </Anchors> <Scripts> <OnUpdate> haomingzi_OnUpdate(); </OnUpdate> </Scripts> </StatusBar> </ui> 更多的框体 当然框体绝对不仅仅只有<StatusBar>这一中。还有诸如<Button> <Frame> <Texture> <FontString>等等等等 这里就不一一解释了,你随便打开一个写好的ADDONS都可以发现他们的身影。自己分析一下把 位置 这里将说说 如何定义一个框体的位置 我们打开任意的一个编写好的ADDONS的XML档。多半会发现形如这样的代码 CODE: [Copy to clipboard] <Anchors> <Anchor point="CENTER" relativeTo="Minimap" relativePoint="CENTER"> <Offset> <AbsDimension x="55" y="-55"/> </Offset> </Anchor></Anchors> 这样的代码就是用来定义的位置的 数学课和物理课都上过把?(虽然我很讨厌我们过去的数学老师 ><) 想知道任何一个物体的位置。只要有一个固定的参考物。再有与参考物体的相对座标。就可以了 --------------------------------------------------------------------------------------------------- 先来点基本概念 我们用一个方框来表示框体,那么 TOP TOPLEFT -------------------- TOPRIGHT | | LEFT | CENTER | RIGHT | | BOTTOMLEFT -------------------- BOTTOMLEFT BOTTOM 应该有点头绪了把? 之前那段代码的意思就是 把位置定义在 小地图的中心点和我们的框体的中心点X座标为55 Y座标为55的地方 如果看不明白这句话 不要紧 我们一句一句的来分析 ------------------------------------------------------------------------------------------------------ 具体分析 头一句和最后一句 <Anchors> </Anchors> 这个嵌套是告诉WOW:中间的代码是定义位置 第二句和倒数第二句 <Anchor point="CENTER" relativeTo="Minimap" relativePoint="CENTER"> </Anchor> 这个嵌套就是告诉WOW 我们开始定义位置了。 先看第二句。 point="CENTER" 参考之前我画的那张很丑陋的图。 意思就是:要定义位置的框体(为了描述方便。以下简称为框体A)的中心点(CENTER)作为定义点。 (定义点这个名词是我杜撰的。如果不明白。先接着往下看) relativeTo="Minimap" 意思就是:给我们的框体A设置一个参考物(为了描述方便。以下把参考物简称为框体B) 在这里。就是把框体B设置为小地图(Minimap) relativePoint="CENTER" 同样的。也得给我们的框体B设置一个定义点 在这里。就设置为中心点(CENTER) 现在解释一下定义点这个我杜撰的名词 为什么要有定义点这个概念呢? 因为所有的框体都不是一个点。而是一个平面。而相对座标只能是点与点的座标。所以就必须在框体上找一个点来定义座标 而这个点。就是我所谓的定义点 至于一个框体的定义点可以设置为那些,参考上面我画的那张丑陋的图 搞明白了以上的概念。那么中间的那段代码 CODE: [Copy to clipboard] <Offset> <AbsDimension x="55" y="-55"/> </Offset> 也就不难理解是什么意思了。这正是设置2个定义点之间的相对座标 ------------------------------------------------------------------------------------------------------- 进阶 为什么一向崇尚操作简单的暴雪要把位置的定义弄的这么复杂呢? 似乎我们只要变换2个定义点之间的相对座标。那么无论我们把定义点怎么设置 都可以达到同样的效果 其实。是因为框体的大小有时候是不固定的。 比如我想实现这样的效果: 在玩家血条的左边显示HP的具体数值 如果这么定义位置: CODE: [Copy to clipboard] <FontString name="HPText"><Anchors> <Anchor point="CENTER" relativeTo="PlayerFrame" relativePoint="CENTER"> <Offset> <AbsDimension x="55" y="0"/> </Offset> </Anchor></Anchors> 。 。 。 。 似乎可以到达效果, 可是HP有多有少。 当HP为3位数的时候。数值是在血条的左边。但是但HP为4为数的时候。数值就超过了左边挡主了部分血条。 所以。得这么写 CODE: [Copy to clipboard] <FontString name="HPText"><Anchors> <Anchor point="RIGHT" relativeTo="PlayerFrame" relativePoint="LEFT"> <Offset> <AbsDimension x="0" y="0"/> </Offset> </Anchor></Anchors> 。 。 。 。 这样。无论HP为多少。数值的右侧永远都和血条的坐侧对齐 --------------------------------------------------------------------------------------------------------------- PS: 当相对座标为0,0的时候。代码可以简化 比如刚才的代码可以简化为 CODE: [Copy to clipboard] <FontString name="HPText"><Anchors> <Anchor point="RIGHT" relativeTo="PlayerFrame" relativePoint="LEFT"/></Anchors> 。 。 。 。 注意:别漏看了第三句最后的那个反斜杠 脚本 看到这里。我们对框体的定义应该有了很大的了解。现在定义一个自己的框体应该没什么难度了把? 在STATUSBAR部分我们提到了脚本。 脚本我个人觉得是ADDONS的精髓 弄懂了脚本部分。那么去他的FLEXBAR 去他的DAB 去他的DUF 我们不需要了。我们自己就可以来做了 基本概念 什么是脚本。通俗的说:脚本就是告诉框体在什么时候执行什么命令 同样的 我们用<Scripts></Scripts>这样的嵌套来表示代码中脚本的部分 具体的举个例子 还记得FLEXBAR或者DAB一个很实用的功能把?当滑鼠进入按扭的区域 按纽显示 离开则隐藏 现在我们直接在ADDONS中写(为了描述方便起见 以下只写出代码中我们需要注意的部分) <BUTTON name="button_1"> <Scripts> <OnEnter> This:Show(); </OnEnter> <OnLeave> This:Hide(); </OnLeave> </Scripts> </BUTTON> <OnEnter>这个嵌套就是滑鼠进入框体的区域需要执行的命令 <OnLeave>则是滑鼠离开框体的区域需要执行的命令 如何?是不是很简单呢? 接着来。 FLEXBAR或者DAB还有很多神奇的功能。比如根据条件自动改变按扭的位置 透明度 缩放 等等等 如果我们直接在ADDONS中编写也很方便 比如进入战斗状态 自动出现按扭 反之隐藏 <BUTTON name="button_1"> <Scripts> <OnLoad> this:RegisterEvent("PLAYER_ENTER_COMBAT"); this:RegisterEvent("PLAYER_LEAVE_COMBAT"); </OnLoad> <OnEvent> if (event == "PLAYER_ENTER_COMBAT") then this:Show(); elseif (event == "PLAYER_LEAVE_COMBAT") then this:Hide(); end </OnEvent> </Scripts> </BUTTON> <OnLoad>是框体被载入的时候需要执行的命令 这里。我们给button_1这个框体注册了2个事件:玩家进入战斗和玩家离开战斗 <OnEvent>是注册的事件发生的时候需要执行的命令 这里。我们用了一个判断语句。 当事件为玩家进入战斗的时候 显示按扭1 当事件为玩家离开战斗的时候 隐藏按扭1 同样的 脚本和框体一样 不可能仅仅只有我上面所说的那几个。 更多的脚本需要你自己去发现。。我不想啰嗦了。 更多的惊喜 以上2个只是很简单的例子。脚本中执行的命令还可以是相互调用。相互依存的来实现更多更复杂的功能 这时候。仅仅一个XML档已经不能满足我们的需要了。我们得在LUA档中来编写 LUA 如果你有耐心看完了上面的全部内容 并且亲手去实验了 那么如何编写一个XML档应该了然与胸了把 当然一个精巧的ADDONS不可能仅仅只有XML档而已。他还需要LUA档 LUA档当然就是用LUA格式写的 具体的LUA的语法 限与帖子的篇幅,不能详尽说明。好在现在网上的资料很多的 我只说几个个人觉得很有用的部分 1。引用LUA和定义函数 先回头看6楼的最后那段代码 <BUTTON name="button_1"> <Scripts> <OnLoad> this:RegisterEvent("PLAYER_ENTER_COMBAT"); this:RegisterEvent("PLAYER_LEAVE_COMBAT"); </OnLoad> </OnEvent> if (event == "PLAYER_ENTER_COMBAT") then this:Show(); elseif (event == "PLAYER_LEAVE_COMBAT") then this:Hide(); end </OnEvent> </Scripts> </BUTTON> 我们可以把脚本的部分放到LUA中来写。 首先我们新建一个BUTTON1.LUA文件 然后在XML档里面要告诉WOW 我们写了BUTTON1.LUA文件 <Script file="BUTTON1.lua"/> <BUTTON name="button_1"> <Scripts> <OnLoad> this:RegisterEvent("PLAYER_ENTER_COMBAT"); this:RegisterEvent("PLAYER_LEAVE_COMBAT"); </OnLoad> <OnEvent> if (event == "PLAYER_ENTER_COMBAT") then this:Show(); elseif (event == "PLAYER_LEAVE_COMBAT") then this:Hide(); end </OnEvent> </Scripts> </BUTTON> 定义函数的LUA命令是function 现在我们就把<OnLoad>和<OnEvent>这2个部分的命令定义为函数 function button_1_onload() this:RegisterEvent("PLAYER_ENTER_COMBAT"); this:RegisterEvent("PLAYER_LEAVE_COMBAT"); end function button_1_onevent(event) if (event == "PLAYER_ENTER_COMBAT") then this:Show(); elseif (event == "PLAYER_LEAVE_COMBAT") then this:Hide(); end end 然后在XML档中引用这2个函数 <Script file="BUTTON1.lua"/> <BUTTON name="button_1"> <Scripts> <OnLoad> button_1_onload(); </OnLoad> <OnEvent> button_1_onevent(event); </OnEvent> </Scripts> </BUTTON> 这样原来的一个XML档就被我们分成了2个档 LUA和XML 也许就上面的那段简单的代码 我们还觉得这样做并没有什么太大的意义 不过。当你写了一段很复杂 很麻烦的代码的时候。这么做显然有助与你简化代码和理清思路 2。代码的本地化 因为WOW有很多国家的版本。所以一些变数的设置需要本地的语言。 比如能在中国使用的ADDONS,有时候并不能在美国使用。这时候我们就需要做一些本地化的工作 怎么做? 我们把所有的需要使用当地语言的变数集中起来 在一个LUA档中定义 (这个LUA档。我们一般起名叫:localization.lua) 而且。WOW还提供了自动判断语种的功能 这些都很简单。随便打开一个ADDONS的localization.lua自己看一看就明白 不啰嗦了 当然LUA的作用远远不止这些 毕竟LUA是一个很成熟的语言。熟练的运用将大大简化我们的工作量 比如LUA的阵列功能。字串的判断 更多的细节。可以在自己动手写ADDONS的过程中慢慢摸索。 继承 WOW已经帮我定义好了很多有用的框体 所以很多的时候。我们并不需要自己完全的重新定义 直接引用他们就可以了 这里就要用到继承这个概念 如何做? 继承的命令是inherits 比如我想定义一个文字框体。他的样子和显示玩家的名字的文字的样子是一样的 那么: <FontString name="TEXT_FRAME" inherits="GameFontNormalSmall" > <FontString/> 这样 我们简单的用了inherits="GameFontNormalSmall"命令 就把 TEXT_FRAME框体的大小 颜色 透明度 字体等等等等属性全部搞定了 如果有不满意的地方 还可以重新定义。 比如改变一下颜色 <FontString name="TEXT_FRAME" inherits="GameFontNormalSmall"> <Color r="0" g="1" b="0"/> <FontString/> 现在他就是绿色的咯 当然我们也可以定义自己的 这将大大有助于简化我们的代码。 还记得我以前写的那个OPENDOOR吗? 我在里面一共定义了7个框体 其中有6个框体是按扭。并且他们很多部分都是公共的。 所以。如果我现在再来写那个OPENDOOR 我会先写一个父框 把6个按扭公共的部分全部写进去 然后在一个一个的继承就OK了。~ 具体的父框的定义 不啰嗦了 大家可以随便打开一个ADDONS 找到名字后面为Template的框体。那多半就是父框了。 动手分析一下把 (提示一点:在父框中的$parent就是要被继承的子框的名字) x0
|