Ace3魔獸世界插件開發之旅(一)- WelcomeHome

本文一步一步講解如何經過Ace3開發框架構建一個WelcomeHome插件。文中大部份內容翻譯自gamepedia,原文地址:https://wow.gamepedia.com/WelcomeHome_-_Your_first_Ace3_Addon
因爲英文水平有限,有不對之處還望指正,謝謝!api

準備工做

目前魔獸的版本是8.1.5,魔獸插件都在<World of Warcraft_retail_InterfaceAddOns>這個目錄下,因此咱們先建一個目錄:WelcomeHome,當WOW在AddOns目錄下發現一個目錄時,它會去找這個目錄下跟目錄同名的TOC文件,這個TOC文件包含了本插件全部文件的清單,WOW會使用這個文件來加載這個插件。下面是咱們這個WelcomeHome.TOC文件的基本骨架:session

## Interface: 81500
## Version: 0.1
## Title: Welcome Home
## Author: xiaop
## Notes: 爐石的時候顯示歡迎信息.
Core.lua

這時候須要建一個空的Core.lua文件,用來存放插件的代碼。如今咱們先讓它空着,這時候登陸WOW在人物選擇界面點擊插件能夠看到WelcomeHome這個插件,雖然它啥也幹不了。架構

引入Ace3庫

要想讓咱們的插件具有具體功能,咱們須要引入Ace3相關的庫。Ace3使用了一個叫作「嵌入式庫」的概念,它容許模塊開發者在其餘模塊加載了相同庫的時候不須要再複製一份代碼。咱們能夠在 http://www.wowace.com/addons/ace3/files/這裏下載最新的Ace3庫,而後解壓到插件目錄的Libs目錄下。本文須要用到如下的庫:框架

如今咱們有了Ace3相關的庫,可是WOW並不知道如何加載他們,咱們須要一個embeds.xml文件來告訴WOW須要加載哪些文件,因而咱們新建一個embeds.xml文件,內容以下:ide

<Ui xmlns="http://www.blizzard.com/wow/ui/" xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance" xsi:schemaLocation="http://www.blizzard.com/wow/ui/
..\FrameXML\UI.xsd">
    <Script file="Libs\LibStub\LibStub.lua"/>
    <Include file="Libs\CallbackHandler-1.0\CallbackHandler-1.0.xml"/>
    <Include file="Libs\AceAddon-3.0\AceAddon-3.0.xml"/>
    <Include file="Libs\AceEvent-3.0\AceEvent-3.0.xml"/>
    <Include file="Libs\AceDB-3.0\AceDB-3.0.xml"/>
    <Include file="Libs\AceConsole-3.0\AceConsole-3.0.xml"/>
    <Include file="Libs\AceGUI-3.0\AceGUI-3.0.xml"/>
    <Include file="Libs\AceConfig-3.0\AceConfig-3.0.xml"/>
</Ui>

Script標籤表示須要引入的lua文件,Include標籤表示要引入的xml文件。須要注意的是LibStub必須先加載,由於其餘庫都依賴它。其餘文件也須要注意使用順序,原則就是被依賴的庫須要先加載。
如今咱們更新一下TOC文件,在core.lua以前引入embeds.xml文件,以保證在core.lua執行以前全部的庫已被加載並可使用。ui

## Interface: 81500
## Version: 0.1
## Title: Welcome Home
## Author: xiaop
## Notes: 爐石的時候顯示歡迎信息.
## SavedVariables: WelcomeHomeDB
## OptionalDeps: Ace3
## X-Embeds: Ace3

embeds.xml
Core.lua

Hello World

下面咱們編輯一下Core.lua文件,加入Ace3最基本的結構:lua

WelcomeHome = LibStub("AceAddon-3.0"):NewAddon("WelcomeHome", "AceConsole-3.0")

function WelcomeHome:OnInitialize()
    -- Called when the addon is loaded
end

function WelcomeHome:OnEnable()
    -- Called when the addon is enabled
end

function WelcomeHome:OnDisable()
    -- Called when the addon is disabled
end

第一行使用NewAddon方法建立一個AceAddon類的實例,由於咱們會用到聊天窗口和斜槓命令,咱們還混入了AceConsole類。
接下來是三個重寫的方法OnInitialize, OnEnable, 和 OnDisable。OnInitialize只會在UI加載的時候執行一次,後面兩個分別在插件被啓用和禁用的時候執行。
下面咱們假設插件正在加載,咱們想在聊天窗口打印一句「Hello World」,只須要在OnEnable方法中添加一行代碼:spa

function WelcomeHome:OnEnable()
    self:Print("Hello World!")
end

