引用 | 編輯
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
|