2020-05-17 22:36
[教程] 老插件教材《Beginning Lua with World of Warcraft Add-Ons》一点粗浅的翻译
英文教材翻译起来太累,而且我不是学编程的,有些术语不了解,好多地方也不知道怎么翻译准确,就按照粗浅的理解翻了一点,剩下的翻译不动了,各位谁有兴趣随便看看,抱歉了。平时不发帖,不大会排版,我试试看
首先说明,这个教材很老旧,大概是巫妖王版本的,有好多API变动很大,而且后来暴雪在游戏中的艺术素材换文件格式了,所以有的章节会出现一些已经没有的东西,已经翻译的部分我尽可能查证后做了标注,理解一下,这本书网上没卖的,找到了电子版,分享给各位爱好的朋友。
链接:https://pan.baidu.com/s/1KoDaypF6YzDqzPsjCuvzSQ
提取码:tyvt
这是查看魔兽自带美术素材的一些工具,魔兽自带代码和美术素材的提取与查看方法在我的另一篇教程里有。
链接:https://pan.baidu.com/s/1RNmlmablHpCMfQAKA4Xqig
提取码:69bf
第一章是准备工具,大多不能用了,第二章是lua基础,网上有很多教程,我是看的一个老视频系列,叫《医生不吭》,所以也没翻译。
======第三章 使用API创建MOD======
/hwadd<id><text>,/hwshow<id>前者将文本添加到将要保存的表中,而后者将保存在id下的文本发送到聊天通道。Gold farmers可能会认为,这个插件唯一可能的用途是利用广告向交易聊天发送垃圾信息,因为它允许您使用一个宏发送大量文本。这个mod的另一个很好的用途可能是保存长boss或战场命令,例如,在一个宏中。你可以很容易地把信息发送到突袭或战场聊天。
让我们从在游戏中运行Lua代码开始
Running a “Hello, World” in WoW
要运行我们将要创建的脚本,只需在魔兽世界中输入以下斜杠命令:
命令/script<text>将<text>编译为一个组块并运行它
组块是具有自己作用域的Lua代码块。因此,如果您在/script命令中定义了一个局部变量,那么在下一个/script命令中它将不可见。
您已经知道print函数,但是这里它与标准Lua有一个小小的区别:魔兽世界将输出写到默认聊天框,而不是您的标准输出。这将在默认聊天框中显示“Hello, World”。这个例子只是为了说明魔兽世界中的Lua和普通Lua的工作原理是一样的。现在我们可以开始创建一个Lua脚本,它被游戏加载为一个插件.
Our First WoW Addon
让我们创建我在本章开头描述的插件。在开始创建mod之前,我们需要决定的第一件事是将Lua文件放在哪里。
Folder Structure
在这个文件夹中创建文件夹helloworld'。这是我们将为“Hello, World”mod创建所有文件的地方。addons文件夹有许多不同的文件扩展名。插件可以使用哪些类型的文件?
魔兽世界试图加载与文件夹名称相同的TOC文件
在这个TOC文件中有两种类型的命令:以包含元数据开始的行。所有其他行都是将被执行以加载插件的文件。
我们为插件定义以下元数据
有许多元数据属性被魔兽世界识别;表3 1表中的各项是在TOC中用双井号添加
可用的元数据属性
双井号(##)后还可以通过使用以x-开头的特性来使用属于自己的元数据。API函数GetAddOnMetadata(addon,attribute)可用于获取元数据。注意,这个函数也适用于尚未加载的插件。所以TOC文件是决定什么时候加载“需要被加载的插件”的文件。检索元数据时不需要附加地区;这是自动完成的。
让我们看一个例子。致命Boss模块的例子“永恒之眼”是一个TOC文件,它广泛使用定制元数据。
除德语属性外,我删除了所有本地化属性;实际文件比较长,因为每个本地化都需要额外的4行。
该文件定义DBM需要的所有元数据,以便在不加载它们的情况下构建可用的mod列表。最重要的属性是X-DBM-Mod-LoadZone,它指示DBM在您进入指定区域时加载插件。这是DBM按需加载工作原理的关键部分。元数据的真正强大之处在于它可以在加载mod之前使用。我们已经创建了一个TOC文件,但是现在需要添加Lua代码来做一些很酷的事情。
Using the WoW API
您已经看到了一些API函数:print,PlaySoundFile,LoadAddOn,和GetAddOnMetadata。所有API函数都可以在全局中使用,因此它们在任何地方都是可用的,:在Lua文件中、在XML文件中(它们可以嵌入Lua代码,我们将在第5章中详细介绍),以及在命令行脚本中。这些函数有自解释的名称和参数。您可以在WoWWiki页面上找到一个完整的特定于wow的函数列表www.wowwiki.com/API。
1.有几种不同类型的函数。最常见的是“真正的”API函数,之所以这么叫是因为它们是用C编写的,并且可以通过Lua的C API获得。这样一个函数的例子是playsoundfile()。默认UI中没有定义这个函数的Lua文件。相反,它是由底层接口API直接提供的,在大多数情况下,不可能使用Lua实现这种类型的函数
2.您还可以在默认UI的Lua文件中找到很多函数;这些函数大多以前缀UI开始。第二类函数的一个例子是UIFrameFadeOut(frame,time),这个插件会随着时间的推移通过减少alpha值(框架的不透明度)淡出框架。
记住你总是可以使用/script <lua code>在游戏中执行Lua代码。用它来处理一些API函数,看看它们是如何工作的。例如,您可以用以下参数调用UIFrameFadeOut来测试UIFrameFadeOut:
这将使你的小地图在1秒后消失。但是你怎么才能找回它呢?
这就引出了下一种类型的API函数:接口API的面向对象部分。所有的框架都是对象;框架可以通过调用CreateFrame(frameType)来被创建或者使用XML文件。您将在第5章中看到如何创建这些框架和可用类型。框架基本上就是一个包含userdate值和许多方法的表。回想一下,您必须使用冒号操作符来调用方法,而这些方法基本上就是存储在表中的函数。这里需要记住的是框架包含许多方法。
3.第三种类型的API函数是一个真正的API函数,它存储在一个框架中,并希望使用冒号操作符调用它。让我们将这种类型称为小部件或框架函数,因为它们与图形用户界面元素密切相关。
现在我们只看面向对象API的基础。在接下来的两章中,您将更多地了解框架和它们背后的面向对象模型。让我们来看一种适用于所有帧的方法:SetAlpha(value)。它设置帧的透明度(一个介于0和1之间的数字),它的所有方法都是通过冒号操作符调用的。试着用这个来找回你的小地图:
GetAlpha()方法也可用;它返回当前的alpha值。您可以在附录a中找到所有可用方法和框架类型的完整列表.
但是回到我们的Hello World mod,我们有一个插件,它拥有魔兽世界需要的元数据,但是它只是一个TOC文件。现在,我们需要将Lua代码添加到文件中,以便处理这些API函数。我们的TOC文件中已经有了helloworld.lua行。当您无论何时试图加载HelloWorld插件时,魔兽世界会尝试在HelloWorld文件夹中找到此lua文件,因此让我们创建此文件。
当您登录时,游戏将在该文件中执行Lua代码。因此,在该lua文件中写入print(“hello,world”)将在你的聊天框显示“Hello, World!”。但这对我们没用;我们需要一些互动的东西。让我们创建一些斜杠命令。
小技巧:/console reloadui来重新加载以前加载的所有文件
Creating Slash(斜杠)& Commands创造斜杠命令
创建自定义斜杠命令很容易。SlashCmdList是全局的一个表,含有要用的函数,每个函数有自己的斜杠命令,由斜杠命令调用,斜杠命令后可以带有要处理的数值。说白了斜杠命令就像表中的一个函数的名称,名称后打上要处理的参数。
这里我们创建俩,一个用来储存字符串,另一个把储存你的字符串发出去
Handler说白了就是表中的函数的意思
创建斜杠命令有两个步骤。第一,为函数想一个名称,该名称就是列表中的你要用的函数的名称,这个名称在下一步和一个专用的key进行绑定,这样没用到KEY就是在调用函数。从这里看出来函数就是具体的操作或算法,函数叫什么要你手动去设置,系统不会自动生成(除了LUA语言和暴雪API自带的)。我们给第一个要用的函数起名字叫HELLO_WORLD_ADD(所以说这种大写加下划线的写法是handler函数的名称的写法),和这个函数绑定的KEY是/hwadd。
第二步是将KEY代表的函数存储在表SlashCmdList中。这个函数用来处理变量,是执行部分。请记住,我们想要使用的斜杠命令是/hwadd <id> <text>和/hwshow<id>,顾名思义,从命名的字面可以看出第一个KEY用来存储信息,第二个KEY用来发送信息。这意味着我们需要两个斜杠命令,当然各自绑定一个函数。
要创建我在本章开头讨论过的mod,我们需要两个斜杠命令的效果是:一个用于存储消息,另一个用于检索消息并将其发送到“chat”通道,这里的chat通道就是游戏里面你按回车键,左下角白色的聊天框,你打完字发出去,你的角色头上就会跳出来一个气泡给别的玩家显示你说的话,这个最直白的聊天通道就是chat,还有其他的聊天通道,有不同的对应的英文名称,比如你和好友私聊,用的通道就是“whisper(好像是这个单词来着)”,比如一些打团插件会在副本队伍里报告打断技能“某某人打断了BOSS技能”,发出来的文本是橘黄色的,这就是在插件里设置了喊话功能,自动把你的行为在“副本队伍”通道里喊出来了。
如果您还没有创建文件HelloWorld.lua,那么现在就创建它。然后添加以下代码:
书中的上列代码中有错:ADD中的存储函数少了一个下划线
让我们逐行看一下这个文件。首先,我们初始化(也就是设置)两个变量SLASH_HELLO_WORLD_ADD1和SLASH_HELLO_WORLD_ADD2,其实这里用一个就行,写第二个是为了保险一点,多留一个KEY,但是两个KEY的功能是一样的。在这里的SlashCmdList就是魔兽API的内容,它的作用是把你起的名字和函数功能绑定起来,可以看出来这里的方括号里面就是函数名字,用的是大写加下划线,后面的function就是函数/功能的意思,是LUA语言设置函数内容的固定形式,后面带个圆括号,圆括号里是你要处理的消息。这里学过LUA的很容易看懂,下面几行就是给这个名称(HELLO_WORLD_SHOW)的函数写内容啦,全局变量HelloWorld_Text将创造一个表,其中包含用户通过KEY想要保存的消息。本地变量channel(通道)将包含我们想要使用的聊天通道。然后,我们用id(也就是函数名称的意思)HELLO_WORLD_ADD创建第一个斜杠命令。每次我们把msg(也就是message的简写,消息)输入到/hwadd <text>或/helloworldadd_<text>的text处,就会调用SlashCmdList中的HELLO_WORLD_ADD的函数来处理消息。
HELLO_WORLD_ADD处理程序的第一行解析用户输入。这是通过使用正则表达式regular expressions(正则表达式就是捕捉符合要求的字符,所以应该是match函数???)实现的;如果你还没有读过第二章关于他们的部分,现在就去读。
首先,%S是%s的补码。%s表示所有空格字符,%S表示所有非空格字符。“+”的意思是“至少有一个”。表达式周围的括号将其定义为捕捉,因此符合捕捉要求而被匹配出来的字符串将是match方法的第一个返回值。这个被筛选出来的信息是以第一个非空白字符为开通,第一个空白为结尾的。因为id是根据函数从msg中筛选出来的,所以id是msg的子字符串。第二个表达式(即%s)表示两个参数之间的分隔空间,第三个表达式(即.+)(也是第二个捕获)是字符串的其余部分。
这种模式可以适用于所有的用户输入像” foo bar”,如果模式与给定的字符串不匹配,match方法返回nil,因此我们的下一个任务是测试id和text是否非nil。一个有效的输入将设置id和text,因此我们可以使用小写版本的id作为HelloWorld_Text中的key,并将该字段的值设置为给定的文本。如果模式不匹配,则输入函数默认失败;但是很容易为无效输入添加错误消息:
第二个斜杠命令处理程序非常简单:它接受用户输入,将其转换为小写,并将其用作HelloWorld_Text中的KEY。如果有一些文本用作键(说白了就是有文本,文本存在),我们就调用SendChatMessage这个函数把之前存储的信息发出去。SendChatMessage 这个函数有好几个参数,如下:(msg,channel,language,target)所有参数中只有第一个参数是必须有的,也就是必须有msg即message,也就是你必须得有输入的信息才能继续执行这个函数。Channel就是要用的信息通道,可以设置为以下当中的任意一个:SAY(默认值),YELL,EMOTE,WHISPER,PARTY,RAID,BATTLEGROUND,RAID WARNING,GUILD或者OFFICER。(从字面上可以看出是游戏中人物说话的方式,比如悄悄话,大喊之类的)第三个参数languege是语言,比如人类语、兽人语、熊猫语之类的;nil(就是不进行设置)将使用你的种族的默认语言。最后,对目标说只需要用于channel=WHISPER
Persistence持续/存留
你存储的信息是不持久的,比如你重新登录一次,上次的信息就都没了,所以你得找办法保存下来,这个办法就是之前那个表格里的元数据属性之一:SavedVariables(由此可见之前列出的数据属性是很有用的,在以后的学习中可能会经常需要声明的),这个属性很重要,我们刚做的这个hello world本质上是插件,那么你保存在插件里的消息相当于你对插件的改动,比如我们用的单位框体设置,移动到哪里?设置多大?这都属于信息,所以这个属性的作用就是让你的这些设置和变动全都存下来。达到这个目的只需要在TOC文件中添加这一行:
更新完TOC文件需要重启游戏。
那么这里出现一个问题,这里的设置方式是把你的用户设置覆盖默认设置,这样你的用户设置就成了新的默认设置,这也是为什么每个角色都有单独的设置文件存档,问题在于你写了自己的插件,又保存了自己的设置,但是暴雪会对游戏更新,你的插件也需要跟进版本,可能上个版本的设置选项是三个,下个版本就出现第四个选项了,但是你的配置文件中并没有第四个选项,那系统就会默认读取nil(不存在),某个选项不存在,系统就会进一步重置设置,统一用默认数据,这样的话即使你在插件里更新了新内容,你的配置文件也没了,又要重新设置,那么什么办法能保存配置,同时在版本更替时让旧的设置继续生效呢?方法在后面的学习中会提到。
提取默认UI
这是教你怎么提取暴雪的默认UI编码文件,但是有以下问题
原著是很多年前的书,里面提到了两种方法,一种是用暴雪自己的工具Blizzard’s Interface AddOn Kit,然而这是早期版本为了让更多玩家了解魔兽运作而特意留下的工具箱,在新版本中已经被替代。所以即使登录外服的暴雪官网也找不到相关支持了。
而暴雪在游戏客户端留下了另一种查看代码和artwork(艺术作品,可以理解为美工)的途径,那就是在游戏内的开发人员控制台中输入以下内容:
虽然我也不知道控制台在哪,给出词条网址:
https://wowwiki.fandom.com/wiki/Extracting_interface_files
另一种是用mpq view,然而自从德拉诺版本后,暴雪在他的所有游戏中都淘汰了mpq格式文件,而是采用了CASC格式,所以想要查看内部文件需要CASC explorer,这个软件必须用新版本,旧版本不能读取新资料片。
在词条中,大致思路是:在战网客户端的游戏设置中添加控制台代码,以此启动控制台,然后用控制台启动游戏,在登录界面操作,输入代码,等待一段时间,得到文件。过程时间很长,且需要几个G的磁盘空间,电脑会有些卡。
==================================================================================================
======第四章 GAME EVENTS相关(残缺)======
关于事件处理程序,也就是函数,魔兽提供事件(event),我们根据事件写函数,告诉游戏应该有什么反应。这种函数也成为回调函数。
本章创建三个插件作为示例。
第一个是鼠标提示功能,对物品、任务、法术起作用。
第二个是计时库,这个插件让我们能在给定时间后执行指定的函数。
第三个是DKP拍卖功能,在第二个基础上,在副本中,对物品进行DKP拍卖。
为了建造这三个插件,我们得处理确切的事件,第一个事件出现在当你的鼠标徘徊在框体的某个链接上时;另一个事件出现在别人给你发了信息时。
框架如何联系到事件上。
用框架监视事件
我们这里用简单的框架来关联事件和相对应的处理函数。
对于框架有很多方法(method),这些方法可以对框架进行处理,比如设置了一个按钮的框架,就可以使用添加文本的“方法”设置框体,这样框体上就可以有文字显示。
这里我们使用几个常用的脚本处理函数:SetScript、GetScript、HookScript、HasScript。
事件本质上是脚本,为什么这么说?因为我们用脚本来表示发生的事件。不同框架类型有不同脚本。比如 OnClick就是一个脚本用来,表示发生了一件事:什么事呢?那就是按钮被按下了。这是按钮类型框架特有的事件。再比如,OnDragStart表示拖拽框架的事件。我们需要下面的方法:
该方法表示,当一个script即事件发生时,调用func即函数来处理。Script通常以“On”开头,比如OnClick就是点击这件事发生了,OnDragStart就是拖拽这件事发生了。
这个函数用来得到“和相应事件绑定的处理程序是什么?”,没有就返回nil
这个函数是在原有的事件处理函数基础上,再多一个附加函数,如果原有的处理函数被删除,那附加的也跟着没了。
如果事件对相应的框架有效,返回1;否则返回nil
上面这些就是events的处理方法,
附录A列出了可以用来做文章的事件。你会发现,几乎所有的事件都与界面UI的视觉效果有关。比如,OnShow用来表示显示,OnHide表示隐藏,OnDragStart和OnDragStop表示开始拖拽和停止拖拽的事件。
第一个插件我们暂时用不着创建新框架,而是使用已有的聊天框架。聊天框架的框架类型是ScrollingMessageFrame。它提供事件OnHyperlinkEnter和OnHyperlinkleave,这两个事件的意思是你把鼠标指针指上一个链接或者离开一个链接这两件事。链接是信息框架里可以点击的数据块。所以,像是物品、聊天通道、玩家名字都是明显的链接。
创建聊天链接工具提示
我们需要的第一个东西是存放在Interface\AddOns中的一个叫做ChatlinkTooltips的文件夹,第二步,创建一个插件总需要创建它的.toc文件,就像下面这样:
可以使用Notes属性添加额外的像是“描述”这种元数据。下一步是创建文件ChatlinkTooltips.lua
聊天框及其脚本处理程序
我们需要得到所有聊天框的参考。我们需要使用他们的脚本处理程序,因为我们需要当玩家将鼠标指针悬停在聊天中的链接上时运行脚本。我们可以将全局变量ChatFrameX用于此。X是我们想要的聊天框的号码,所以您的第一个聊天框是ChatFrame1,第二个是ChatFrame2,以此类推。默认聊天框(带有编辑框以发送消息的聊天框)也存储在全局变量DEFAULT_CHAT_FRAME。另一个有用的全局变量是NUM_CHAT_WINDOWS,这是您可以拥有的最大聊天框架数。默认情况下是7个,但是插件或未来的补丁可能会改变它。
对于这个插件,我们需要创建一个循环,循环遍历所有的聊天框架,并设置函数,这些函数显示或隐藏这个作为链接脚本处理程序的工具。在这个例子中,一个复杂问题是我们需要动态地访问每一个全局变量ChatFrameX,按顺序的(换句话说,我们需要在循环的第一次迭代中访问ChatFrame1,第二次迭代中访问ChatFrame2,以此类推)。魔兽世界提供了一个函数,它返回全局变量的值,具有给定名称:getglobal(name)。参数name在这里是一个字符串,因此我们可以在每次迭代期间动态构建名称。
下面的代码显示了这样一个循环和两个用作脚本处理程序的伪函数(showTooltip和hideTooltip)。这些函数只是将它们的参数输出到聊天框中,这样您就可以看到发生了什么。我还创建了一个helper函数,如果已经存在脚本处理程序,它将尝试挂接该脚本处理程序;否则,它将设置一个新的脚本处理程序。将此功能移到一个小函数中很有用,因为我们需要为两个脚本处理程序OnHyperLinkEnter和OnHyperLinkLeave执行此任务。代码如下:
让我们来看看循环的主体。第一行只是将对这个迭代处理的框架的引用复制到一个名为frame的局部变量中,所以我们不必一直编写getglobal(“ChatFrame”..i)。然后,代码检查框体是否真的存在;这个检查理应不会失败,但是您永远无法知道您的用户安装了哪些插件可能会破坏聊天框架。然后它调用助手函数setOrHookHandler,如果框架没有用于此脚本的脚本处理程序,它将设置我们的脚本处理程序。但是,它将挂接我们的脚本处理程序以保留任何已经存在的。请注意,默认用户界面不使用这两个脚本处理程序。此检查只是为了保持与可能已经具有类似功能的聊天插件的兼容性。
后面没翻译,抱歉,当时太心急,跳过去了。
====================================================================================================================================
======第五章 实验XML和框架======
XML设置界面UI
lua设置程序,好处是分开搞不易混乱。
将全面程序分成几小部分
第五章是制作一个德州扑克插件,可以在魔兽里玩,但是只制作了一小部分
然后,可以将服务器拆分为一个提供辅助功能的部分,例如比较双手以确定胜利者,以及一个与您桌上的客户机通信的部分。
客户端还可以分为多个部分,这些部分允许您从驻留在您的公会或raid中的表列表中选择表,以及显示当前正在使用的表的GUI。
我们将在本章中设计和创建图形用户界面的基础。但要做到这一点,我们首先需要讨论XML。
XML 基础
让我们从一个例子开始,这样您就可以看到魔兽世界中的XML文件是什么样子的。
下面的XML使用脚本处理程序onload和onevent创建一个框架。
如果要测试该示例,请将以下代码存储在名为firstUI.xml的文件中,并将该文件添加到TOC文件中。
向TOC文件添加XML文件的工作方式与向Lua文件添加插件类似,只需在TOC文件中添加一行firstUI.xml
在解释这个例子的作用之前,我需要介绍一些术语
XML术语
一个XML元素的形式是<元素名>内容</元素名>,有一个很短的办法来定义没有内容的元素:<元素名/>。
元素的内容可以是其他XML元素或文本,如Lua代码。
XML文档始终只有一个根元素,其中包含所有其他元素。
元素也可以具有如下属性:<元素名 属性=”值”>.
属性的值必须始终用引号括起来,即使它只是一个数字.
元素的开启标记是启动该元素的代码<ele attr1=”x” attr2=”y”>;结束标记</ele>标记元素的结束。XML中的注释以<!--开头,以-->结尾
魔兽世界XML中的元素(不同元素的不同作用)
魔兽世界的根元素总是元素<Ui>,您可以在示例的前三行中看到。
结束标记在最后一行。
它的属性也总是相同的,所以您可以将其复制并粘贴到您创建的每个新XML文件中。
这个元素的开始标记也可以只写一行,因为您可以在任何需要的地方插入新行和空格。
<Ui>元素的属性是xmlns、xmlns:xsi和xsi:schemaLocation。
他们的值包含元数据,这些元数据将此XML文件标识为一个魔兽世界的UI文件,并将其读取到程序中。
创造框架
Ui根元素包含一个框架类型的元素,这个元素具有属性name=”MyTestFrame”魔兽世界将在加载这个XML文件时创建一个框架。
框架名称可以通过调用方法frame:Getname()来搜索。
该名称还用作全局变量的标识符,该全局变量将用于存储对框架的引用。
所以您可以从Lua代码访问您的框架。您还可以从Lua创建命名框架;只需将名称作为第二个参数传递给CreatFrame(type,name)
此元素的内容是元素<Scripts>,它包含框架的所有脚本处理程序。所有可以用frame:SetScript设置的脚本处理程序也可以在这里使用。我们的示例框架具有脚本处理程序OnLoad和OnEvent。脚本处理程序OnLoad在魔兽世界完成加载框架后被调用。用SetScript(“OnLoad”,func)从lua中添加这个处理程序是无意义的,因为脚本处理程序将在调用CreatFrame(frame)之后被调用。如果您的框架是在没有OnLoad处理程序的情况下用XML创建的,那么在之后添加一个也是没有意义的。该处理程序在创建框架时正确地执行一次。稍后添加的OnLoad处理程序将永远不会执行。
脚本处理程序元素的内容是要存放在函数中的Lua代码。但是函数的头部在这里丢失了,那么我们如何知道哪些参数将被传递给它们呢?OnLoad的函数头是function(self),因为这个脚本处理程序不接收额外的参数。
OnEvent看起来像function(self,event,...)
附录A列出了所有XML脚本处理程序可用的参数。
包含的外部Lua和XML文件
XML文件还可以包含脚本处理程序和框架之外的Lua代码。您可以为此使用元素<Script>。它可以直接包含Lua代码,也可以使用file属性加载包含Lua脚本的文件。代码将在加载XML文件时执行。需要将<Script>元素放在所有<Frame>元素的外部,但要放在根元素<Ui>的内部:
元素<Include>允许包含另一个XML文件。该文件是通过使用该元素的file属性指定的。这个元素还必须放在所有其他元素之外,但必须放在根元素<Ui>的内部。
您不需要向TOC文件中添加通过<Script>/<Include>元素所包含的Lua或XML文件。这允许您在不使用TOC文件的情况下包含文件,如果您有必要添加许多属于一起的文件,并且希望将它们与其他文件分离,那么TOC文件非常有用。当您广泛使用所谓的嵌入式库时,常常会遇到这种情况,您将在第10章中更多地了解这个特性。
下面的XML显示了一个文件,它包括一个来自于子文件夹libs的Lua文件和一个来自于子文件夹libs中的XML文件,这些文件通常在创建Ace插件时使用。Ace也包含在第10章中。
如您所见,XML非常简单。可以用XML描述所有类型的框架。附录A提供了所有可用于所有框架类型的所有属性和元素的完整文档。
但是,如果XML文件中出现错误,会发生什么情况呢?如果没有在魔兽世界中测试,你怎么知道一个文件是否有效?
验证和调试XML
如果您使用的是更完整的ide之一,比如我在第1章中介绍的WoW AddOn Studio,那么您已经有了一个具有自动完成支持的语法检查器
但是,如果您使用SciTE,那么您只有对XML语法高亮显示和代码折叠的基本支持。
为了补充这一点,我在SciTE的发行版中包含了命令行实用程序MSV (Sun MultiSchema Validator)。
这一小节暂时略过,之后用到再看
框架基础
本节介绍基本框架以及如何显示在屏幕上。
框架可以有任意数量的子框架,但只能有一个或没有父框架。改变框架的属性也会影响到子框架。
我们将在这里创建扑克插件的第一部分:允许您浏览可用表列表并加入其中的一个表的框架。首先创建一个文件夹和一个TOC文件,然后向TOC文件添加文件localization.en.lua,TableBrowser.xml和TableBrowser.lua。TOC文件中文件的顺序定义加载顺序。如下所示。
我们要加载的第一个东西是本地化文件,我们将在其中放入我们将要使用的所有字符串(它们需要在加载其他任何东西之前可用)。我们希望加载的下一个文件是XML文件,因为我们希望在执行Lua代码之前创建并可用框架。
我把我的插件名字命名为Texas_Hold’em.用以下代码填充XML文件:
它定义了一个名称为Poker_TableBrowser的框架,我们将在本章的剩余部分对其内容进行扩展。
使它可见
我们需要一个在屏幕上可见的框架。我们需要提供至少一个锚点(也可以几个点)。如果只有一个点,还需要设置大小。最终我们需要框架里的内容这样我们才能看到一些东西。
UI父级(定义父子级)
这个框架由游戏定义,是所有其他框架的父级。框架不需要是UI父级的直接子级,这以便于通过修改单个框架来更改所有框架的某些属性。改变UI的大小调用UIParent::SetScale(scale)。
我们将parent=”UIParent”添加到XML文件中的<Frame>内:
可以通过调用frame:SetParent(parent)来从Lua更改框架的父框架,您还可以在从Lua创建框架时定义父框架,只需将父参数作为第三个参数传递给CreateFrame(type,name,parent)。
Anchors(锚点)确定位置用
锚定义了框架在游戏UI中的位置。一个框架可以有多个锚,我们从一个锚开始。<Anchor>元素必须放在<Frame>元素中的<Anchors>元素中。如下所示:
这段代码将框架的左侧锚定到其父框架的中心。Anchor point是进行设置的框架的哪个位置,relativePoint是要被绑定的游戏界面的位置。偏移量定义了父框架和子框架之间的空间。正x向右,负号向左。正y值——向上,负值向下。<AbsDimension>表示偏离距离负左正右。也可以使用<RelDimension>,它对x和y取0到1之间的值,为整个屏幕的百分比。偏移量可选填的,x和y的默认值是0。属性relativePoint也是可选的,默认情况下等于point。这表示着我们可以通过提供属性point快速地将框架锚定到其父框架的内部。我们的框架将被默认锚定在父框架。如果一个框架没有父框架,它将被锚定到整个屏幕。<Anchor>中的属性relativeTo=”frame”定义创建的框架将被锚定到的框架是哪个。
Points(点)
第一个图:显示了所有可用的点。第二个图:示意一个例子,其中子框架的点BOTTOMLEFT锚定在父框架的点BOTTOM上,X偏移量为15,Y偏移量为-10。
还可以用Lua代码设置、获取和删除锚。方法frame:SetPoint(point,relativeTo,relativePoint,x,y)创建一个新锚。除了point之外的所有参数都是可选的。relativeTo的默认值是框架的父值,relativePoint的默认值是作为第一个参数point传递的值,偏移量x和y默认为0。值relativeTo可以是一个框架,也可以是一个框架的名称。当您想要省略relativeTo和relativePoint时,可以将x和y作为第二个和第三个参数传递。
方法frame:GetPoint(n)返回框架的第n个点。返回值与SetPoint的参数相同。
您可以通过调用frame:ClearAllPoints()来删除所有点。调用此方法,屏幕上框架的位置不会改变,直到通过调用方法SetPoint设置新点。请注意,如果您想通过更改锚来移动框架,则必须始终调用ClearAllPoints。不清除这些点可能会产生意外结果,因为您最终会得到多个点。在本章后面,您将看到多个点如何影响框架的位置和大小。
在加载XML文件时仍然看不到任何效果,因为框架仍没有进行其他设置。但是我们可以用另一个框架,像默认UI的框架MinimapCluster,来测试方法SetPoint和ClearAllPoints的效果。以下代码可以在游戏中执行,以移动小地图与所有属于它的元素:
MinimapCluster:ClearAllPoints()
MinimapCluster:SetPoint("CENTER",100,-50)
现在可以测试设置不同点的效果。在设置新参数之前,不要忘记调用ClearAllPoints
框架大小
两种方法,绝对值(数)和相对值(比),相对值即屏幕比例,比如相对高度和想对宽度都0.5,最终占屏幕的四分之一。绝对值取决于UI的缩放设置。屏幕的高度总是768像素除以UI scale(缩放)设置,你可以用UIParent:GetScale()检索到.例如,我将UI scale设置为0.75,因此屏幕的高度(从插件的角度来看)是768/0.75=1024。宽度取决于屏幕的纵横比。例如,我有一个16:10的宽屏显示器,所以我的虚拟屏幕的宽度是1024*1.6 = 1638.4。这里是说需要自己计算后设置,比如你更改了UI比例缩放,但是没换屏幕,那么屏幕的分辨率是不变的,但是768/缩放得到的数变化了,这个数就是虚拟分辨率。
因此,当我们定义大小或偏移量时,总是使用虚拟的分辨率,然后由游戏映射到您的真实屏幕上。该系统的优点是,框架的位置和大小不依赖于分辨率。当你在窗口模式下玩游戏时,这一点尤为重要,因为它允许你调整窗口大小而无需重新定位帧。
<Size>设置框架的大小。尺寸384*350比较好,因为384是默认UI对话框(比如字符框)的宽度。将下面的代码添加到<Frame>中,开始和结束标记之间:
同样,也可以在这里使用<RelDimension>。如果您想从Lua中设置大小,可以使用方法frame:SetWidth(width)和frame:SetHeight(Height)。您可以通过调用GetWidth和GetHeight来检索大小。我们的框架如果有任何内容,将是可见的。
边框Backdrop
背景是边框和框架背景的组合。将下面的<Backdrop>元素添加到<Frame>:
让我们看一下边框的属性和元素。第一个属性bgFile,设定用作背景纹理质感的路径。tile是true,纹理将重复填充;tile为false纹理将拉伸填充。
第二个属性edgeFile用于框架边框的纹理。下图显示了我们示例中使用的纹理。图像包含我们框架的所有边框。
tilesize定义边框纹理的宽度。高度总是纹理文件的高度。???
edgesize定义用于边缘的纹理部分的大小。粗细
边框是您在图5-2的左半部分可以看到的四条直线,而边是右半部分的角。
backgroundinsets设置背景将被绘制多远。就是阴影面积的四个边分别距离对应边框的距离。如果edgefile的边框较小,则使用较低的值
对于大多数插件来说,从暴雪的UI中复制和粘贴一个现有的背景元素就足够了。这可以确保你的插件具有原生外观,你不必设计自己的背景。
但是我还是要自己搞一个。
如果不想在默认情况下显示该框架,创建一个斜杠命令和一个用于稍后显示该内容的小地图按钮。
然后,您可以通过调用Poker_TableBrowser:Show()来显示它,在这个框架工作时,你可能想要保持隐藏状态在它的默认值false。??
如何设置多个点来限制框架。暂且略过P103
层和纹理,即 框架里的背景设定。
层(strata)就是前后顺序,和PS中的层是一个意思。框架层从高到低如下:
TOOLTIP
FULLSCREEN_DIALOG
FULLSCREEN
DIALOG
HIGH
MEDIUM
LOW
BACKGROUND
这个序列的翻译从高到低是:提示框>对话框全屏>全屏>对话框>加亮区?>中等的?>低等的?>背景
DIALOG,我们使用的都是这个,默认为对话框使用,覆盖在所有非全屏的窗口前面,可以通过调用frame:SetFrameStrata(strata)来设置框架的层。
层(layers)
保存纹理或文本,这个层包含在框架中。定义了美工贴图的层次。
下面的例子使用图层和纹理向我们的框架添加标题区域。一个例子,需要添加到frame里:
<layers>保存所有<layer>,<layer>用来保存纹理<Texture>和文本块<FontString>,<layer>的唯一属性是level,下面是所有属性有效值从高到低:
HIGHLIGHT
OVERLAY
ARTWORK(default value)
BORDER
BACKGROUND
HIGHLIGHT级别特殊,因为放在这个层中的元素只有将鼠标移到上面时才可见。所以可用来设置奇特的辉光效果。示例中的层包含两个元素:一个纹理和一个文本。
纹理textures
层中的纹理与框架类似,需要锚点和大小。纹理名称为$parentTitle,其中$parent被父级名称替代。它的引用在具有此名称的全局变量下可用。file设置将要显示的纹理。要了解纹理可以拥有的所有元素和属性,参考附录a。
字体字符串
字体字符串需要名字。因为纹理的名称$parentTitle用作本字体字符串锚点中的relativeTo属性的值。
这让我们将标题上的文本正确地锚定到它的背景上,不可能通过XML将一个框架锚定到一个无名的框架上。您能通过Lua的方法SetPoint,将对该被绑定框架的引用作为第二个属性传递,将其用作锚。
属性inherits告诉游戏使用字体项目font objectGameFontNormal(就是正常字体的意思)。一个字体项目定义大小,颜色,字体。可以在FrameXML\Fonts.xml文件里找到所有默认UI的可用的字体项目。附录A包含有关字体用的所有文件、元素。
字体大小不是必须设置,但是可以设置上限。超出了宽度,将被包围。超过了高度,就会被截断。
我们可以通过在文件localization.en.lua中添加以下行来更改标题。
可移动框架
通过向<Frame>添加属性movable=”true”使框体可移动。这句话要加到和框架名称一个引号里,似乎对于插件基础设定的识别只认一个三角引号
通过添加属性enableMouse=”true”使框架能感应到鼠标点击。(事件属性,大概和上一章有关)
脚本处理程序OnDragStart和OnDragEnd用来检测你什么时候尝试拖动框架。方法frame:RegisterForDrag(button)用来设定哪个鼠标键可以拖动框架。LeftButton,MiddleButton,RightButton,MouseButton4,MouseButton5分别代表鼠标左中右和两个侧键。
OnDragStart:使用frame:StartMoving()在拖动时将框架和鼠标指针绑定。
OnDragEnd:释放时调用frame:StopMovingOrSizing()
示例如下(添加到框架里):
其中的self是指自己这个框架。
在基础设定的引号里添加clampedToScreen="true"这句话可以让框架移动时不会超出屏幕。
self:SetUserPlaced(1)--保存位置用
继承
继承是设定一个虚拟框架,包括一些属性和元素,当需要创建新框架时直接从虚拟框架复制过来,免去很多公共设定重复操作。
属性virtual=”true” , inherits=”templateName”用来告诉游戏你想继承一个叫什么名字的虚拟框架。在用Lua文件创建时,把template作为第四个参数设给
虚拟框架只能在xml中设定,无法在Lua文件中设定。
框架类型按钮
按钮是本质也是框架。它们提供脚本处理程序OnClick,它在单击按钮时调用。按钮还具有纹理、显示在其上的文本块和定义文本字体样式的字体对象。纹理和字体样式可以随着按钮的状态而改变。有四种状态:1.有一个纹理和字体的默认组合,在正常状态下显示。2.当您将光标放在按钮上时,会出现一个组合。3、当您单击按钮时,会出现另一个组合。4.当禁用按钮时,将使用纹理和字体的第四个组合。
无法单击禁用的按钮。
XML中的按钮
按钮具有框架具有的所有元素和属性。因此,设置按钮的位置和大小也适用于锚点和<Size>也就是大小元素。同样,请参阅附录A以获得对按钮所能做的一切的概述。
下面的XML定义了一个标准的按钮:
这里缺少大小和锚点,因此如果您想使用此按钮,需要添加它们。按钮的文本用元素<ButtonText>定义,在本例中,要放在按钮的中间。元素<NormalFont>,<HighlightFont>和<DisabledFont>设置字体用于不同的状况。按下的按钮总是使用<HighlightFont>,因为您必须将鼠标放在按钮上才能按下它。四个<XTexture>元素定义了用于按钮不同状态的纹理。
有趣的是纹理使用继承属性。此属性的值是一个模板,所有其他属性和元素都从该模板中复制。所以我们必须看看这些模板。下面的代码显示了模板UIPanelButtonUpTexture,它可以在默认UI的文件Interface/FrameXML/UIPanelTemplates.xml中找到.
使用按钮模板
如果想更改外观,设置一个按钮模版,以此避免重复设置。
你可以把属性virtual=”true”定义到按钮中,每次使用按钮,就使用inherits=”Poker_Button”,但是这里更倾向使用暴雪定义好的模版,这样暴雪对游戏有改动时,模版会自动更改。
在创建自己的模板之前,您应该始终查看Blizzard的XML文件。所有名称中包含单词Template的XML文件都是模板的潜在资源,您的插件可以使用这些模板。
这个模板的名字是UIPanelButtonTemplate,它也在文件Interface/FrameXML/UIPanelTemplates.xml中定义。在创建自己的模板之前,一定要查看该文件,因为它已经包含了一些好的模板。然后可以使用下面的代码从这个模板创建一个按钮。按钮应该是一个框架Poker_TableBrowser的子框架。一个框架的所有子框架必须放在框架的元素<Frames>中,所以添加以下代码到您的<Frames>元素来创建按钮作为子元素:
首先说明,这个教材很老旧,大概是巫妖王版本的,有好多API变动很大,而且后来暴雪在游戏中的艺术素材换文件格式了,所以有的章节会出现一些已经没有的东西,已经翻译的部分我尽可能查证后做了标注,理解一下,这本书网上没卖的,找到了电子版,分享给各位爱好的朋友。
链接:https://pan.baidu.com/s/1KoDaypF6YzDqzPsjCuvzSQ
提取码:tyvt
这是查看魔兽自带美术素材的一些工具,魔兽自带代码和美术素材的提取与查看方法在我的另一篇教程里有。
链接:https://pan.baidu.com/s/1RNmlmablHpCMfQAKA4Xqig
提取码:69bf
第一章是准备工具,大多不能用了,第二章是lua基础,网上有很多教程,我是看的一个老视频系列,叫《医生不吭》,所以也没翻译。
======第三章 使用API创建MOD======
/hwadd<id><text>,/hwshow<id>前者将文本添加到将要保存的表中,而后者将保存在id下的文本发送到聊天通道。Gold farmers可能会认为,这个插件唯一可能的用途是利用广告向交易聊天发送垃圾信息,因为它允许您使用一个宏发送大量文本。这个mod的另一个很好的用途可能是保存长boss或战场命令,例如,在一个宏中。你可以很容易地把信息发送到突袭或战场聊天。
让我们从在游戏中运行Lua代码开始
Running a “Hello, World” in WoW
要运行我们将要创建的脚本,只需在魔兽世界中输入以下斜杠命令:
/script print(“hello world”)命令/script<text>将<text>编译为一个组块并运行它
组块是具有自己作用域的Lua代码块。因此,如果您在/script命令中定义了一个局部变量,那么在下一个/script命令中它将不可见。
您已经知道print函数,但是这里它与标准Lua有一个小小的区别:魔兽世界将输出写到默认聊天框,而不是您的标准输出。这将在默认聊天框中显示“Hello, World”。这个例子只是为了说明魔兽世界中的Lua和普通Lua的工作原理是一样的。现在我们可以开始创建一个Lua脚本,它被游戏加载为一个插件.
Our First WoW Addon
让我们创建我在本章开头描述的插件。在开始创建mod之前,我们需要决定的第一件事是将Lua文件放在哪里。
Folder Structure
在这个文件夹中创建文件夹helloworld'。这是我们将为“Hello, World”mod创建所有文件的地方。addons文件夹有许多不同的文件扩展名。插件可以使用哪些类型的文件?
魔兽世界试图加载与文件夹名称相同的TOC文件
##Interface:30100——适用的魔兽版本号##Title:hello,world!——插件名称##Title-deDE:hallo,welt!——德语版本(省去)##Notes:the best “hello world”addon!——注释,可在游戏内看到,把鼠标放上去Helloworld.lua——要执行的Lua文件在这个TOC文件中有两种类型的命令:以包含元数据开始的行。所有其他行都是将被执行以加载插件的文件。
我们为插件定义以下元数据
有许多元数据属性被魔兽世界识别;表3 1表中的各项是在TOC中用双井号添加
可用的元数据属性
| Interface/接口 | 适用魔兽版本 |
| 名称title | 名称 |
| 注释notes | 中文注释 |
| 依赖插件RequiredDeps | 此附加程序所需的以逗号分隔的插件列表。加载所有依赖项后,加载附加项 如果它的某个依赖项丢失或禁用,它将不加载。 |
| 可选倚赖板块OptionalDeps | 一个逗号分隔的插件列表,如果这些插件已安装并启用,那么它们将在插件之前加载。 |
| 延迟加载LoadOnDemand | 0(默认)或1。登录时将不加载需要加载的插件。您可以通过在另一个插件中调用LoadAddOn(addon)加载该插件。 |
| 陪同加载LoadWith | 需要LoadOnDemand。以逗号分隔的插件列表。 如果加载其中一个插件,将加载该插件. |
| 主办行LoadManagers | 以逗号分隔的插件列表。如果一个给定的插件是启用的,这将设置LoadOnDemand为1 |
| 存储状态变量SavedVariables | 一个以逗号分隔的全局变量列表,当退出游戏的时候,它将保存到\wtf\account\<account name>\savedvariables\<addon>.lua |
| 状态变量填充字符 SavedVariablesPerCharacter | 这与SavedVariables基本相同,但是它会根据每个字符的基础原则将所有内容保存到\wtf\account\<account name>\<sever>\<character>\savedvariables\<addon>.lua |
| 默许状况 DefaultState | 要么enabled(可行的)要么disabled(不可行的)。确定默认情况下是否启用或禁用该插件 |
| 安全的 Secure | 1或者0。有暴雪数字签名的安全插件。目前还没有办法获得这样的签名,所以这个属性只能被暴雪的插件使用。 |
双井号(##)后还可以通过使用以x-开头的特性来使用属于自己的元数据。API函数GetAddOnMetadata(addon,attribute)可用于获取元数据。注意,这个函数也适用于尚未加载的插件。所以TOC文件是决定什么时候加载“需要被加载的插件”的文件。检索元数据时不需要附加地区;这是自动完成的。
让我们看一个例子。致命Boss模块的例子“永恒之眼”是一个TOC文件,它广泛使用定制元数据。
##Interface:30100##Title:<DBM>Eye of Eternity##Title-deDE:<DBM> Auge der Ewigkeit##LoadOnDemand:1##RequiredDeps:DBM-Core##SavedVariables:DBMEyeofEternity_SavedVars, DBMEyeofEternity_SvedStats##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 Ewigkeitlocalization.en.lualocalization.de.luaMalygos.lua除德语属性外,我删除了所有本地化属性;实际文件比较长,因为每个本地化都需要额外的4行。
该文件定义DBM需要的所有元数据,以便在不加载它们的情况下构建可用的mod列表。最重要的属性是X-DBM-Mod-LoadZone,它指示DBM在您进入指定区域时加载插件。这是DBM按需加载工作原理的关键部分。元数据的真正强大之处在于它可以在加载mod之前使用。我们已经创建了一个TOC文件,但是现在需要添加Lua代码来做一些很酷的事情。
Using the WoW API
您已经看到了一些API函数:print,PlaySoundFile,LoadAddOn,和GetAddOnMetadata。所有API函数都可以在全局中使用,因此它们在任何地方都是可用的,:在Lua文件中、在XML文件中(它们可以嵌入Lua代码,我们将在第5章中详细介绍),以及在命令行脚本中。这些函数有自解释的名称和参数。您可以在WoWWiki页面上找到一个完整的特定于wow的函数列表www.wowwiki.com/API。
1.有几种不同类型的函数。最常见的是“真正的”API函数,之所以这么叫是因为它们是用C编写的,并且可以通过Lua的C API获得。这样一个函数的例子是playsoundfile()。默认UI中没有定义这个函数的Lua文件。相反,它是由底层接口API直接提供的,在大多数情况下,不可能使用Lua实现这种类型的函数
2.您还可以在默认UI的Lua文件中找到很多函数;这些函数大多以前缀UI开始。第二类函数的一个例子是UIFrameFadeOut(frame,time),这个插件会随着时间的推移通过减少alpha值(框架的不透明度)淡出框架。
记住你总是可以使用/script <lua code>在游戏中执行Lua代码。用它来处理一些API函数,看看它们是如何工作的。例如,您可以用以下参数调用UIFrameFadeOut来测试UIFrameFadeOut:
/script UIFrameFadeOut(Minimap,1)这将使你的小地图在1秒后消失。但是你怎么才能找回它呢?
这就引出了下一种类型的API函数:接口API的面向对象部分。所有的框架都是对象;框架可以通过调用CreateFrame(frameType)来被创建或者使用XML文件。您将在第5章中看到如何创建这些框架和可用类型。框架基本上就是一个包含userdate值和许多方法的表。回想一下,您必须使用冒号操作符来调用方法,而这些方法基本上就是存储在表中的函数。这里需要记住的是框架包含许多方法。
3.第三种类型的API函数是一个真正的API函数,它存储在一个框架中,并希望使用冒号操作符调用它。让我们将这种类型称为小部件或框架函数,因为它们与图形用户界面元素密切相关。
现在我们只看面向对象API的基础。在接下来的两章中,您将更多地了解框架和它们背后的面向对象模型。让我们来看一种适用于所有帧的方法:SetAlpha(value)。它设置帧的透明度(一个介于0和1之间的数字),它的所有方法都是通过冒号操作符调用的。试着用这个来找回你的小地图:
/script Minimap:SetAlpha(1)GetAlpha()方法也可用;它返回当前的alpha值。您可以在附录a中找到所有可用方法和框架类型的完整列表.
但是回到我们的Hello World mod,我们有一个插件,它拥有魔兽世界需要的元数据,但是它只是一个TOC文件。现在,我们需要将Lua代码添加到文件中,以便处理这些API函数。我们的TOC文件中已经有了helloworld.lua行。当您无论何时试图加载HelloWorld插件时,魔兽世界会尝试在HelloWorld文件夹中找到此lua文件,因此让我们创建此文件。
当您登录时,游戏将在该文件中执行Lua代码。因此,在该lua文件中写入print(“hello,world”)将在你的聊天框显示“Hello, World!”。但这对我们没用;我们需要一些互动的东西。让我们创建一些斜杠命令。
小技巧:/console reloadui来重新加载以前加载的所有文件
Creating Slash(斜杠)& Commands创造斜杠命令
创建自定义斜杠命令很容易。SlashCmdList是全局的一个表,含有要用的函数,每个函数有自己的斜杠命令,由斜杠命令调用,斜杠命令后可以带有要处理的数值。说白了斜杠命令就像表中的一个函数的名称,名称后打上要处理的参数。
这里我们创建俩,一个用来储存字符串,另一个把储存你的字符串发出去
Handler说白了就是表中的函数的意思
创建斜杠命令有两个步骤。第一,为函数想一个名称,该名称就是列表中的你要用的函数的名称,这个名称在下一步和一个专用的key进行绑定,这样没用到KEY就是在调用函数。从这里看出来函数就是具体的操作或算法,函数叫什么要你手动去设置,系统不会自动生成(除了LUA语言和暴雪API自带的)。我们给第一个要用的函数起名字叫HELLO_WORLD_ADD(所以说这种大写加下划线的写法是handler函数的名称的写法),和这个函数绑定的KEY是/hwadd。
第二步是将KEY代表的函数存储在表SlashCmdList中。这个函数用来处理变量,是执行部分。请记住,我们想要使用的斜杠命令是/hwadd <id> <text>和/hwshow<id>,顾名思义,从命名的字面可以看出第一个KEY用来存储信息,第二个KEY用来发送信息。这意味着我们需要两个斜杠命令,当然各自绑定一个函数。
要创建我在本章开头讨论过的mod,我们需要两个斜杠命令的效果是:一个用于存储消息,另一个用于检索消息并将其发送到“chat”通道,这里的chat通道就是游戏里面你按回车键,左下角白色的聊天框,你打完字发出去,你的角色头上就会跳出来一个气泡给别的玩家显示你说的话,这个最直白的聊天通道就是chat,还有其他的聊天通道,有不同的对应的英文名称,比如你和好友私聊,用的通道就是“whisper(好像是这个单词来着)”,比如一些打团插件会在副本队伍里报告打断技能“某某人打断了BOSS技能”,发出来的文本是橘黄色的,这就是在插件里设置了喊话功能,自动把你的行为在“副本队伍”通道里喊出来了。
如果您还没有创建文件HelloWorld.lua,那么现在就创建它。然后添加以下代码:
HelloWorld_Text = {}local channel="SAY"SLASH_HELLO_WORLD_ADD1="/hwadd"SLASH_HELLO_WORLD_ADD2="/helloworldadd"-- an alias for/hwaddSlashCmdList["HELLO_WORLD_ADD"]=function(msg)  local id,text=msg:match("(%S+)%s+(.+)")  if id and text then    HelloWorldText[id:lower()]=text  endendSLASH_HELLO_WORLD_SHOW1="/hwshow"SLASH_HELLO_WORLD_SHOW2="/helloworldshow"--an alias for/hwshowSlashCmdList["HELLO_WORLD_SHOW"]=function(msg)  local text=HelloWorld_Text[msg:lower()]  if text then    SendChatMessage(text,channel)  endend书中的上列代码中有错:ADD中的存储函数少了一个下划线
让我们逐行看一下这个文件。首先,我们初始化(也就是设置)两个变量SLASH_HELLO_WORLD_ADD1和SLASH_HELLO_WORLD_ADD2,其实这里用一个就行,写第二个是为了保险一点,多留一个KEY,但是两个KEY的功能是一样的。在这里的SlashCmdList就是魔兽API的内容,它的作用是把你起的名字和函数功能绑定起来,可以看出来这里的方括号里面就是函数名字,用的是大写加下划线,后面的function就是函数/功能的意思,是LUA语言设置函数内容的固定形式,后面带个圆括号,圆括号里是你要处理的消息。这里学过LUA的很容易看懂,下面几行就是给这个名称(HELLO_WORLD_SHOW)的函数写内容啦,全局变量HelloWorld_Text将创造一个表,其中包含用户通过KEY想要保存的消息。本地变量channel(通道)将包含我们想要使用的聊天通道。然后,我们用id(也就是函数名称的意思)HELLO_WORLD_ADD创建第一个斜杠命令。每次我们把msg(也就是message的简写,消息)输入到/hwadd <text>或/helloworldadd_<text>的text处,就会调用SlashCmdList中的HELLO_WORLD_ADD的函数来处理消息。
HELLO_WORLD_ADD处理程序的第一行解析用户输入。这是通过使用正则表达式regular expressions(正则表达式就是捕捉符合要求的字符,所以应该是match函数???)实现的;如果你还没有读过第二章关于他们的部分,现在就去读。
首先,%S是%s的补码。%s表示所有空格字符,%S表示所有非空格字符。“+”的意思是“至少有一个”。表达式周围的括号将其定义为捕捉,因此符合捕捉要求而被匹配出来的字符串将是match方法的第一个返回值。这个被筛选出来的信息是以第一个非空白字符为开通,第一个空白为结尾的。因为id是根据函数从msg中筛选出来的,所以id是msg的子字符串。第二个表达式(即%s)表示两个参数之间的分隔空间,第三个表达式(即.+)(也是第二个捕获)是字符串的其余部分。
这种模式可以适用于所有的用户输入像” foo bar”,如果模式与给定的字符串不匹配,match方法返回nil,因此我们的下一个任务是测试id和text是否非nil。一个有效的输入将设置id和text,因此我们可以使用小写版本的id作为HelloWorld_Text中的key,并将该字段的值设置为给定的文本。如果模式不匹配,则输入函数默认失败;但是很容易为无效输入添加错误消息:
if id and text then HelloWorld_Text[id:lower()] = textelse print("Usage:/hwadd <id><text>")end第二个斜杠命令处理程序非常简单:它接受用户输入,将其转换为小写,并将其用作HelloWorld_Text中的KEY。如果有一些文本用作键(说白了就是有文本,文本存在),我们就调用SendChatMessage这个函数把之前存储的信息发出去。SendChatMessage 这个函数有好几个参数,如下:(msg,channel,language,target)所有参数中只有第一个参数是必须有的,也就是必须有msg即message,也就是你必须得有输入的信息才能继续执行这个函数。Channel就是要用的信息通道,可以设置为以下当中的任意一个:SAY(默认值),YELL,EMOTE,WHISPER,PARTY,RAID,BATTLEGROUND,RAID WARNING,GUILD或者OFFICER。(从字面上可以看出是游戏中人物说话的方式,比如悄悄话,大喊之类的)第三个参数languege是语言,比如人类语、兽人语、熊猫语之类的;nil(就是不进行设置)将使用你的种族的默认语言。最后,对目标说只需要用于channel=WHISPER
Persistence持续/存留
你存储的信息是不持久的,比如你重新登录一次,上次的信息就都没了,所以你得找办法保存下来,这个办法就是之前那个表格里的元数据属性之一:SavedVariables(由此可见之前列出的数据属性是很有用的,在以后的学习中可能会经常需要声明的),这个属性很重要,我们刚做的这个hello world本质上是插件,那么你保存在插件里的消息相当于你对插件的改动,比如我们用的单位框体设置,移动到哪里?设置多大?这都属于信息,所以这个属性的作用就是让你的这些设置和变动全都存下来。达到这个目的只需要在TOC文件中添加这一行:
## SavedVariables:HelloWorld_Text更新完TOC文件需要重启游戏。
那么这里出现一个问题,这里的设置方式是把你的用户设置覆盖默认设置,这样你的用户设置就成了新的默认设置,这也是为什么每个角色都有单独的设置文件存档,问题在于你写了自己的插件,又保存了自己的设置,但是暴雪会对游戏更新,你的插件也需要跟进版本,可能上个版本的设置选项是三个,下个版本就出现第四个选项了,但是你的配置文件中并没有第四个选项,那系统就会默认读取nil(不存在),某个选项不存在,系统就会进一步重置设置,统一用默认数据,这样的话即使你在插件里更新了新内容,你的配置文件也没了,又要重新设置,那么什么办法能保存配置,同时在版本更替时让旧的设置继续生效呢?方法在后面的学习中会提到。
提取默认UI
这是教你怎么提取暴雪的默认UI编码文件,但是有以下问题
原著是很多年前的书,里面提到了两种方法,一种是用暴雪自己的工具Blizzard’s Interface AddOn Kit,然而这是早期版本为了让更多玩家了解魔兽运作而特意留下的工具箱,在新版本中已经被替代。所以即使登录外服的暴雪官网也找不到相关支持了。
而暴雪在游戏客户端留下了另一种查看代码和artwork(艺术作品,可以理解为美工)的途径,那就是在游戏内的开发人员控制台中输入以下内容:
exportInterfaceFiles codeexportInterfaceFiles art虽然我也不知道控制台在哪,给出词条网址:
https://wowwiki.fandom.com/wiki/Extracting_interface_files
另一种是用mpq view,然而自从德拉诺版本后,暴雪在他的所有游戏中都淘汰了mpq格式文件,而是采用了CASC格式,所以想要查看内部文件需要CASC explorer,这个软件必须用新版本,旧版本不能读取新资料片。
在词条中,大致思路是:在战网客户端的游戏设置中添加控制台代码,以此启动控制台,然后用控制台启动游戏,在登录界面操作,输入代码,等待一段时间,得到文件。过程时间很长,且需要几个G的磁盘空间,电脑会有些卡。
==================================================================================================
======第四章 GAME EVENTS相关(残缺)======
关于事件处理程序,也就是函数,魔兽提供事件(event),我们根据事件写函数,告诉游戏应该有什么反应。这种函数也成为回调函数。
本章创建三个插件作为示例。
第一个是鼠标提示功能,对物品、任务、法术起作用。
第二个是计时库,这个插件让我们能在给定时间后执行指定的函数。
第三个是DKP拍卖功能,在第二个基础上,在副本中,对物品进行DKP拍卖。
为了建造这三个插件,我们得处理确切的事件,第一个事件出现在当你的鼠标徘徊在框体的某个链接上时;另一个事件出现在别人给你发了信息时。
框架如何联系到事件上。
用框架监视事件
我们这里用简单的框架来关联事件和相对应的处理函数。
Local myFrame=CreateFrame(“Frame”)对于框架有很多方法(method),这些方法可以对框架进行处理,比如设置了一个按钮的框架,就可以使用添加文本的“方法”设置框体,这样框体上就可以有文字显示。
这里我们使用几个常用的脚本处理函数:SetScript、GetScript、HookScript、HasScript。
事件本质上是脚本,为什么这么说?因为我们用脚本来表示发生的事件。不同框架类型有不同脚本。比如 OnClick就是一个脚本用来,表示发生了一件事:什么事呢?那就是按钮被按下了。这是按钮类型框架特有的事件。再比如,OnDragStart表示拖拽框架的事件。我们需要下面的方法:
Frame:SetScript(script,func)该方法表示,当一个script即事件发生时,调用func即函数来处理。Script通常以“On”开头,比如OnClick就是点击这件事发生了,OnDragStart就是拖拽这件事发生了。
Frame:GetScript(script)这个函数用来得到“和相应事件绑定的处理程序是什么?”,没有就返回nil
Frame:HookScript(script,func)这个函数是在原有的事件处理函数基础上,再多一个附加函数,如果原有的处理函数被删除,那附加的也跟着没了。
Frame:HasScript(script)如果事件对相应的框架有效,返回1;否则返回nil
上面这些就是events的处理方法,
附录A列出了可以用来做文章的事件。你会发现,几乎所有的事件都与界面UI的视觉效果有关。比如,OnShow用来表示显示,OnHide表示隐藏,OnDragStart和OnDragStop表示开始拖拽和停止拖拽的事件。
第一个插件我们暂时用不着创建新框架,而是使用已有的聊天框架。聊天框架的框架类型是ScrollingMessageFrame。它提供事件OnHyperlinkEnter和OnHyperlinkleave,这两个事件的意思是你把鼠标指针指上一个链接或者离开一个链接这两件事。链接是信息框架里可以点击的数据块。所以,像是物品、聊天通道、玩家名字都是明显的链接。
创建聊天链接工具提示
我们需要的第一个东西是存放在Interface\AddOns中的一个叫做ChatlinkTooltips的文件夹,第二步,创建一个插件总需要创建它的.toc文件,就像下面这样:
## Interface:30100## Title:Chatlink TooltipsChatlinkTooltips.lua可以使用Notes属性添加额外的像是“描述”这种元数据。下一步是创建文件ChatlinkTooltips.lua
聊天框及其脚本处理程序
我们需要得到所有聊天框的参考。我们需要使用他们的脚本处理程序,因为我们需要当玩家将鼠标指针悬停在聊天中的链接上时运行脚本。我们可以将全局变量ChatFrameX用于此。X是我们想要的聊天框的号码,所以您的第一个聊天框是ChatFrame1,第二个是ChatFrame2,以此类推。默认聊天框(带有编辑框以发送消息的聊天框)也存储在全局变量DEFAULT_CHAT_FRAME。另一个有用的全局变量是NUM_CHAT_WINDOWS,这是您可以拥有的最大聊天框架数。默认情况下是7个,但是插件或未来的补丁可能会改变它。
对于这个插件,我们需要创建一个循环,循环遍历所有的聊天框架,并设置函数,这些函数显示或隐藏这个作为链接脚本处理程序的工具。在这个例子中,一个复杂问题是我们需要动态地访问每一个全局变量ChatFrameX,按顺序的(换句话说,我们需要在循环的第一次迭代中访问ChatFrame1,第二次迭代中访问ChatFrame2,以此类推)。魔兽世界提供了一个函数,它返回全局变量的值,具有给定名称:getglobal(name)。参数name在这里是一个字符串,因此我们可以在每次迭代期间动态构建名称。
下面的代码显示了这样一个循环和两个用作脚本处理程序的伪函数(showTooltip和hideTooltip)。这些函数只是将它们的参数输出到聊天框中,这样您就可以看到发生了什么。我还创建了一个helper函数,如果已经存在脚本处理程序,它将尝试挂接该脚本处理程序;否则,它将设置一个新的脚本处理程序。将此功能移到一个小函数中很有用,因为我们需要为两个脚本处理程序OnHyperLinkEnter和OnHyperLinkLeave执行此任务。代码如下:
1ocal function showTooltip(...)print(...)endlocal function hideTooltip(...)print(...)endlocal function setOrHookHandler(frame, script, func)if frame:GetScript(script) then -- check if it already has a script handler...frame:HookScript(script, func) -- ...and hook itelseframe:SetScript(script, func) set our function as script handler otherwiseendend .for i = 1,NUM_CHAT_WINDOWS_do .local frame = getglobal("ChatFrame"..i) -- copy a referenceif frame then -- make sure that the frame existssetOrHookHandler(frame,"OnHyperLinkEnter", showTooltip)setOrHookHandler(frame,"OnHyperLinkLeave", hideTooltip)endend让我们来看看循环的主体。第一行只是将对这个迭代处理的框架的引用复制到一个名为frame的局部变量中,所以我们不必一直编写getglobal(“ChatFrame”..i)。然后,代码检查框体是否真的存在;这个检查理应不会失败,但是您永远无法知道您的用户安装了哪些插件可能会破坏聊天框架。然后它调用助手函数setOrHookHandler,如果框架没有用于此脚本的脚本处理程序,它将设置我们的脚本处理程序。但是,它将挂接我们的脚本处理程序以保留任何已经存在的。请注意,默认用户界面不使用这两个脚本处理程序。此检查只是为了保持与可能已经具有类似功能的聊天插件的兼容性。
后面没翻译,抱歉,当时太心急,跳过去了。
====================================================================================================================================
======第五章 实验XML和框架======
XML设置界面UI
lua设置程序,好处是分开搞不易混乱。
将全面程序分成几小部分
第五章是制作一个德州扑克插件,可以在魔兽里玩,但是只制作了一小部分
然后,可以将服务器拆分为一个提供辅助功能的部分,例如比较双手以确定胜利者,以及一个与您桌上的客户机通信的部分。
客户端还可以分为多个部分,这些部分允许您从驻留在您的公会或raid中的表列表中选择表,以及显示当前正在使用的表的GUI。
我们将在本章中设计和创建图形用户界面的基础。但要做到这一点,我们首先需要讨论XML。
XML 基础
让我们从一个例子开始,这样您就可以看到魔兽世界中的XML文件是什么样子的。
下面的XML使用脚本处理程序onload和onevent创建一个框架。
如果要测试该示例,请将以下代码存储在名为firstUI.xml的文件中,并将该文件添加到TOC文件中。
向TOC文件添加XML文件的工作方式与向Lua文件添加插件类似,只需在TOC文件中添加一行firstUI.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"><Frame name="MyTestFrame"> <Scripts><!--this is a comment -->  <OnLoad>   self:RegisterEvent("CHAT_MSG_WHISPER")  </OnLoad>  <OnEvent>   local msg, sender =... print(event,sender,msg)  </OnEvent> </Scripts> </Frame></Ui>在解释这个例子的作用之前,我需要介绍一些术语
XML术语
一个XML元素的形式是<元素名>内容</元素名>,有一个很短的办法来定义没有内容的元素:<元素名/>。
元素的内容可以是其他XML元素或文本,如Lua代码。
XML文档始终只有一个根元素,其中包含所有其他元素。
元素也可以具有如下属性:<元素名 属性=”值”>.
属性的值必须始终用引号括起来,即使它只是一个数字.
元素的开启标记是启动该元素的代码<ele attr1=”x” attr2=”y”>;结束标记</ele>标记元素的结束。XML中的注释以<!--开头,以-->结尾
魔兽世界XML中的元素(不同元素的不同作用)
魔兽世界的根元素总是元素<Ui>,您可以在示例的前三行中看到。
结束标记在最后一行。
它的属性也总是相同的,所以您可以将其复制并粘贴到您创建的每个新XML文件中。
这个元素的开始标记也可以只写一行,因为您可以在任何需要的地方插入新行和空格。
<Ui>元素的属性是xmlns、xmlns:xsi和xsi:schemaLocation。
他们的值包含元数据,这些元数据将此XML文件标识为一个魔兽世界的UI文件,并将其读取到程序中。
创造框架
Ui根元素包含一个框架类型的元素,这个元素具有属性name=”MyTestFrame”魔兽世界将在加载这个XML文件时创建一个框架。
框架名称可以通过调用方法frame:Getname()来搜索。
该名称还用作全局变量的标识符,该全局变量将用于存储对框架的引用。
所以您可以从Lua代码访问您的框架。您还可以从Lua创建命名框架;只需将名称作为第二个参数传递给CreatFrame(type,name)
此元素的内容是元素<Scripts>,它包含框架的所有脚本处理程序。所有可以用frame:SetScript设置的脚本处理程序也可以在这里使用。我们的示例框架具有脚本处理程序OnLoad和OnEvent。脚本处理程序OnLoad在魔兽世界完成加载框架后被调用。用SetScript(“OnLoad”,func)从lua中添加这个处理程序是无意义的,因为脚本处理程序将在调用CreatFrame(frame)之后被调用。如果您的框架是在没有OnLoad处理程序的情况下用XML创建的,那么在之后添加一个也是没有意义的。该处理程序在创建框架时正确地执行一次。稍后添加的OnLoad处理程序将永远不会执行。
脚本处理程序元素的内容是要存放在函数中的Lua代码。但是函数的头部在这里丢失了,那么我们如何知道哪些参数将被传递给它们呢?OnLoad的函数头是function(self),因为这个脚本处理程序不接收额外的参数。
OnEvent看起来像function(self,event,...)
附录A列出了所有XML脚本处理程序可用的参数。
包含的外部Lua和XML文件
XML文件还可以包含脚本处理程序和框架之外的Lua代码。您可以为此使用元素<Script>。它可以直接包含Lua代码,也可以使用file属性加载包含Lua脚本的文件。代码将在加载XML文件时执行。需要将<Script>元素放在所有<Frame>元素的外部,但要放在根元素<Ui>的内部:
元素<Include>允许包含另一个XML文件。该文件是通过使用该元素的file属性指定的。这个元素还必须放在所有其他元素之外,但必须放在根元素<Ui>的内部。
您不需要向TOC文件中添加通过<Script>/<Include>元素所包含的Lua或XML文件。这允许您在不使用TOC文件的情况下包含文件,如果您有必要添加许多属于一起的文件,并且希望将它们与其他文件分离,那么TOC文件非常有用。当您广泛使用所谓的嵌入式库时,常常会遇到这种情况,您将在第10章中更多地了解这个特性。
下面的XML显示了一个文件,它包括一个来自于子文件夹libs的Lua文件和一个来自于子文件夹libs中的XML文件,这些文件通常在创建Ace插件时使用。Ace也包含在第10章中。
<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\AceAddon-3.0.xml"/> <!-- even more Lua and XML files can be placed here--></Ui>如您所见,XML非常简单。可以用XML描述所有类型的框架。附录A提供了所有可用于所有框架类型的所有属性和元素的完整文档。
但是,如果XML文件中出现错误,会发生什么情况呢?如果没有在魔兽世界中测试,你怎么知道一个文件是否有效?
验证和调试XML
如果您使用的是更完整的ide之一,比如我在第1章中介绍的WoW AddOn Studio,那么您已经有了一个具有自动完成支持的语法检查器
但是,如果您使用SciTE,那么您只有对XML语法高亮显示和代码折叠的基本支持。
为了补充这一点,我在SciTE的发行版中包含了命令行实用程序MSV (Sun MultiSchema Validator)。
这一小节暂时略过,之后用到再看
框架基础
本节介绍基本框架以及如何显示在屏幕上。
框架可以有任意数量的子框架,但只能有一个或没有父框架。改变框架的属性也会影响到子框架。
我们将在这里创建扑克插件的第一部分:允许您浏览可用表列表并加入其中的一个表的框架。首先创建一个文件夹和一个TOC文件,然后向TOC文件添加文件localization.en.lua,TableBrowser.xml和TableBrowser.lua。TOC文件中文件的顺序定义加载顺序。如下所示。
##Interface: 30100locatization.en.luaTableBrowser.xmlTableBrowser.lua我们要加载的第一个东西是本地化文件,我们将在其中放入我们将要使用的所有字符串(它们需要在加载其他任何东西之前可用)。我们希望加载的下一个文件是XML文件,因为我们希望在执行Lua代码之前创建并可用框架。
我把我的插件名字命名为Texas_Hold’em.用以下代码填充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"> <Frame name="Poker_TableBrowser"> </Frame></Ui>它定义了一个名称为Poker_TableBrowser的框架,我们将在本章的剩余部分对其内容进行扩展。
使它可见
我们需要一个在屏幕上可见的框架。我们需要提供至少一个锚点(也可以几个点)。如果只有一个点,还需要设置大小。最终我们需要框架里的内容这样我们才能看到一些东西。
UI父级(定义父子级)
这个框架由游戏定义,是所有其他框架的父级。框架不需要是UI父级的直接子级,这以便于通过修改单个框架来更改所有框架的某些属性。改变UI的大小调用UIParent::SetScale(scale)。
我们将parent=”UIParent”添加到XML文件中的<Frame>内:
<Frame name="Poker_TableBrowser" parent="UIParent">可以通过调用frame:SetParent(parent)来从Lua更改框架的父框架,您还可以在从Lua创建框架时定义父框架,只需将父参数作为第三个参数传递给CreateFrame(type,name,parent)。
Anchors(锚点)确定位置用
锚定义了框架在游戏UI中的位置。一个框架可以有多个锚,我们从一个锚开始。<Anchor>元素必须放在<Frame>元素中的<Anchors>元素中。如下所示:
<Frame name="Poker_TableBrowser" parent="UIParent"> <Anchors> <Anchor point="LEFT" relativePoint="CENTERE"> <Offset> <AbsDimension x="200" y="0"/> </Offset> </Anchor> </Anchors></Frame>这段代码将框架的左侧锚定到其父框架的中心。Anchor point是进行设置的框架的哪个位置,relativePoint是要被绑定的游戏界面的位置。偏移量定义了父框架和子框架之间的空间。正x向右,负号向左。正y值——向上,负值向下。<AbsDimension>表示偏离距离负左正右。也可以使用<RelDimension>,它对x和y取0到1之间的值,为整个屏幕的百分比。偏移量可选填的,x和y的默认值是0。属性relativePoint也是可选的,默认情况下等于point。这表示着我们可以通过提供属性point快速地将框架锚定到其父框架的内部。我们的框架将被默认锚定在父框架。如果一个框架没有父框架,它将被锚定到整个屏幕。<Anchor>中的属性relativeTo=”frame”定义创建的框架将被锚定到的框架是哪个。
Points(点)
第一个图:显示了所有可用的点。第二个图:示意一个例子,其中子框架的点BOTTOMLEFT锚定在父框架的点BOTTOM上,X偏移量为15,Y偏移量为-10。
还可以用Lua代码设置、获取和删除锚。方法frame:SetPoint(point,relativeTo,relativePoint,x,y)创建一个新锚。除了point之外的所有参数都是可选的。relativeTo的默认值是框架的父值,relativePoint的默认值是作为第一个参数point传递的值,偏移量x和y默认为0。值relativeTo可以是一个框架,也可以是一个框架的名称。当您想要省略relativeTo和relativePoint时,可以将x和y作为第二个和第三个参数传递。
方法frame:GetPoint(n)返回框架的第n个点。返回值与SetPoint的参数相同。
您可以通过调用frame:ClearAllPoints()来删除所有点。调用此方法,屏幕上框架的位置不会改变,直到通过调用方法SetPoint设置新点。请注意,如果您想通过更改锚来移动框架,则必须始终调用ClearAllPoints。不清除这些点可能会产生意外结果,因为您最终会得到多个点。在本章后面,您将看到多个点如何影响框架的位置和大小。
在加载XML文件时仍然看不到任何效果,因为框架仍没有进行其他设置。但是我们可以用另一个框架,像默认UI的框架MinimapCluster,来测试方法SetPoint和ClearAllPoints的效果。以下代码可以在游戏中执行,以移动小地图与所有属于它的元素:
MinimapCluster:ClearAllPoints()
MinimapCluster:SetPoint("CENTER",100,-50)
现在可以测试设置不同点的效果。在设置新参数之前,不要忘记调用ClearAllPoints
MinimapCluster:ClearAllPoints()MinimapCluster:SetPoint(“CENTER”,100,-50)框架大小
两种方法,绝对值(数)和相对值(比),相对值即屏幕比例,比如相对高度和想对宽度都0.5,最终占屏幕的四分之一。绝对值取决于UI的缩放设置。屏幕的高度总是768像素除以UI scale(缩放)设置,你可以用UIParent:GetScale()检索到.例如,我将UI scale设置为0.75,因此屏幕的高度(从插件的角度来看)是768/0.75=1024。宽度取决于屏幕的纵横比。例如,我有一个16:10的宽屏显示器,所以我的虚拟屏幕的宽度是1024*1.6 = 1638.4。这里是说需要自己计算后设置,比如你更改了UI比例缩放,但是没换屏幕,那么屏幕的分辨率是不变的,但是768/缩放得到的数变化了,这个数就是虚拟分辨率。
因此,当我们定义大小或偏移量时,总是使用虚拟的分辨率,然后由游戏映射到您的真实屏幕上。该系统的优点是,框架的位置和大小不依赖于分辨率。当你在窗口模式下玩游戏时,这一点尤为重要,因为它允许你调整窗口大小而无需重新定位帧。
<Size>设置框架的大小。尺寸384*350比较好,因为384是默认UI对话框(比如字符框)的宽度。将下面的代码添加到<Frame>中,开始和结束标记之间:
<Size> <AbsDimension x="384" y="350"/> <Size>同样,也可以在这里使用<RelDimension>。如果您想从Lua中设置大小,可以使用方法frame:SetWidth(width)和frame:SetHeight(Height)。您可以通过调用GetWidth和GetHeight来检索大小。我们的框架如果有任何内容,将是可见的。
边框Backdrop
背景是边框和框架背景的组合。将下面的<Backdrop>元素添加到<Frame>:
<Backdrop bgFile="Interface\DialogFrame\UI-DialogBox-Background" tile="true"edgeFile="Interface\DialogFrame\UI-DialogBox-Border"> <TileSize> <AbsValue val="32"/> </TileSize> <EdgeSize> <AbsValue val="32"/> </EdgeSize> <BackgroundInsets> <AbsInset left="11" right="12" top="12" bottom="11"/> </BackgroundInsets></Backdrop>让我们看一下边框的属性和元素。第一个属性bgFile,设定用作背景纹理质感的路径。tile是true,纹理将重复填充;tile为false纹理将拉伸填充。
第二个属性edgeFile用于框架边框的纹理。下图显示了我们示例中使用的纹理。图像包含我们框架的所有边框。
tilesize定义边框纹理的宽度。高度总是纹理文件的高度。???
edgesize定义用于边缘的纹理部分的大小。粗细
边框是您在图5-2的左半部分可以看到的四条直线,而边是右半部分的角。
backgroundinsets设置背景将被绘制多远。就是阴影面积的四个边分别距离对应边框的距离。如果edgefile的边框较小,则使用较低的值
对于大多数插件来说,从暴雪的UI中复制和粘贴一个现有的背景元素就足够了。这可以确保你的插件具有原生外观,你不必设计自己的背景。
但是我还是要自己搞一个。
如果不想在默认情况下显示该框架,创建一个斜杠命令和一个用于稍后显示该内容的小地图按钮。
然后,您可以通过调用Poker_TableBrowser:Show()来显示它,在这个框架工作时,你可能想要保持隐藏状态在它的默认值false。??
如何设置多个点来限制框架。暂且略过P103
层和纹理,即 框架里的背景设定。
层(strata)就是前后顺序,和PS中的层是一个意思。框架层从高到低如下:
TOOLTIP
FULLSCREEN_DIALOG
FULLSCREEN
DIALOG
HIGH
MEDIUM
LOW
BACKGROUND
这个序列的翻译从高到低是:提示框>对话框全屏>全屏>对话框>加亮区?>中等的?>低等的?>背景
DIALOG,我们使用的都是这个,默认为对话框使用,覆盖在所有非全屏的窗口前面,可以通过调用frame:SetFrameStrata(strata)来设置框架的层。
层(layers)
保存纹理或文本,这个层包含在框架中。定义了美工贴图的层次。
下面的例子使用图层和纹理向我们的框架添加标题区域。一个例子,需要添加到frame里:
<Layers> <Layer level= "ARTWORK"> <Texture name="$parentTitle" file="InterfacelDialogF ramelUI-DialogBox-Header"> <Size> <AbsDimension x="375" y=" 64"/> </Size> <Anchors> <Anchor point= "TOP"> <Offset> <AbsDimension x="0" y="12"/> </Offset> </Anchor> </Anchors></Texture><FontString inherits ="GameFontNormal" text= "POKER_BROWSER_TITLE"> <Anchors> <Anchor point= "TOP" relativeTo="$parentTitle"> <Offset> <AbsDimension x="0" y="-14"/> </Offset> </Anchor> </Anchors></FontString></Layer></Layers><layers>保存所有<layer>,<layer>用来保存纹理<Texture>和文本块<FontString>,<layer>的唯一属性是level,下面是所有属性有效值从高到低:
HIGHLIGHT
OVERLAY
ARTWORK(default value)
BORDER
BACKGROUND
HIGHLIGHT级别特殊,因为放在这个层中的元素只有将鼠标移到上面时才可见。所以可用来设置奇特的辉光效果。示例中的层包含两个元素:一个纹理和一个文本。
纹理textures
层中的纹理与框架类似,需要锚点和大小。纹理名称为$parentTitle,其中$parent被父级名称替代。它的引用在具有此名称的全局变量下可用。file设置将要显示的纹理。要了解纹理可以拥有的所有元素和属性,参考附录a。
字体字符串
字体字符串需要名字。因为纹理的名称$parentTitle用作本字体字符串锚点中的relativeTo属性的值。
这让我们将标题上的文本正确地锚定到它的背景上,不可能通过XML将一个框架锚定到一个无名的框架上。您能通过Lua的方法SetPoint,将对该被绑定框架的引用作为第二个属性传递,将其用作锚。
属性inherits告诉游戏使用字体项目font objectGameFontNormal(就是正常字体的意思)。一个字体项目定义大小,颜色,字体。可以在FrameXML\Fonts.xml文件里找到所有默认UI的可用的字体项目。附录A包含有关字体用的所有文件、元素。
字体大小不是必须设置,但是可以设置上限。超出了宽度,将被包围。超过了高度,就会被截断。
我们可以通过在文件localization.en.lua中添加以下行来更改标题。
POKER_BROWSER_TITLE = "Texas Hold'em Table Browser"可移动框架
通过向<Frame>添加属性movable=”true”使框体可移动。这句话要加到和框架名称一个引号里,似乎对于插件基础设定的识别只认一个三角引号
通过添加属性enableMouse=”true”使框架能感应到鼠标点击。(事件属性,大概和上一章有关)
脚本处理程序OnDragStart和OnDragEnd用来检测你什么时候尝试拖动框架。方法frame:RegisterForDrag(button)用来设定哪个鼠标键可以拖动框架。LeftButton,MiddleButton,RightButton,MouseButton4,MouseButton5分别代表鼠标左中右和两个侧键。
OnDragStart:使用frame:StartMoving()在拖动时将框架和鼠标指针绑定。
OnDragEnd:释放时调用frame:StopMovingOrSizing()
示例如下(添加到框架里):
<Scripts> <OnLoad> self:RegisterForDrag("LeftButton") </0nLoad> <0nDragStart> self:StartMoving() </0nDragStart> <0nDragStop> self:StopMoving0rSizing() </0nDragStop></Scripts>其中的self是指自己这个框架。
在基础设定的引号里添加clampedToScreen="true"这句话可以让框架移动时不会超出屏幕。
self:SetUserPlaced(1)--保存位置用
继承
继承是设定一个虚拟框架,包括一些属性和元素,当需要创建新框架时直接从虚拟框架复制过来,免去很多公共设定重复操作。
属性virtual=”true” , inherits=”templateName”用来告诉游戏你想继承一个叫什么名字的虚拟框架。在用Lua文件创建时,把template作为第四个参数设给
CreateFrame(type,name,parent,template)虚拟框架只能在xml中设定,无法在Lua文件中设定。
框架类型按钮
按钮是本质也是框架。它们提供脚本处理程序OnClick,它在单击按钮时调用。按钮还具有纹理、显示在其上的文本块和定义文本字体样式的字体对象。纹理和字体样式可以随着按钮的状态而改变。有四种状态:1.有一个纹理和字体的默认组合,在正常状态下显示。2.当您将光标放在按钮上时,会出现一个组合。3、当您单击按钮时,会出现另一个组合。4.当禁用按钮时,将使用纹理和字体的第四个组合。
无法单击禁用的按钮。
XML中的按钮
按钮具有框架具有的所有元素和属性。因此,设置按钮的位置和大小也适用于锚点和<Size>也就是大小元素。同样,请参阅附录A以获得对按钮所能做的一切的概述。
下面的XML定义了一个标准的按钮:
<Button name="Poker Button"> <ButtonText> <Anchors> <Anchor point="CENTER"> <Offset> <AbsDimension x="0" y="1"/></0ffset> </Offset> </Anchor> </Anchors> </ButtonText> <NormalFont style="GameFontNormal"/> <HighlightFont style="GameFontHighlight" /> <DisabledFont style= "GameFontDisable" /> <NormalTexture inherits="UIPanelButtonUpTexture"/> <PushedTexture inherits= "UIPanelButtonDownTexture" /> <DisabledTexture inherits= "UIPanelButtonDisabledTexture" /> <HighlightTexture inherits="UIPanelButtonHighlightTexture"/></Button>这里缺少大小和锚点,因此如果您想使用此按钮,需要添加它们。按钮的文本用元素<ButtonText>定义,在本例中,要放在按钮的中间。元素<NormalFont>,<HighlightFont>和<DisabledFont>设置字体用于不同的状况。按下的按钮总是使用<HighlightFont>,因为您必须将鼠标放在按钮上才能按下它。四个<XTexture>元素定义了用于按钮不同状态的纹理。
有趣的是纹理使用继承属性。此属性的值是一个模板,所有其他属性和元素都从该模板中复制。所以我们必须看看这些模板。下面的代码显示了模板UIPanelButtonUpTexture,它可以在默认UI的文件Interface/FrameXML/UIPanelTemplates.xml中找到.
<Texture name= "UIPanelButtonUpTexture" file="Interface\Buttons\UI-Panel-Button-Up" virtual-"true"> <TexCoords left="0" right="0.625" top="0" bottom="0.6875"/> </Texture>使用按钮模板
如果想更改外观,设置一个按钮模版,以此避免重复设置。
你可以把属性virtual=”true”定义到按钮中,每次使用按钮,就使用inherits=”Poker_Button”,但是这里更倾向使用暴雪定义好的模版,这样暴雪对游戏有改动时,模版会自动更改。
在创建自己的模板之前,您应该始终查看Blizzard的XML文件。所有名称中包含单词Template的XML文件都是模板的潜在资源,您的插件可以使用这些模板。
这个模板的名字是UIPanelButtonTemplate,它也在文件Interface/FrameXML/UIPanelTemplates.xml中定义。在创建自己的模板之前,一定要查看该文件,因为它已经包含了一些好的模板。然后可以使用下面的代码从这个模板创建一个按钮。按钮应该是一个框架Poker_TableBrowser的子框架。一个框架的所有子框架必须放在框架的元素<Frames>中,所以添加以下代码到您的<Frames>元素来创建按钮作为子元素:
<Frames> <Button name="$parentTestButton" text="Test :)" inherits= "UIPanelButtonTemplate" > <Size> <AbsDimension x="96" y="32"/> </Size> <Anchors> <Anchor point="CENTER" /> </Anchors> <Scripts> <0nLoad> self:RegisterForClicks( "LeftButtonUp" , "RightButtonUp" , "MiddleButtonDown" ) </0nLoad> <OnClick> -- the function header is function(self, button) print("Clicked with" , button) </0nClick> </Scripts> </Button></Frames>已编辑