這樣,在咱們從新進入WOW的時候就會在聊天窗口底部看到這句「Hello World」。插件

響應事件

這個插件的目標是在WOWer回到爐石所在區域的時候提示歡迎信息,那麼咱們要如何知道WOWer已經回到爐石所在區域了呢?很簡單,咱們只須要響應某一個ZONE_CHANGED事件便可,這個事件會在WOWer進入一個新區域的時候觸發。
WoW是事件驅動的,咱們插件能作的事情都要依賴於各類事件,不然咱們啥也幹不了。
爲了響應事件咱們須要混入AceEvent庫,而後經過RegisterEvent方法監聽ZONE_CHANGED事件,在事件觸發的時候調用ZONE_CHANGED方法打印一句「You have changed zones!」在聊天窗口。Core.lua的代碼以下:命令行

WelcomeHome = LibStub("AceAddon-3.0"):NewAddon("WelcomeHome", "AceConsole-3.0", "AceEvent-3.0")

function WelcomeHome:OnInitialize()
    -- Called when the addon is loaded
end

function WelcomeHome:OnEnable()
    self:RegisterEvent("ZONE_CHANGED")
end

function WelcomeHome:ZONE_CHANGED()
    self:Print("You have changed zones!")
end

無需重啓遊戲,使用/reload命令便可從新載入修改後的插件。

使用WoW API

如今咱們有一個方法能夠在WoWer切換區域的時候被調用,那麼咱們如何知道他如今在哪一個區域,他的爐石又綁在哪裏呢?這個能夠在WoW API中找到答案,

  • GetBindLocation 方法會返回子區域的名字,其中包含了爐石所在地
  • GetSubZoneText 方法返回WoWer當前所在子區域,若是當前沒有子區域,會返回空串。

下面修改代碼來看看效果,咱們在切換區域的時候把上面兩個方法的返回值打印到聊天窗口中,代碼以下:

function WelcomeHome:ZONE_CHANGED()
    self:Print(GetBindLocation())
    self:Print("================")
    self:Print(GetSubZoneText())
end

這樣咱們能夠在切換區域的時候打印出爐石所在區域和當前的區域。效果以下:
image.png

聊天命令和配置

咱們可使用AceConfig來註冊一個option table,以支持開箱即用的斜槓命令。

WelcomeHome = LibStub("AceAddon-3.0"):NewAddon("WelcomeHome", "AceConsole-3.0", "AceEvent-3.0")
local options = {
    name = "WelcomeHome",
    handler = WelcomeHome,
    type = 'group',
    args = {
    },
}
function WelcomeHome:OnInitialize()
    -- Called when the addon is loaded
    LibStub("AceConfig-3.0"):RegisterOptionsTable("WelcomeHome", options, {"welcomehome", "wh"})
end

重載UI之後在聊天窗口輸入/WelcomeHome或者/wh,能夠看到插件的提示信息(插件名字,描述, 可用命令等)。
下面咱們添加一個讓用戶能夠改變提示文字的命令,命令名叫msg,能夠攜帶一個文本參數,使用get和set方法來操做底層變量,:

local options = {
    name = "WelcomeHome",
    handler = WelcomeHome,
    type = 'group',
    args = {
        msg = {
                type = "input",
                name = "Message",
                desc = "The message to be displayed when you get home.",
                usage = "<Your message>",
                get = "GetMessage",
                set = "SetMessage",
            },
    },
}
function WelcomeHome:GetMessage(info)
    return self.message
end

function WelcomeHome:SetMessage(info, newValue)
    self.message = newValue
end

function WelcomeHome:ZONE_CHANGED()
    self:Print("welcome msg is:",self.message)
    self:Print("================")
    self:Print("your heartstone set is : ",GetBindLocation())
    self:Print("================")
    self:Print("current subzone is : ",GetSubZoneText())
end

執行命令/wh msg helloworld,而後再切換區域的i時候就會彈出如下信息:
image.png

GUI和暴雪接口選項

咱們應該不只知足於使用命令來實現插件,暴雪從2.4補丁開始重作了接口選項,能夠將插件添加到遊戲的「插件」標籤。爲了作到這一點,咱們須要稍微改變如下代碼的處理方式:

function WelcomeHome:OnInitialize()
    -- Called when the addon is loaded
    LibStub("AceConfig-3.0"):RegisterOptionsTable("WelcomeHome", options)
    self.optionsFrame = LibStub("AceConfigDialog-3.0"):AddToBlizOptions("WelcomeHome", "WelcomeHome")
    self:RegisterChatCommand("welcomehome", "ChatCommand")
    self:RegisterChatCommand("wh", "ChatCommand")
    WelcomeHome.message = "Welcome Home!"
