前言:
因为魔兽世界9.0 暗影界 版本,重新回归了。了解到了魔兽世界里的一些宏命令,就想着自己弄一些自己想要功能的插件,其他插件太过臃肿,不太喜欢。刚好在网络上找到了《Beginning Lua with World of Warcraft Add-ons》这本书的英文电子版。虽然不知道Lua还是不是开发插件的最好语言,抱着好奇心,尝试进行翻译。
翻译的动力也是来自NGA社区。里面的留言呢,也是建议从第三章开始看起,因为这版本的电子书有些年代了,前面两章的内容已经不太适用了。(英文撇脚,翻译较为生硬,刚好锻炼锻炼自己的英文)
正文:
CHAPTER 3
标题:使用WoW API来创建一个“Hello,World”实例MOD
内容:
让我们开始,在这之前你需要在第二章看一些Lua的实例,这样在你编写“Hello World!”这个MOD的时候就不会有问题困扰你了。
但是,这第一个样例所做的事情,要比只显示“Hello,World”这几个字,要多做一些事情。因此我们提供了两个斜杠指令:
/hwadd <id><text>
/hwshow <id>
前面第一行样式语句添加内容,将内容存储在表中;第二行的样式语句将通过id找到内容并将其发送到聊天窗口。有些小心思的人可能已经发现了,通过这个插件宏发送大量信息,用来向其他人发送广告信息。除此之外还是有好用的地方,比如,通过一个单一的宏指令它可以存储很长的BOSS战或者战场信息。并在副本聊天窗口或者战场聊天窗口,一键发送出去。
让我们在游戏中运行Lua代码吧。
在游戏中运行一个"Hello , World"
直接在游戏的代码块中输入下面我们创建的斜杆指令:
/script print("Hello,World!")
这个代码指令“/script <text>”将编译"<text>"内容并在一个新的代码块中运行。
Note:在Lua中,一个代码块是单独存在的。因此,当你使用"/script"指令定义一个本地变量时,该变量无法在下一个"/script"中使用。
(我自己的注释:意思就是说,每一个"/script"都是单独存在的,每一个"/script"之间的数据是不互通的。)
你已经知道print函数的使用方法,但在这里它与Lua标准使用方法有些许不同:"魔兽世界"写入的内容输出在默认的聊天窗口,而不是在你的代码窗口。所以,它将在你的默认聊天窗口显示“Hello,World”。
就这?对就这。这就是最基本的。但这也是最简单的类似于宏的代码,同时我向你承诺,你在学习的过程中将学会如何编写插件,所以让我们来创建一个属于你的第一个插件Addon。这个例子只是展示了Lua代码也可以在“魔兽世界”中正常运行。我们现在可以创建一段Lua代码并且在游戏中加载它。
我们的第一个WoW插件
让我们开始创建在本章开头说的插件吧。在我们开始创建这个Mod之前我们需要决定把我们的Lua文件放在哪里。
文件夹结构
你可能已经知道了,我们需要把插件放在游戏“魔兽世界”的"Interface\AddOns"文件夹中来启动它们。这个文件夹默认在你的“C:\Program Files\World of Warcraft\Interface\AddOns”路径下。所以,让我们在这个文件夹中创建一个名叫“HelloWorld”的文件夹吧。这个文件夹中将放入所有我们创建“HelloWorld”Mod用到的文件。也许你可能看过“AddOns”文件夹的内容?这个文件夹里面有很多的文件扩展。哪种类型的文件是可以在插件中使用的呢?
《魔兽世界》提供了API方法来处理下面这些文件类型:Lua开发语言文件(.lua),XML文件(一种数据处理语言,你可以在 Chapter 5中学到更多)(.xml),材质文件(插件中将使用的图片)(.tga和.blp),声音文件(.wav和.mp3),字体文件(.ttf)和模型文件(3D对象)(.m2)。你可以在插件中使用这些文件并随着《魔兽世界》客户端一起启用。包含了背景音乐,说话窗口的文字,材质,模型等等。如果这些满足不了你,你可以在插件中提供自己的音乐声音,材质等等。
《魔兽世界》的API提供了一个虚拟文件系统(virtual file system),意味着你可以在其他设备上使用已经创建好的文件就像在你本地电脑上使用一样(游戏中你游戏角色设定的宏,可以在不同的电脑上共用)。这允许你访问Interface文件夹和MPQ档案(MPQ archives)内的所有文件。
Note:MPQ是暴雪所有游戏的一种档案格式。你可以把它想象成是一个zip的压缩文件。你将在本章中看到更多MPQ文件相关的内容。
当你启动游戏的时候这些虚拟文件系统将被建立,所以当你重新启动游戏才会识别出你新增的文件。每当你通过这本书来添加新的文件时,你都要重新启动一次游戏;重新加载你的接口(即通过插件开发窗口输入代码"/script ReloadUI()"或者简单的执行“/reload”)是不会生效的。
这些MPQ档案(MPQ archives)同样包含了定义默认UI界面的Lua文件和XML文件。这些UI界面都可以像Lua中的常规插件一样编写。在本章节的后面,你将知道如何查看默认UI界面的代码。
当运行《魔兽世界》时,所有相对文件路径和MPQ档案(MPQ archives)将被映射到游戏的根目录下。没有任何方法读取安装目录之外的文件。所以一个声音文件的路径可以像这样设置:
Interface\AddOns\yourMod\sounds\something.mps
或者这样:
Sound\Creature\Illidan\BLACK_Illidan_04.wav
第一个样例文件是放在你自己的插件中。第二个样例是在expansion-speech-locale.mpq档案(archives)中的(你将在章节的后面学到如何从MPQ archives中浏览并提取文件)。
我们首先来尝试下通过路径名来播放声音文件,因为这是最简单最好的样例。魔兽世界提供了一个简单的内部方法通过路径和文件名字来播放音频文件:PlaySoundFile(file)。尝试在游戏的窗口中运行下面的代码吧:
/script PlaySoundFile("Sound/Creature/Illidan/BLACK_Illidan_04.wav")
你将听到伊利丹的著名台词“你们这是自寻死路”(实际英文是:“You are not prepared!”中文翻译的问题,正确的翻译应该是:你们还没有准备好!),但是伊利丹明显错了;你已经准备好了。
Note:你可以播放任何"Interface\AddOns"下的声音文件,它很容易被游戏内的MP3播放器识别。你只需要将文件名字写入插件中(比如,一个table表),并将所有MP3文件复制到你的文件夹下,因为没有内部方法可以返回文件路径下的文件清单。比如,当你把所有MP3文件放在“Interface\AddOns\MyMusic”下时,你可以通过运行下面的代码在游戏中播放其中一个音频文件。
/script PlaySoundFile("Interface/AddOns/MyMusic/MyFavoriteSong.mp3")
因为我们使用Lua进行编写插件,所以我们最好使用".lua"类型的文件。我们需要告诉游戏何时执行哪些Lua代码。我们需要额外的向游戏提供一些我们插件的信息,比如名字之类的。因此我们需要使用到一个TOC文件。让我们看看这是什么。
TOC文件样式
一个TOC文件(table of contents)里面包含了一些元数据,或者说是“数据的数据”,用于描述我们插件的一些信息。随着一系列的Lua文件和XML文件一起在启用时加载。里面最少需要提供插件的名字,游戏的版本号这两个必须的属性。当然也可以在里面自定义一些属性,存储任意的数据。让我们为你的“Hello,World”创建一个TOC文件吧。
Note:《魔兽世界》仅在第一次启动游戏时去读取TOC文件,如果你更改了TOC文件的内容,你需要重新启动《魔兽世界》。记住,在游戏界面重新加载插件是不会生效的。
我们需要在游戏安装目录的Interface\AddOns下创建一个名叫HelloWorld的文件夹,并将我们的TOC文件放在里面。《魔兽世界》会试图去加载与文件夹同名的TOC文件,所以我们创建一个HelloWorld.toc并在里面输入下面内容:
##Interface:30100
##Title:Hello,World!
##Title-deDE:Hello,Welt!
##Notes:The best "Hello , World ! " addon !
HelloWorld.lua
TOC文件有两种文本格式:以##开头的元数据,除此以外的其他行都将在加载运行插件同时执行。
相当于为我们的插件定义了下面这些元数据:
Interface = 30100
这告诉游戏,我们的插件是为《魔兽世界》3.1.x版本所编写的。如果这个版本号码低于当前游戏的版本,《魔兽世界》会提醒说插件过期或者插件不兼容。
Title = Hello , World!
这定义了我们插件的名字,在已安装插件列表里面我们将看到名叫“Hello,World”的插件。
Title-deDE = Hello , Welt!
这是一个本地化的例子,只有在你使用德国的游戏客户端才会有用。它会覆盖之前我们所定义的插件名,在德国的游戏客户端上,我们的插件名字将会是“Hello,Welt!”。你可以添加任何的本地化名字将你的插件本地化。本地化是根据语言环境来进行的,就像 deDE 代表的是德国人-德语。英文客户端的语言环境总是 enUS , 即使你的游戏版本是欧洲版本。
Notes = The best "Hello , World!" addon !
该内容将在插件的工具提示中显示,用来描述插件的。
《魔兽世界》的插件toc文件中还有下面这些许多原定的属性值可以使用:
Metadata | Description |
Interface | 编写的插件版本 |
Title | 插件的名字 |
Notes | 关于插件的一些描述信息 |
RequiredDeps | 依赖插件。以英文逗号为分隔,列出该插件所需要使用的其它依赖插件。这些依赖插件加载之后才加载该插件,如果其中某一个没有加载成功或者文件缺失导致失败,则该插件也不会加载。 |
OptionalDeps | 可选插件。以英文逗号为分隔,列出该插件的可选插件。在加载该插件之前,会去尝试加载这些可选插件。即使它们可能没安装或者不存在。 |
LoadOnDemand | 是否延迟加载。有0和1两个值,默认为0。在启动游戏客户端的时候延迟加载的插件不会随之启动。你可以在其他插件中调用方法 LoadAddOn(addon)来启动这个插件。 |
LoadWith | 当LoadOnDemand为1的时候,才有用。一组以英文逗号分隔的插件。当其中的任意一个插件加载完成之后,该插件也会进行加载。 |
SavedVariables | 存储的全局变量。以英文逗号为分隔的全局变量列表。当退出游戏的时候,这些全局变量将存储到指定路径下。路径:\WTF\Account\<account name>\SaveVariables\<addon>.lua 其中<account name>为帐号名称,<addon>为插件名。 |
SaveVariablesPerCharacter | 和SavedVariables所做的事一样,不一样的是,它可以存储任意字符数据。存储路径:\WTF\Account\<account name>\<server>\<character>\SavedVariables\<addon>.lua |
LoadManagers | 加载管理。以英文逗号为分隔的插件列表。如果其中一个是可用的,那么LoadOnDemand的值将被视为1 |
DefaultState | 默认状态。enabled启用或者disabled禁用。这决定了插件的默认状态是启用还是禁用。 |
Secure | 是否安全。有0和1两个值。只有暴雪自己的插件才用得到。 |
你也可以定义使用自己的元数据,以X-开头。使用API方法GetAddOnMetadata(addon,attribute)方法来获取自己设置的元数据值。需要注意的是,这个方法也可以去读取那些没有加载的插件。所以TOC文件就是定义那些插件启动时需要加载的东西。你也没必要专门为这些元数据设置语言,系统会自动进行检索。
让我们看一下这个案例。“The Eye of Eternity”(永恒之眼)里的一个模组“Deadly Boss Mod”(有名的DBM插件,用于BOSS技能报警的)的TOC文件。它广泛使用了很多元数据,以及自定义的数据:
##Interface:30100
##Title:<DBM> Eye of Eternity
##Title-deDE:<DBM> Auge der Ewigkeit
##LoadOnDemand:1
##RequiredDeps:DBM-Core
##SavedVariables:DBMEyeOfEternity_SavedVars,DBMEyeOfEternity_SavedStats
##X-DBM-Mod:1
##X-DBM-Mod-Category:WotLK
##X-DBM-Mod-Name:Eye of Eternity
##X-DBM-Mod-Name-deDE:Das Auge der Ewigkeit
##X-DBM-Mod-Sort:3
##X-DBM-Mod-LoadZone:The Eye of Eternity
##X-DBM-Mod-LoadZone-deDE:Das Auge der Ewigkeit
localization.en.lua
localization.de.lua
Malygos.lua
除了deDE德语德国的自定义数据,其他的语言版本都被作者移除了。真正的文件比这些还要长,每一个元数据/自定义数据,都有四个添加的语言版本。(下面是2021年1月9日DBM更新的插件,其中一个TOC文件的截图,现在已经不止支持4个语言版本了。)
这个文件(不是图片里的,而是代码段里的)定义了很多元数据,都是DBM插件在启用时需要启动的其他插件。其中最重要的属性是X-DBM-Mod-LoadZone,当进入指定的位置的时候,这个属性会去加载指定的插件。这就是DBM插件运行的关键部分。元数据的真正用到的地方是在mod被加载的时候。
我们已经创建了一个TOC文件,现在我们需要去做一些更酷的东西。《魔兽世界》提供了许多内部方法,让我们在插件中能够用的上。
使用WoW的API
到现在为止,你可能已经知道这几个API方法了:print,PlaySoundFile,LoadAddOn和GetAddOnMetadata。所有的API方法在全局变量中都是可用的,在Lua文件XML文件(可以嵌入Lua代码中,将在第五章节学到更多)也是可以使用的,也可以在命令行中使用。这些方法可以从方法名,参数知道它的作用。你可以在WoWWiki页面(http://www.wowwiki.com/API)找到全部方法的具体说明。出于下面两点原因,本书将不会罗列出所有可用的方法:该方法比大多数方法的说明更长;这个方法在以后的版本中将过时。所有的Lua方法已经在第二章节描述过,都是可用的除非特别提到。
也有少量的不同类型的函数。大多数方法都是“真正”的API方法,但是有些方法是使用C语言编写的,然后通过调用Lua的C语言的API来使用它。有一个很好例子的方法:PlaySoundFile()。在默认的UI中是没有定义这个方法的Lua文件的。它是直接调用底层的API接口,在多数情况下是不可能通过Lua代码去实现这个方法的。
你可以在默认UI里的Lua文件找到许多的方法;这些方法的前缀多以UI开头。这里以有两个入参的方法UIFrameFadeOut(frame,time)作为例子,这个方法的作用是随着指定时间的推移通过减少透明度的方式逐渐将某一个frame淡出。
记住,你也可以在游戏中使用/script <lua code>来执行Lua代码。使用这个方法来看这些API方法是如何工作的。举个例子,你可以使用这些参数来运行UIFrameFadeOut方法:
/script UIFrameFadeOut(Minimap,1)
这段代码将使你的小地图在1秒后消失看不见。但是,你要如何还原回来呢?全局变量Minimap代表的是什么?
这个思考让我们知道了第二种API类型方法:面向对象的API接口。所有的窗口frame都是对象;他们可以通过使用CreateFrame(frameType)方法来创建,或者使用XML文件来进行创建。你将在第五章看到如何创建这些窗口frame和可用的类型。窗口frame基本上可以看做是一个存储了userdata用户数据和方法的表格。回忆一下你使用冒号调用方法,实际上这些方法function基本上都存储在表table中。记住,窗口frame包含了许多的函数方法。所以,第三种API类型方法就是真正的API函数方法,存储在窗口frame中的,并且通过冒号来调用。让我们来调用这些小部件和这些窗口frame方法吧,他们都是用户的图形化界面密切相关的。
现在我们来看一下基本的面向对象的API。在接下来的两个章节,你将学到更多的关于窗口frames和面向对象的组件model。让我们看一下所有窗口frame都可以使用的方法:SetAlpha(value)。它用来设置窗口的透明度(取值在0和1之间),所有的透明度类型的方法都使用冒号来调用。试着用这条语句,让你的小地图重新显示出来吧。
/script Minimap:SetAlpha(1)
同样的GetAlpha方法也是可以使用的;用来返回当前对象的透明度。你可以找到一份所有可用方法和窗口类型的完整表单。在附录A中。
回到我们的 Hello World mod项目中。我们的插件已经有了《魔兽世界》所认可的元数据,对,就是那个TOC文件。现在我们需要使用一些API方法在文件中添加Lua代码来做一些事情。在我们的TOC文件中我们已经准备了 HelloWorld.lua 这一行数据。当你试图加载插件HelloWorld时,《魔兽世界》将试图在“HelloWorld”文件夹中找到这些文件。所以,让我们来创建这些文件。
当你登录游戏的时候,客户端会去执行这些lua代码。所以在文件中编写一个print("Hello,World!")就会在登录后的聊天窗口显示“Hello,World!”。我们现在需要进行交互。让我们来创建一些简单的指令吧。
Note:在很早之前我已经告诉了你,在添加新的文件后需要重新启动游戏。但是当你更改了一个代码文件内容后你不需要重新启动游戏,直接输入 /console reloadui 来重新加载所有之前的文件。
创建斜杆指令(Slash Commands)
创建一些自定义的斜杆指令是很简单的。所有处理程序的指令方法都存储在表table中,通过使用全局命名空间SlashCmdList。后续我们将使用类似于/hwadd这样相关的精简指令。用户可以随时调用这个方法添加一些精简指令。通过斜杆指令(slash commands)接收一个字符串。所以,输入/hwadd test string 将使用“test string”来调用接收程序(handler)。
在本章开始之前我们就决定创建一个mod了,我们需要用到两种精简指令:一条储存我们设置好的字符串,另一条则将设置好的字符串输出在聊天窗口。分为两步来创建精简指令。首先,我们需要为我们的指令接收器(handler)命名一个不重复的ID,在下一步中我们这将成为使用表SlashCmdList的关键。我们为指令/hwadd设置的命名为HELLO_WORLD_ADD。我们的方法将会和此键所关联。我们也需要设置我们想使用的精简指令来和这个ID相关联起来。我们也可以设置一些全局变量,例如SLASH_identifier1 到 SLASH_identifierN(这里作者的意思是说,可以在末尾加数字,从1开始一直到N),可以对应到很多条斜杆指令。在我们的实例中我们使用两个SLASH_HELLO_WORLD_ADD1和SLASH_HELLO_WORLD_ADD2来对应两条斜杆指令。
第二步呢,通过使用我们设置好的id在表SlashCmdList中存储我们设置好的指令。通过传入一个参数来设置接收器中的方法。我们想要使用/hwadd <id> <text> 和/hwshow <id> 两条斜杆指令。这意味着我们需要使用两个指令handler。下面的代码就是很好的例子。
如果你还没有创建HelloWorld.lua文件的话,现在就去创建它吧,并在里面写入这些代码:
HelloWorld_Text = {}
local channel = "SAY"
SLASH_HELLO_WORLD_ADD1 = "/hwadd"
SLASH_HELLO_WORLD_ADD2 = "/helloworldadd"
SlashCmdList["HELLO_WORLD_ADD"] = function(msg)
local id,text = msg:match("(%S+)%s+(.+)")
if id and text then
HelloWorldText[id:lower()] = text
end
end
SLASH_HELLO_WORLD_SHOW1 = "/hwshow"
SLASH_HELLO_WORLD_SHOW2 = "/helloworldshow"
SlashCmdList["HELLO_WORLD_SHOW"] = function(msg)
local text = HelloWorld_Text[msg:lower()]
if text then
SendChatMessage(text,channel)
end
end
让我们一行一行来进行解析。在最前面,我们定义了两个变量。全局变量HelloWorld_Text是一个table类型,用户使用/hwadd添加的字符串都会被添加进去。局部变量channel设置了我们想要的聊天频道。然后,我们为第一条斜杆指令设置了一个ID为HELLO_WORLD_ADD。我们通过key值HELLO_WORLD_ADD在SlashCmdList中存储了传入一个参数msg的方法,在任意时刻,当我们输入/hwadd <text>或者/helloworldadd <text>来调用那个方法。
在HELLO_WORLD_ADD接收器中的第一行通过正则表达式解析了用户输入的值。如果你没有在第二章看到这部分,现在回去看它吧。首先,%S是对%s的补充,%s代表了所有的空格字符,%S代表了所有的非空格字符。+号是“至少有一个”的意思。括号括起来是为了捕获那些,首先通过match方法匹配到的字符串。所以,第一个非空字符到第一个空格字符之间的字符,将被视作为id。第二个表达式是为了分隔两个参数,第三个表达式表示剩下的字符串。
这个正则表达式可以匹配所有用户想象得出的像“ foo bar"这样的输入方式,如果输入的字符串没有匹配这个正则表达式,那么match方法将返回空值nil。所以我们接下来的任务确保id和text不为nil才能进行。在输入设置了一个有效的id和text后,我们可以将id小写化当做是key键存储到HelloWorld_Text中,将对应的值设置为text。如果正则表达式没有匹配到任何字符,我们也无法得知。所以我们需要添加一个简单的报错信息来告诉我们是无效的输入,修改一下代码:
if id and text then
HelloWorld_Text[id:lower()] = text
else
print("Usage: /hwadd <id> <text>")
end
第二条斜杆指令就比较简单了,它获取用户输入的值,将其小写化,然后将其作为key值去HelloWorld_Text寻找对应的值。如果匹配到了对应的值,将调用方法SendChatMessage(msg,channel,language,target)在对应的聊天频道使用指定语言发送信息。只有第一个参数msg是必须的,channel参数可以是下面这些值中的一个:SAY(默认值),YELL,EMOTE,WHISPER,PARTY,RAID,BATTLEGROUND,RAID WARNING,GUILD和OFFICER。language参数将使用游戏客户端使用的语言,如果为空将会使用msg字符对应的语言。target参数只有当channel为WHISPER才使用得到。
持久化
有一个问题一直存在着:表table中的数据是临时的,意味着当你退出游戏的时候,这些数据就会消失。所以我们需要将它们保存在某个位置。我们可以使用一个你见过的元数据属性,SavedVariables,来存储数据。直接在我们的TOC文件中添加下面这一行:
##SavedVariables: HelloWorld_Text
(当你更新了你的TOC文件后,别忘了重新启动游戏)这一行数据是干什么的呢?
当你登录游戏《魔兽世界》,客户端会加载你的mod,然后初始化一个新的HelloWorld_Text,用空表table数据。在mod加载完成之后,《魔兽世界》游戏会试图加载存储在这个mod里的变量。当你第一次使用这个mod的时候,HelloWorld_Text没有保存过的,不会发生任何事情。如果HelloWorld_Text保存过,那么它就不会被设置成nil空值,并且将原先初始化的空表删除,然后将这些数据存储进去。需要注意的是,当加载mod的时候,初始化的变量不再使用默认值而是使用存储的值。当你退出游戏的时候,如果HelloWorld_Text不是空的,那么客户端会将里面的结果存储下来。
但是默认值还是会造成一些问题的。想象一下你有一个插件,在表table里存储了一些选项,这些选项可能像这样:
MyAddOn_Options = {
Option1 = true ,
Option2 = false ,
Option3 = "a.string!"
}
现在假设在使用这个插件几天之后,新版本发布了。它在里面添加了一些新的选项,而且MyAddOn_Options初始数据变成了下面这样:
MyAddOn_Options = {
Option1 = true ,
Option2 = false ,
Option3 = "a.string!"
Option4 = 7.5
}
如果现在一个新用户安装了这个插件,那是没问题的。这个用户之前没有存任何数据,所以它可以得到所有这4个选项option。但是,你更新你的插件的时候,《魔兽世界》将向全局变量MyAddOn Options重新写入数据,删除原有的新的表table。因为之前存储的数据没有Option4,所以它的值将为空。假设这个变量在mod中是一个数值类型并且尝试和其他结果相加,那么它会抛出一个错误“attempt to perform arithmetic on a nil value”。保证你的mod是可更新的是非常重要的,可能在之后版本里,你将为你的插件添加新的特性。如果暴雪在以后的补丁中决定更新它的API方法,也有可能会让你的mod无法使用。到时候你将不得不推倒你的mod,并发布一个全新的版本。
针对这些问题有一些解决方法可以参考。第一个方法,可以更换全局变量的名称,但是这个方法有很多缺点,所有使用到该变量的地方你都要进行替换,每次为你的插件添加新选项option的时候,用户都将损失他自己原先的设置。而且,每更新一次就要去替换所有使用的地方,是很低效的编码风格。这种解决方案只适用于那些小型插件,添加类似于单项选择的时候默认值最好设置为nil。你可以在之后使用if判断对其设置默认值。这种方法虽然快速,但是随着时间推移,你将添加更多的if判断,会让代码更加让人困惑,这也不是最好的编码风格。
最好的解决方法是将默认值存储在单独的表table中。这个表里面的内容只存储用户更改过的选项。在这本书里,你将看到一个非常聪明的方法。这个方法的好处在于,每次当你要访问它的时候都将它的值重置为默认值。在本书中,我们将在所有案例中采用这种方法来保存选项option。
我们创建我们的第一个《魔兽世界》插件,而且也提到将为插件写一个默认的UI界面。所以,你可能想要知道暴雪是如何进行编写的。在本章节的末尾,你将解开这个疑惑。
提取默认的UI界面
默认的UI界面存储在 MPQ档案文件中,有这么几个方法可以从中提取出其中的代码。最简单的方法是使用暴雪自己的接口插件工具,也就是在第一章中介绍过。这个工具使用起来非常的简单,而且还可以提取出所有的Lua文件,XML文件,TOC文件以及所有默认UI界面使用的纹理。
你也可以使用专门的MPQ查看器,或者那种MPQ编辑器来查看所有的MPQ文件,这些也在第一章中介绍过。这些MPQ编辑工具比暴雪的接口插件工具更强。例如,编辑器可以直接提取出可用的音频文件,模组文件,材质文件,还有暴雪工具不会提取出来的一些Lua文件和XML文件(比如说,登录界面的代码)。
MPQ编辑器就像解压工具那样使用。你在文件菜单栏中搜索“Open MPQ Archive”,导航到《魔兽世界》安装目录下的Data文件夹。让我们打开lichking.mpq文件,里面包含了WotLK相关的数据。如果这个项目提示你是否打开选项(Open Options),选择默认值就好了。现在你可以浏览archive里面所有的内容了,通过双击打开他们,通过拖动文件来提取数据。举个例子,我们看到了一个Sounds文件夹,里面包含了背景音乐和一些生物的声音。你可以在插件中使用这些音频文件。
Attention注意:MPQ编辑器是一个很强大的工具,甚至允许你创建自定义的MPQ文件。但是千万别这样做,它会破坏你的游戏,并且违反了用户协议。
你也可以访问这个网站http://wowcompares.com(貌似不可用了,有梯子的可以试试),diffs会详细的列出所有补丁的差异。
Note:diff将列出两个版本之间的差异,你可以发现两个版本之间的变化。
如果你不知道一个API方法是如何工作的或者在更新了一个补丁之后有什么改变,而且暴雪基本没有提供什么参考文档的时候,在MPQ archives看里面的代码是很有帮助的。你也许注意到了一些奇怪的地方,比如说,在代码的任意地方使用分号。许多插件开发者都不会使用分号,大多数情况,分号在Lua代码中不是必要的。
下面这段代码是容易读懂的,而且还有少量的注释。加一些注释有利于阅读代码:
elseif (status == "error") then
-- Should never happen haha
end
这里还有另一个案例
else
-- Temporary Hack (Temporary meaning 2 yrs, haha)
mapFileName = "World";
OutlandButton:Hide();
AzerothButton:Hide();
end
总结
在这个知识点里,你知道了Lua是如何工作的,知道了一些《魔兽世界》基本的API方法,而且你也可以创建插件并且在《魔兽世界》中运行加载它们。所以,在下一个章节,是什么创建你的第一个有用的插件了。