魔兽世界的插件系统
魔兽世界的插件系统
TungLam简介
「魔兽世界」的插件是用 Lua 和 XML 编写的扩展程序,作用是用于增强、修改和替换「魔兽世界」的用户界面,或者实现一些游戏本身不具备的功能,方式是使用 Lua 调用游戏提供的 API 来实现特定的逻辑。
在游戏领域中,插件 和 地图 是两种常见的 UGC 形式,都是由用户/玩家来开发实现的游戏扩展,但是他们的侧重点有所不同,具体表现为:
- 插件:通过脚本语言调用游戏接口来实现,更多的是对游戏当前内容进行增强、修改和替换
- 地图:通过游戏提供的地图编辑器来实现,更多的是用于新增、补充游戏当前所没有的内容
在插件或者地图的开发过程中,体验也有所不同:
- 插件:插件的开发者更接近于游戏团队中的「程序」职位,为游戏提供当前所没有的功能,工作内容大多数是在撰写代码
- 地图:地图的开发这更接近于游戏团队中的「策划」职位,利用已有的工具来创造新地图,工作内容大多数是在关卡设计
常见插件
「魔兽世界」常见的插件有如下几类:
- 界面增强
- ElvUI:界面重塑插件,可以完全替换官方默认 UI,高度可定制化。
- Bartender4:动作条替换插件,允许自定义动作条的位置、大小和透明度。
- WeakAuras:用于创建自定义的图形提示,帮助玩家跟踪技能冷却、增益和减益等效果。
- 战斗辅助:
- Deadly Boss Mods:提供副本和首领战斗的实时警报和计时提醒,帮助玩家更及时地了解战斗实况。
- Details! Damage Meter:详细的伤害统计插件,帮助玩家分析战斗表现,了解场上输出和团队整体表现。
- GTFO:提供声音警报,提醒玩家注意地面效果和危险区域。
- 经济管理:
- TradeSkillMaster :帮助玩家管理拍卖行、制作和销售物品,优化经济效益。
- Auctionator:简化拍卖行操作,提供快速的物品搜索和价格比对功能,适合日常交易。
- Postal:改善邮件管理,提供批量发送和接收邮件的功能,方便玩家处理邮件。
「魔兽世界」的默认界面也是一个插件,可以在控制台输入 exportInterfaceFiles code
和 exportInterfaceFiles art
来导出默认界面的插件源码和美术素材
技术方案
所有的插件都位于一个名为 AddOns 的目录中,该目录的路径为 %WorldOfWarcraftFolder%\Interface\AddOns
,其中 %WorldOfWarcraftFolder%
代表你的电脑中魔兽世界的安装位置,一般情况下,完整路径如下:
- Windows:
C:\Program Files\World of Warcraft\Interface\AddOns
- MacOS:
/Applications/World of Warcraft/Interface/AddOns
一个「魔兽世界」的插件是由下面三种文件构成的:
- TOC:描述文件,用于描述插件的名称、作者、用途和插件逻辑的入口
- XML:界面布局文件,用于描述插件的 UI 布局
- Lua:逻辑脚本文件,用于实现插件的具体逻辑
TOC —— 工程描述文件
toc 文件描述了插件工程的基本信息和逻辑入口,主要包含以下内容:
1 | ##Interface: 适用的客户端版本号 |
- 以
##
开头的是插件元数据,用于描述插件的名称、用途、作者等信息,所有可供使用的元数据如下: - 以 # 或 % 开头行的为注释行
- 在文件最后的
HelloWorld.lua
/HelloWorld.xml
则指示了该插件需要加载的 Lua/XML 文件,如果有多个需要加载的文件,则分多行写入,从上到下对应着文件的载入顺序。
Lua
一个最简单的插件 Lua 脚本如下:
1 | local frame = CreateFrame('Frame') |
- 该脚本创建了一个 Frame 对象,并通过 Frame 来监听 玩家登录 事件,在玩家登录事件触发后,打印一条
Hello World
的消息。 - 其中 Frame 是逻辑的入口点,所有的视觉界面元素都是一个 Frame,比如窗口、按钮、文本框,一个 Frame 里面也可以包含其他 Frame,比如窗口里可以包含文字、按钮,利用 Frame 来构成任意复杂的界面。
- Frame 也可以调用 RegisterEvent 和 SetScript 函数用于监听事件并注册响应函数,从而让游戏客户端调用插件实现的自定义逻辑。
- 在该插件中,不需要可视化图形界面,只需要 打印一条消息 这样的逻辑功能,所以默认创建了一个没有设置任何样式、不可见的 Frame。
对于 Lua 中的功能函数,想要让游戏客户端进行调用主要有三种方式:
- 监听事件:监听游戏事件,事件发生时则触发自定义的逻辑
- 定时 / 周期任务:设置定时/周期任务,在合适的时间点触发自定义的逻辑
- 斜杠命令:注册斜杠命令,玩家在控制台输入对应命令时触发自定义的逻辑
监听事件
在 Lua 中使用 Frame 来监听游戏事件:
1 | frame.RegisterEvent('PLAYER_LOGIN') |
目前所有的事件分类有 159 种,总共 1499 条,可以查阅 Events - Wowpedia - Your wiki guide to the World of Warcraft 文档来获取需要使用的事件
定时任务和周期任务
「魔兽世界」中的定时任务或周期任务有三种方式来实现:
C_Timer.After
:用于创建一个定时器,在指定的延迟后执行一次函数:
1 | C_Timer.After(5, function() |
C_Timer.NewTicker
:用于创建一个定时器,在指定的时间间隔内重复执行某个函数:
1 | C_Timer.NewTicker(2, function() |
Frame.OnUpdate
:设置一个函数,在游戏的每一个渲染帧都调用该函数:
1 | local frame = CreateFrame('Frame') |
C_Timer
和 OnUpdate
各自使用的场景有所不同:
C_Timer
:- 性能:按指定的时间进行触发,与帧率无关,可以减少对性能的影响
- 精确:允许开发者基于实际时间而非帧数来调度任务的执行,时间控制更加精确直观
Frame.OnUpdate
:- 高频:在游戏的每一渲染帧都会执行,频率更高,适合需要快速相应的任务
- 灵活:可以在
OnUpdate
中实现定时器的功能,使用更加灵活,对于需要根据时间变化连续处理数据的场景更为直接有效
斜杆命令
对于一些需要玩家主动调用的插件函数,可以对该函数注册一个斜杠命令,在控制台内通过 /命令名
的方式来触发该函数的执行,注册斜杠命令的脚本如下:
1 | local function ToggleVisible(message, editbox) |
该脚本注册了一条斜杠命令,在客户端控制台内输出 /hello
,即可实现 frame 的显示和隐藏
在插件开发过程中,插件需要调用接口来获取游戏内的状态并做出处理,「魔兽世界」提供了大量 API 来供插件使用,包括拍卖、竞技场、聊天、成就等类别,所有的 API 函数可以参阅 World_of_Warcraft_API 来获取
XML
插件中的 XML 是布局描述文件,用于描述插件的 UI 布局,作用类似于 Web 开发中的 HTML 文件,利用 XML 来描述界面布局,Lua 来实现具体逻辑,可以很好地将设计与逻辑分离开,实现系统的解耦,一个 XML 文件一般包含以下内容:
1 | <!-- UI 注释行 --> |
Ui 标签
<Ui>
是最上级标签,其中的 schemaLocation
用来做语法检查或代码提示,如果编辑器不支持的话,也可以简化成:
1 | <Ui xmlns="http://www.w3.org/1999/xhtml" |
Script 标签
<Script>
用于载入 Lua 文件,导入顺序和文件上下顺序关联,除了在 XML 中导入 Lua 脚本文件以外,我们也可以使用 TOC 内声明的方式来导入 Lua 文件
Frame 标签
<Frame>
是最重要的标签,用来声明插件的界面布局和样式,也提供了注册事件、响应输入等接口,所有的「魔兽世界」插件都需要使用 Frame
来做为逻辑的入口点,其他的控件一般都是 Frame
的子类,比如 <Button>
,<Slider>
,<StatusBar>
等等,可以通过 属性 标签来设置 Frame 对象的属性值,如下:
1 | <Frame name="名称" inherits="继承 该控件未标明的属性全都继承自某控件" parent="UIParent 父标签" id="编号" movable="true 可移动"> |
比较特殊的是 <Scripts>
标签,在其中可以直接写入 Lua 代码来执行逻辑,效果和把代码写到 Lua 文件中并加载是一样的
「魔兽世界」中的 UI 对象继承关系如下:
XML 和 Lua 的关系与 HTML 和 JavaScript 的关系十分类似,都是为了把逻辑和表现分离,实现解耦,在一些样式比较简单或者逻辑比较简单的情况下:
- 可以通过 CreateFrame,SetSize,SetPoint 等接口在 Lua 中创建所有的界面和设置样式,无需单独的 XML 文件
- 也可以通过 Scripts 标签把所有的逻辑代码都写在 XML 文件中,无需单独的 Lua 文件
总结
对于游戏玩家而言:
「魔兽世界」的插件系统允许玩家通过第三方插件来定制和增强游戏体验,在界面定制、信息增强、任务辅助上都可以很好地满足玩家的需求、减轻游玩负担,但是插件的安装和开发都具备一定的门槛,对新手玩家不太友好,且很有可能会产生依赖性,导致没有插件的情况下难以适应游戏。
对于开发人员而言:
通过 exportInterfaceFiles code
和 exportInterfaceFiles art
可以发现,「魔兽世界」的默认界面本身就是一个插件,某些情况下,游戏的开发者和插件的开发者是在使用同一套框架进行开发,同时插件使用 XML 来定制界面、Lua 调用接口来实现逻辑的技术方案,一定程度上,可以迫使游戏开发者在设计 UI 框架、暴露游戏接口时进行更多的思考,倒逼软件设计的正交性、解耦性等。