end
function WelcomeHome:ChatCommand(input)
    if not input or input:trim() == "" then
        InterfaceOptionsFrame_OpenToCategory(self.optionsFrame)
    else
        LibStub("AceConfigCmd-3.0"):HandleCommand("wh", "WelcomeHome", input)
    end
end

AddToBlizOptions方法返回一個frame,咱們能夠用這個frame來打開暴雪的接口選項。而後咱們用ChatCommand方法來控制插件的行爲表現,若是輸入的內容爲空,則會直接打開暴雪接口選項,這樣咱們就能夠經過GUI來配置咱們的插件,不然仍是按照命令原來的顯示邏輯在聊天窗口中顯示。
如下是輸入/wh的效果:
image.png

讓提示消息更突出

咱們可使用UIErrorsFrame 來將提示消息顯示在屏幕的指定位置,簡單來講只須要添加一行代碼:

function WelcomeHome:ZONE_CHANGED()
    self:Print("welcome msg is:",self.message)
    self:Print("================")
    self:Print("your heartstone set is : ",GetBindLocation())
    self:Print("================")
    self:Print("current subzone is : ",GetSubZoneText())
    UIErrorsFrame:AddMessage(self.message, 1.0, 1.0, 1.0, 5.0)
end

效果以下:
image.png

在不一樣的session之間保存配置

目前咱們的歡迎消息在咱們退出遊戲之後就丟失了,WoW提供了一種在不一樣的session之間保存配置的方法叫作Saved Variables,而Ace的方法是AceDB,經過AceDB咱們將提示消息持久保存。

local defaults = {
    profile = {
        message = "Welcome Home!"
    },
}
function WelcomeHome:GetMessage(info)
    -- return self.message
    return self.db.profile.message
end

function WelcomeHome:SetMessage(info, newValue)
    self.db.profile.message = newValue
end

function WelcomeHome:OnInitialize()
    -- 命令行的方式
    -- LibStub("AceConfig-3.0"):RegisterOptionsTable("WelcomeHome", options, {"welcomehome", "wh"})
    
    -- GUI的方式    
    self.db = LibStub("AceDB-3.0"):New("WelcomeHomeDB", defaults, true)
    
    LibStub("AceConfig-3.0"):RegisterOptionsTable("WelcomeHome", options)
    self.optionsFrame = LibStub("AceConfigDialog-3.0"):AddToBlizOptions("WelcomeHome", "WelcomeHome")
    self:RegisterChatCommand("welcomehome", "ChatCommand")
    self:RegisterChatCommand("wh", "ChatCommand")
    WelcomeHome.message = "Welcome Home!"
end

至此,這個插件基本就完成了,雖然功能比較簡單,可是是一個很好的入門項目.

完整代碼

  • WelcomeHome.TOC
## Interface: 81500
## Version: 0.1
## Title: Welcome Home
## Author: xiaop
## Notes: 爐石的時候顯示歡迎信息.
## SavedVariables: WelcomeHomeDB
## OptionalDeps: Ace3
## X-Embeds: Ace3

embeds.xml
Core.lua
  • embeds.xml
<Ui xmlns="http://www.blizzard.com/wow/ui/" xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance" xsi:schemaLocation="http://www.blizzard.com/wow/ui/
..\FrameXML\UI.xsd">
    <Script file="Libs\LibStub\LibStub.lua"/>
    <Include file="Libs\CallbackHandler-1.0\CallbackHandler-1.0.xml"/>
    <Include file="Libs\AceAddon-3.0\AceAddon-3.0.xml"/>
    <Include file="Libs\AceEvent-3.0\AceEvent-3.0.xml"/>
    <Include file="Libs\AceDB-3.0\AceDB-3.0.xml"/>
    <Include file="Libs\AceConsole-3.0\AceConsole-3.0.xml"/>
    <Include file="Libs\AceGUI-3.0\AceGUI-3.0.xml"/>
    <Include file="Libs\AceConfig-3.0\AceConfig-3.0.xml"/>
</Ui>
  • Core.lua:
WelcomeHome = LibStub("AceAddon-3.0"):NewAddon("WelcomeHome", "AceConsole-3.0", "AceEvent-3.0")
local options = {
    name = "WelcomeHome",
    handler = WelcomeHome,
    type = 'group',
    args = {
        msg = {
                type = "input",
                name = "Message",
                desc = "The message to be displayed when you get home.",
                usage = "<Your message>",
                get = "GetMessage",
                set = "SetMessage",
            },
    },
}
local defaults = {
    profile = {
        message = "Welcome Home!"
    },
}
-- 保存在db中就不須要這個變量了
-- WelcomeHome.message = "Welcome Home!"

function WelcomeHome:GetMessage(info)
    -- return self.message
    return self.db.profile.message
end

function WelcomeHome:SetMessage(info, newValue)
    self.db.profile.message = newValue
end

function WelcomeHome:OnInitialize()
    -- 命令行的方式
    -- LibStub("AceConfig-3.0"):RegisterOptionsTable("WelcomeHome", options, {"welcomehome", "wh"})
    
    -- GUI的方式    
    self.db = LibStub("AceDB-3.0"):New("WelcomeHomeDB", defaults, true)
    
    LibStub("AceConfig-3.0"):RegisterOptionsTable("WelcomeHome", options)
    self.optionsFrame = LibStub("AceConfigDialog-3.0"):AddToBlizOptions("WelcomeHome", "WelcomeHome")
    self:RegisterChatCommand("welcomehome", "ChatCommand")
    self:RegisterChatCommand("wh", "ChatCommand")
    WelcomeHome.message = "Welcome Home!"
end

function WelcomeHome:ChatCommand(input)
    if not input or input:trim() == "" then
        InterfaceOptionsFrame_OpenToCategory(self.optionsFrame)
    else
        LibStub("AceConfigCmd-3.0"):HandleCommand("wh", "WelcomeHome", input)
    end
end

function WelcomeHome:OnEnable()
    self:RegisterEvent("ZONE_CHANGED")
end

function WelcomeHome:ZONE_CHANGED()
    self:Print("welcome msg is:",self.db.profile.message)
    self:Print("================")
    self:Print("your heartstone set is : ",GetBindLocation())
    self:Print("================")
    self:Print("current subzone is : ",GetSubZoneText())
    UIErrorsFrame:AddMessage(self.db.profile.message, 1.0, 1.0, 1.0, 5.0)
end
  • WelcomeHome目錄結構:
│  Core.lua
│  embeds.xml
│  WelcomeHome.TOC
│
└─Libs
    ├─AceAddon-3.0
    │      AceAddon-3.0.lua
    │      AceAddon-3.0.xml
    │
    ├─AceConfig-3.0
    │  │  AceConfig-3.0.lua
    │  │  AceConfig-3.0.xml
    │  │
    │  ├─AceConfigCmd-3.0
    │  │      AceConfigCmd-3.0.lua
    │  │      AceConfigCmd-3.0.xml
    │  │
    │  ├─AceConfigDialog-3.0
    │  │      AceConfigDialog-3.0.lua
    │  │      AceConfigDialog-3.0.xml
    │  │
    │  └─AceConfigRegistry-3.0
    │          AceConfigRegistry-3.0.lua
    │          AceConfigRegistry-3.0.xml
    │
    ├─AceConsole-3.0
    │      AceConsole-3.0.lua
    │      AceConsole-3.0.xml
    │
    ├─AceDB-3.0
    │      AceDB-3.0.lua
    │      AceDB-3.0.xml
    │
    ├─AceEvent-3.0
    │      AceEvent-3.0.lua
    │      AceEvent-3.0.xml
    │
    ├─AceGUI-3.0
    │  │  AceGUI-3.0.lua
    │  │  AceGUI-3.0.xml
    │  │
    │  └─widgets
    │          AceGUIContainer-BlizOptionsGroup.lua
    │          AceGUIContainer-DropDownGroup.lua
    │          AceGUIContainer-Frame.lua
    │          AceGUIContainer-InlineGroup.lua
    │          AceGUIContainer-ScrollFrame.lua
    │          AceGUIContainer-SimpleGroup.lua
    │          AceGUIContainer-TabGroup.lua
    │          AceGUIContainer-TreeGroup.lua
    │          AceGUIContainer-Window.lua
    │          AceGUIWidget-Button.lua
    │          AceGUIWidget-CheckBox.lua
    │          AceGUIWidget-ColorPicker.lua
    │          AceGUIWidget-DropDown-Items.lua
    │          AceGUIWidget-DropDown.lua
    │          AceGUIWidget-EditBox.lua
    │          AceGUIWidget-Heading.lua
    │          AceGUIWidget-Icon.lua
    │          AceGUIWidget-InteractiveLabel.lua
    │          AceGUIWidget-Keybinding.lua
    │          AceGUIWidget-Label.lua
    │          AceGUIWidget-MultiLineEditBox.lua
    │          AceGUIWidget-Slider.lua
    │
    ├─CallbackHandler-1.0
    │      CallbackHandler-1.0.lua
    │      CallbackHandler-1.0.xml
    │
    └─LibStub
            LibStub.lua
相關文章
相關標籤/搜索