第五章 使用XML和框架(魔兽世界Lua插件开发指南)

[复制链接]

该用户从未签到

2380

主题

2433

帖子

9139

积分

管理员

Rank: 9Rank: 9Rank: 9

积分
9139
QQ
发表于 2023-12-21 21:11:54 | 显示全部楼层 |阅读模式

想要查看内容赶紧注册登陆吧!

您需要 登录 才可以下载或查看,没有帐号?立即注册

x
上一章讲了在Lua文件中创建框架,这一章则是在XML文件中创建,xml用来描述数据,在这里描述的数据正是游戏中的框架。lua可以创建框架,xml也可以,两者各有优劣,虽然xml功能强大,但并不全能,有些部分还是要Lua来完成。XML最大的优点之一是可以将UI设计和代码清晰地分离开来。这允许您稍后更改加载项的外观,而无需修改任何代码。只需使用不同的XML文件,甚至不需要接触Lua代码,就可以很容易地为插件实现不同的皮肤。
Lua的优点是它可以在脚本运行时动态创建框架。稍后您将看到如何结合XML和Lua以获得两者的优点。
我们将要编写的示例mod将是一个功能齐全的游戏内德州Hold'em扑克游戏。这是一个很长的示例,所以这一章并没有涵盖整个mod。下面的章节将继续创建这个插件。到本章结束时,我们只创建了这个插件的一小部分,但它涵盖了许多特别有趣的框架类型。您将学习如何在这里创建配置框架,然后在第7章和第8章中构建处理扑克游戏的部分。
你应该试着把这样一个综合性的项目分成几个小部分。这可以减少一个大问题(创建一个poker插件),方法是将它分解成许多小问题,然后再将这些小问题分解成更小的任务,这些任务可以像单个Lua函数一样简单。我们将需要一个服务器,含有一个扑克表格和客户端,在客户端中可以加入一个表格中玩。然后,服务器可能被分成一部分,提供辅助功能,例如比较两只手来确定赢家;另一部分与表格中的客户端进行交互。客户端也可以拆分为一个部分,允许你挑选一个表格,这个表格来自于公会或raid中含有的表格总列表中的一个,用户界面显示当前正在游玩的表格。
在本章中,我们将设计和创建图形用户界面的基本部分。但要做到这一点,我们首先需要讨论XML。
XML 基础
让我们从一个例子开始,这样你就可以看到魔兽世界中的XML文件是什么样子的。
下面的XML使用脚本处理程序onload和onevent创建一个框架。
如果要测试该示例,请将以下代码存储在名为firstUI.xml的文件中,并将该文件添加到TOC文件中。
向TOC文件添加XML文件的工作方式与向Lua文件添加插件类似,只需在TOC文件中添加一行firstUI.xml
Code c:
  1. <Ui xmlns="[url]http://www.blizzard.com/wow/ui/"[/url]
  2. xmlns:xsi="[url]http://www.w3.org/2001/XMLSchema-instance"[/url]
  3. xsi:schemaLocation="[url]http://www.blizzard.com/wow/ui/[/url] ..\FrameXML\UI.xsd">
  4. <Frame name="MyTestFrame">
  5.    <Scripts><!--this is a comment -->
  6.   <OnLoad>
  7.        self:RegisterEvent("CHAT_MSG_WHISPER")
  8.   </OnLoad>
  9.   <OnEvent>
  10.        local msg, sender =...
  11.              print(event,sender,msg)
  12.   </OnEvent>
  13.      </Scripts>
  14.    </Frame>
  15. </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章中。
Code c:
<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文件中文件的顺序定义加载顺序。如下所示。
Code c:
  1. ##Interface: 30100
  2. locatization.en.lua
  3. TableBrowser.xml
  4. TableBrowser.lua
复制代码
我们要加载的第一个东西是本地化文件,我们将在其中放入我们将要使用的所有字符串(它们需要在加载其他任何东西之前可用)。我们希望加载的下一个文件是XML文件,因为我们希望在执行Lua代码之前创建并可用框架。
我把我的插件名字命名为Texas_Hold’em.用以下代码填充XML文件:
Code c:
  1. <Ui xmlns="[url]http://www.blizzard.com/wow/ui/"[/url]
  2. xmlns:xsi="[url]http://www.w3.org/2001/XMLSchema-instance"[/url]
  3. xsi:schemaLocation="[url]http://www.blizzard.com/wow/ui/[/url] ..\FrameXML\UI.xsd">
  4.     <Frame name="Poker_TableBrowser">
  5.     </Frame>
  6. </Ui>
复制代码
它定义了一个名称为Poker_TableBrowser的框架,我们将在本章的剩余部分对其内容进行扩展。
使它可见
我们需要一个在屏幕上可见的框架。我们需要提供至少一个锚点(也可以几个点)。如果只有一个点,还需要设置大小。最终我们需要框架里的内容这样我们才能看到一些东西。
UI父级(定义父子级)
这个框架由游戏定义,是所有其他框架的父级。框架不需要是UI父级的直接子级,这以便于通过修改单个框架来更改所有框架的某些属性。改变UI的大小调用UIParent::SetScale(scale)。
我们将parent=”UIParent”添加到XML文件中的<Frame>内:
Code c:
  1. <Frame name="Poker_TableBrowser" parent="UIParent">
复制代码
可以通过调用frame:SetParent(parent)来从Lua更改框架的父框架,您还可以在从Lua创建框架时定义父框架,只需将父参数作为第三个参数传递给CreateFrame(type,name,parent)。
Anchors(锚点)确定位置用
锚定义了框架在游戏UI中的位置。一个框架可以有多个锚,我们从一个锚开始。<Anchor>元素必须放在<Frame>元素中的<Anchors>元素中。如下所示:
Code c:
  1. <Frame name="Poker_TableBrowser" parent="UIParent">
  2.    <Anchors>
  3.       <Anchor point="LEFT" relativePoint="CENTERE">
  4.           <Offset>
  5.                <AbsDimension x="200" y="0"/>
  6.           </Offset>
  7.        </Anchor>
  8.     </Anchors>
  9. </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。

1.jpg
2.jpg
还可以用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
Code c:
  1. MinimapCluster:ClearAllPoints()
  2. 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>中,开始和结束标记之间:
Code c:
  1. <Size>
  2.      <AbsDimension x="384" y="350"/>
  3.   <Size>
复制代码
同样,也可以在这里使用<RelDimension>。如果您想从Lua中设置大小,可以使用方法frame:SetWidth(width)和frame:SetHeight(Height)。您可以通过调用GetWidth和GetHeight来检索大小。我们的框架如果有任何内容,将是可见的。
边框Backdrop
背景是边框和框架背景的组合。将下面的<Backdrop>元素添加到<Frame>:
Code c:
  1. <Backdrop bgFile="Interface\DialogFrame\UI-DialogBox-Background" tile="true"
  2. edgeFile="Interface\DialogFrame\UI-DialogBox-Border">
  3.     <TileSize>
  4.        <AbsValue val="32"/>
  5.     </TileSize>
  6.     <EdgeSize>
  7.         <AbsValue val="32"/>
  8.     </EdgeSize>
  9.     <BackgroundInsets>
  10.         <AbsInset left="11" right="12" top="12" bottom="11"/>
  11.     </BackgroundInsets>
  12. </Backdrop>
复制代码
让我们看一下边框的属性和元素。第一个属性bgFile,设定用作背景纹理质感的路径。tile是true,纹理将重复填充;tile为false纹理将拉伸填充。
第二个属性edgeFile用于框架边框的纹理。下图显示了我们示例中使用的纹理。图像包含我们框架的所有边框。

3.jpg
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里:
Code c:
  1. <Layers>
  2.   <Layer level= "ARTWORK">
  3.     <Texture name="$parentTitle" file="InterfacelDialogF ramelUI-DialogBox-Header">
  4.       <Size>
  5.         <AbsDimension x="375" y=" 64"/>
  6.       </Size>
  7.       <Anchors>
  8.         <Anchor point= "TOP">
  9.           <Offset>
  10.             <AbsDimension x="0" y="12"/>
  11.           </Offset>
  12.         </Anchor>
  13.       </Anchors>
  14. </Texture>
  15. <FontString inherits ="GameFontNormal" text= "POKER_BROWSER_TITLE">
  16.   <Anchors>
  17.     <Anchor point= "TOP" relativeTo="$parentTitle">
  18.       <Offset>
  19.         <AbsDimension x="0" y="-14"/>
  20.       </Offset>
  21.     </Anchor>
  22.   </Anchors>
  23. </FontString>
  24. </Layer>
  25. </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中添加以下行来更改标题。
Code c:
  1. 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()
示例如下(添加到框架里):
Code c:
  1. <Scripts>
  2.   <OnLoad>
  3.     self:RegisterForDrag("LeftButton")
  4.   </0nLoad>
  5.   <0nDragStart>
  6.     self:StartMoving()
  7.   </0nDragStart>
  8.   <0nDragStop>
  9.     self:StopMoving0rSizing()
  10.   </0nDragStop>
  11. </Scripts>
复制代码
其中的self是指自己这个框架。

在基础设定的引号里添加clampedToScreen="true"这句话可以让框架移动时不会超出屏幕。
self:SetUserPlaced(1)--保存位置用

继承
继承是设定一个虚拟框架,包括一些属性和元素,当需要创建新框架时直接从虚拟框架复制过来,免去很多公共设定重复操作。
属性virtual=”true” , inherits=”templateName”用来告诉游戏你想继承一个叫什么名字的虚拟框架。在用Lua文件创建时,把template作为第四个参数设给
Code c:
  1. CreateFrame(type,name,parent,template)
复制代码

虚拟框架只能在xml中设定,无法在Lua文件中设定。

框架类型按钮
按钮是本质也是框架。它们提供脚本处理程序OnClick,它在单击按钮时调用。按钮还具有纹理、显示在其上的文本块和定义文本字体样式的字体对象。纹理和字体样式可以随着按钮的状态而改变。有四种状态:1.有一个纹理和字体的默认组合,在正常状态下显示。2.当您将光标放在按钮上时,会出现一个组合。3、当您单击按钮时,会出现另一个组合。4.当禁用按钮时,将使用纹理和字体的第四个组合。
无法单击禁用的按钮。
XML中的按钮
按钮具有框架具有的所有元素和属性。因此,设置按钮的位置和大小也适用于锚点和<Size>也就是大小元素。同样,请参阅附录A以获得对按钮所能做的一切的概述。
下面的XML定义了一个标准的按钮:
Code c:
  1. <Button name="Poker Button">
  2.   <ButtonText>
  3.     <Anchors>
  4.       <Anchor point="CENTER">
  5.         <Offset>
  6.           <AbsDimension x="0" y="1"/></0ffset>
  7.         </Offset>
  8.       </Anchor>
  9.      </Anchors>
  10.   </ButtonText>
  11. <NormalFont style="GameFontNormal"/>
  12. <HighlightFont style="GameFontHighlight" />
  13. <DisabledFont style= "GameFontDisable" />
  14. <NormalTexture inherits="UIPanelButtonUpTexture"/>
  15. <PushedTexture inherits= "UIPanelButtonDownTexture" />
  16. <DisabledTexture inherits= "UIPanelButtonDisabledTexture" />
  17. <HighlightTexture inherits="UIPanelButtonHighlightTexture"/>
  18. </Button>
复制代码

这里缺少大小和锚点,因此如果您想使用此按钮,需要添加它们。按钮的文本用元素<ButtonText>定义,在本例中,要放在按钮的中间。元素<NormalFont>,<HighlightFont>和<DisabledFont>设置字体用于不同的状况。按下的按钮总是使用<HighlightFont>,因为您必须将鼠标放在按钮上才能按下它。四个<XTexture>元素定义了用于按钮不同状态的纹理。
有趣的是纹理使用继承属性。此属性的值是一个模板,所有其他属性和元素都从该模板中复制。所以我们必须看看这些模板。下面的代码显示了模板UIPanelButtonUpTexture,它可以在默认UI的文件Interface/FrameXML/UIPanelTemplates.xml中找到.
Code c:
  1. <Texture name= "UIPanelButtonUpTexture"
  2. file="Interface\Buttons\UI-Panel-Button-Up" virtual-"true">
  3.     <TexCoords left="0" right="0.625" top="0" bottom="0.6875"/>
  4.     </Texture>
复制代码
使用按钮模板
如果想更改外观,设置一个按钮模版,以此避免重复设置。
你可以把属性virtual=”true”定义到按钮中,每次使用按钮,就使用inherits=”Poker_Button”,但是这里更倾向使用暴雪定义好的模版,这样暴雪对游戏有改动时,模版会自动更改。
在创建自己的模板之前,您应该始终查看Blizzard的XML文件。所有名称中包含单词Template的XML文件都是模板的潜在资源,您的插件可以使用这些模板。
这个模板的名字是UIPanelButtonTemplate,它也在文件Interface/FrameXML/UIPanelTemplates.xml中定义。在创建自己的模板之前,一定要查看该文件,因为它已经包含了一些好的模板。然后可以使用下面的代码从这个模板创建一个按钮。按钮应该是一个框架Poker_TableBrowser的子框架。一个框架的所有子框架必须放在框架的元素<Frames>中,所以添加以下代码到您的<Frames>元素来创建按钮作为子元素:
Code c:
  1. <Frames>
  2.   <Button name="$parentTestButton" text="Test :)" inherits= "UIPanelButtonTemplate" >
  3.     <Size>
  4.       <AbsDimension x="96" y="32"/>
  5.     </Size>
  6.     <Anchors>
  7.       <Anchor point="CENTER" />
  8.     </Anchors>
  9.     <Scripts>
  10.       <0nLoad>
  11.         self:RegisterForClicks(
  12.            "LeftButtonUp" ,
  13.            "RightButtonUp" ,
  14.            "MiddleButtonDown"
  15.            )
  16.       </0nLoad>
  17.       <OnClick> -- the function header is function(self, button)
  18.         print("Clicked with" , button)
  19.       </0nClick>
  20.     </Scripts>
  21.   </Button>
  22. </Frames>
复制代码
方法RegisterForClicks获取鼠标事件列表,这样的一个鼠标事件由一个按键(如鼠标左键、右键、滑轮、侧键)和OnClick处理程序被调用的时刻组成。这一时刻也可以是Up或者Down,第二个调用到此方法的事件会覆盖上一个,如果你根本不想调用此方法,他会听从默认设置的LeftButtonUp.
加载此XML会增加一个按钮,就出现在你的Poker_TableBrowser框体的中间,点击按钮会调用OnClick脚本程序,在程序中,按钮作为第二个参数存在,第一个参数是框架本身,此程序稍后会在你的聊天中发送信息。
现在我们成功创建了一些测试按钮,我们需要将其转变为有用的按钮,我们的框体应该看起来是什么样?需要哪些按钮?放在哪?

设计表格浏览器
在你写XML文件前,先检查你想创建的框架,至少确定完工后的外观如何,表5-3展示了我们将要创造的框体样式。
4.png
如你所见,此框架显示了一个列表,其中包含了所有在你的公会或者队伍中可获得的表格。使用者可以从整个列表中选一个表格点击,因此这列表中的条目也可以点击。下方的“Enter a Name”(添加名字)可用于手动输入一个持有扑克列表的玩家的名字,以防止某些玩家不在公会或者队伍中。按钮“Creat Table”(创建列表)会产生一个对话,这个对话允许你持有一个表格。最后的按钮是“Close Window”即关闭窗口。让我们来创建这些按钮。
简单的按钮
如果你从上一部分学习中添加了按钮,现在删掉再来继续学。“Close”按钮应该也作为一个子框架,因此我们要把他放在框架元素<Frames>中,下面的代码展示<Frames>元素包含一个按钮,此按钮以父框架底端作为锚点。
Code c:

  1. <Frames>
  2.     <Button name="$parentClose" text="POKER_BROWSER_CLOSE"  inherits="UIPanelButtonTemplete">
  3.       <Size>
  4.           <AbsDimension x="64" y="24"/>
  5.       </Size>
  6.       <Anchors>
  7.         <Anchor point="BOTTOM">
  8.           <Offset>
  9.             <AbsDimension x="0" y="15"/>
  10.           </Offset>
  11.         </Anchor>
  12.       </Anchors>
  13.       <Scripts>
  14.         <OnClick>
  15.              self:GetParent():Hide()
  16.         </OnClick>
  17.       </Scripts>
  18.     </Button>
  19. </Frames>
复制代码

你也需要添加下面的一行代码,添加到文件localization.en.lua:
POKER_BROWSER_CLOSE = “Close”
“OnClick”处理程序调用self:GetParent()函数来获得按钮的父级,也就是Poker_TableBrowser框架,之后代码调用框架的Hide()方法。
我们应继续添加一个简单的指令按钮,但你该为他们选择哪个锚点?一个好的锚点是刚好位于它们上方的所有列表的总表(也就是包含所有表格分支的对话框),因此我们应该首先创建它。
创建一个列表元素
我们需要一个带有可点击元素和一个标头的列表。标头要在我们希望用各种标准对列表进行分类时也变得可点击。我们将要使用的框架类型是按钮。
创建一个边界
但是首先,我们需要给这些按钮一个边界。我们可以用一个背景层来绘制一个好的边界。背景层一直是框架的一部分;你不能直接把背景层作为子级加到另一个框架中。但是我们可以创建一个尺寸刚好的空的框架,这个空框架以一个背景层作为子级,下面的代码展示了一个框架,这个框架带有的背景层没有背景,只有边界。它使用工具提示边框,这样我们得到了一个不起眼的边框,围绕在UI总列表这部分一圈。
Code c:

  1. <Frame name="$parentTableList">
  2.       <Size>
  3.           <AbsDimension x="354" y="225"/>
  4.       </Size>
  5.       <Anchors>
  6.         <Anchor point="TOP">
  7.           <Offset>
  8.             <AbsDimension x="0" y="-26"/>
  9.           </Offset>
  10.         </Anchor>
  11.       </Anchors>
  12.       <Backdrop edgeFile="Interface\Tooltips\UI-Tooltip-Border" tile="true">
  13.         <EdgeSize>
  14.              <AbsValue val="16"/>
  15.         </EdgeSize>
  16.         <TileSize>
  17.              <AbsValue val="16"/>
  18.         </TileSize>
  19.     </Backdrop>
  20. </Frames>
复制代码
记住,我们想要这些变成框架Poker_TableBrowser的子级,因此你要把这些代码放在Poker_TableBrowser’s<Frames>元素之内。把一个框架作为另一个框架的子级并没有问题,甚至这个框架在他的<Frames>元素内还可以有一个子级框架。
这里有个问题,为什么我们要在这里使用<AbsDimension>元素。毕竟,我在图5-3中的草图中添加了以百分比表示的近似尺寸,所以我们在这不能用<RelDimension>元素吗?最好使用相对于父框架的尺寸,但是<RelDimension>不能像这样工作,它总是相对于整个屏幕,而不是相对于父框架。这就使得<RelDimension>对我们来说毫无用处。所以我们必须使用绝对值。
现在我们可以创建列表,第一件事是标头。
为标头创建一个模板
每一列都需要自己的标题(也就是标头),即一个按钮。单击此按钮将调用对列表排序分类的函数。我们有四列,所以我们需要四个标题。为这些按钮创建一个模板是个好主意,因为这样可以节省大量重复的代码。下面的XML显示了可用于标题按钮的模板。模板不是真实的框架,因此不能将其放置在框架的<Frames>元素中。它必须在我们之前创建的框架之外,但在根元素<Ui>内。由于我们稍后将在框架中引用此模板,因此它必须位于文件的开头。
Code c:

<Button name="oker_TableBrowserHeader" virtual="true">
      <Scripts>
          <OnClick>
                Poker_TableBrowser.SortTables(this:GetID())
                PlaySound("igMainMenuOptionCheckBoxOn")
          </OnClick>
          <OnEnter>
                getglobal(self:GetName().."BG"):SetVertexColor(1,1,1,1)
          </OnEnter>
          <OnLeave>
                getglobal(self:GetName().."BG"):SetVertexColor(0.7,0.7,0.7,0.7)
          </OnLeave>
          <OnLoad>
                self:GetScript("OnLeave")(self)
          </OnLoad>
      </Scripts>
      <ButtonText>
         <Anchors>
            <Anchor point="LEFT">
               <Offset>
                  <AbsDimension x="4" y="0"/>
               </Offset>
            </Anchor>
         </Anchors>
      </ButtonText>
        <NormalFont style="GameFontHighlight"/>
        <HighlightFont style="GameFontNormal"/>
        <NormalTexture name="$parentBG">
             <Color r="0.4" b="0.4" a="0.4"/>
        </NormalTexture >
    </Backdrop>
</Button>
锚点的代码以及按钮的文本和字体应该非常清晰。注意<NormalTexture>不引用文件;相反,它使用<Color>元素,创建一个纯色纹理。这里我们不使用元素<HighlightTexture>,作为代替我们在脚本程序OnEnter和OnLeave中使用方法frame:SetVertexColor(red,green,blue,alpha),以此来修改我们的<NormalTexture>元素的vertex color(顶点着色)。顶点颜色只是3D图形中的一个花哨术语,你会在第8章中看到更多关于纹理和颜色的内容。现在你只需要知道顶点颜色可以用来修改纹理的颜色。基本上,当鼠标光标在按钮上时,我们通过将顶点颜色设置为1,1,1,1来高亮显示按钮的背景纹理。我们无法从XML设置框架的顶点颜色,因此在加载框架时需要调用OnLeave处理程序,因为OnLeave处理程序设置了默认的顶点颜色,我们不想重复这段代码。我们在这里使用frame:GetScript(“handler”)方法来检索OnLeave处理程序。记住,这个处理程序要求它的第一个参数是框架本身,所以我们需要在这里略过它。
当我们点击按钮,处理程序OnClick定义了我们想要获取的动作,我们调用函数Poker_TableBrowser.SortTables,这个函数我们在后面会创建出来。这个函数需要知道被点击的按钮的ID,我们把ID传给函数。对此,最简单的方法是创建按钮时给每个按钮不同的ID,当按钮被点击,ID就被获取。你可以通过使用属性id=”number”来设置ID。ID默认值是0。也可能把self传递给函数,然后调用方法frame:GetName();我们可以用这个名字定义被点击的标头。但是使用ID更好,因为他们只是一小串数字,而名字很长。
我们会从这个模板创建一些按钮,所有的按钮都会由相同的OnClick处理程序。
使用模板
让我们从模板创建真正的按钮。添加下列代码到框架$parentTableList来创建四个标头,这些将作为$parentTableList的子级北我们的列表使用。
Code c:
  1. <Frames>
  2. <Button inherits=”Poker_TableBrowserHeader” name=”$parentHeaderName” id=”1”
  3. text=”POKER_BROWSER_NAME”>
  4.    <Size>
  5.       <AbsDimension x=”138” y=”24”/>
  6.    </Size>
  7.    <Anchors>
  8.    <Anchor point=”TOPLEFT”>
  9.       <Offset>
  10.         <AbsDimension x=”4” y=”-4”/>
  11.       </Offset>
  12.    </Anchor>
  13.    </Anchors>
  14. </Button>
  15. <Button inherits=”Poker_TableBrowserHeader” name=”$parentHeaderHost” id=”2”
  16. text=”POKER_BROWSER_HOST”>
  17.    <Size>
  18.       <AbsDimension x=”90” y=”24”/>
  19.    </Size>
  20.    <Anchors>
  21. <Anchor point=”LEFT” relativePoint=”RIGHT”
  22. relativeTo=”$parentHeaderName”/>
  23.    </Anchors>
  24. </Button>
  25. <Button inherits=”Poker_TableBrowserHeader” name=”$parentHeaderPlayers” id=”3”
  26. text=”POKER_BROWSER_PLAYERS”>
  27.    <Size>
  28.       <AbsDimension x=”50” y=”24”/>
  29.    </Size>
  30.    <Anchors>
  31. <Anchor point=”LEFT” relativePoint=”RIGHT” relativeTo=”$parentHeaderHost”/>
  32.    </Anchors>
  33. </Button>
  34. <Button inherits=”Poker_TableBrowserHeader” name=”$parentHeaderBlinds” id=”5”
  35. text=”POKER_BROWSER_BLINDS”>
  36.    <Size>
  37.       <AbsDimension x=”50” y=”24”/>
  38.    </Size>
  39.    <Anchors>
  40. <Anchor point=”LEFT” relativePoint=”RIGHT”
  41. relativeTo=”$parentHeaderPlayers”/>
  42.    </Anchors>
  43. </Button>
  44. </Frames>
复制代码

这里创建四个标头于表中,在右侧留下了一个小空间。我们之后将添加一个滚动条。按钮都有ID,按钮被点击时ID将会被传给分类函数,我们使用ID1,2,3,5。跳过4是因为第三个栏实际上存储了两个值:当前玩家和列表玩家最大数量。
我们需要添加下面的代码至局部文件来得到按钮的正确文本:
Code c:
  1. POKER_BROWSER_NAME = ”Table Name”
  2. POKER_BROWSER_HOST = “Host”
  3. POKER_BROWSER_PLAYERS = “Players”
  4. POKER_BROWSER_BLINDS = “Blinds”
复制代码

关于我们XML文件的简短概况
现在整个文件应该有以下构造,我们的标头按钮是框架的子级,而框架是用于背景层。
Code c:
  1. <Ui...>
  2.    <Button name=”Poker_TableBrowserHeader”...>
  3.    ...
  4.    </Button>
  5.    <Frame name=”Poker_TableBrowser”...>
  6.    ...
  7.       <Frames>
  8.          <Button name=”$parentClose”...>
  9.        ...
  10.      </Button>
  11.      <Frame name=”$parentTableList”>
  12.        ...
  13. <Frames>
  14.             <Button name=”$parentHeaderName”...>
  15.                ...
  16.             </Button>
  17.             <Button name=”$parentHeaderHost”...>
  18.                ...
  19.             </Button>
  20.             <Button name=”$parentHeaderPlayers”...>
  21.                ...
  22.             </Button>
  23.             <Button name=”$parentHeaderBlinds”...>
  24.                ...
  25.             </Button>
  26.           </Frames>
  27.         </Frame>
  28.      </Frames>
  29.   </Frame>
  30. </Ui>
复制代码
牢记你的XML文件构造的概况是很重要的,尤其是当你添加新的框体使文件变得越来越大。在本章末尾会有一个关于这个文件的最终总结。
现在我们需要用entries(进入)填充列表,进入也是按钮。
创建一个“进入”的模板
我们需要为列表中代表一个进入的按钮建立模板。我们不能使用元素<ButtonText>,因为需要给每个栏准备一个文本元素(并且每个按钮横跨所有的四个栏)。但是不要紧,因为按钮也能含有层(layers)。因此我们需要的是一个按钮,这个按钮在一个层里面包含四个<FontString>元素。这里使用默认层,也就是ARTWORK。
一个“进入”按钮被点击时调用函数Poker_TableBrowser.SelectEntry,被双击时调用函数Poker_TableBrowser.JoinSelectedTable。我们也使用OnEnter和OnLeave处理程序来获得好的悬停作用。下面的代码必须放在XML文件的开头,但要在<Ui>元素后。因为它展示了合适的“进入”按钮模板。
Code c:
  1. <Button name=”Poker_TableBrowserEntry” hidden=”true” virtual=”true”>
  2. <Size>
  3.    <AbsDimension x=”328” y=”24”/>
  4. </Size>
  5. <Layers>
  6.   <Layer>
  7.     <FontString name=””$parentName” justifyH=”LEFT”
  8. inherits=”GameFontNormalSmall”>
  9.       <Size>
  10.         <AbsDimension x=”138” y=”24”/>
  11.       </Size>
  12.           <Anchors>
  13.             <Anchor point=”LEFT”>
  14.               <Offset>
  15.           <AbsDimension x=”4” y=”0”/>
  16.             </Offset>
  17.             </Anchor>
  18.           </Anchors>
  19.         </FontString>
  20.     <FontString name=””$parentHost” justifyH=”LEFT”
  21. inherits=”GameFontNormalSmall”>
  22.       <Size>
  23.         <AbsDimension x=”80” y=”24”/>
  24.       </Size>
  25.           <Anchors>
  26.             <Anchor point=”LEFT” relativePoint=”RIGHT” relativeTo=”$parentName”/>
  27.           </Anchors>
  28.         </FontString>
  29.     <FontString name=””$parentPlayers” justifyH=”LEFT”
  30. inherits=”GameFontNormalSmall”>
  31.       <Size>
  32.         <AbsDimension x=”50” y=”24”/>
  33.       </Size>
  34.           <Anchors>
  35.             <Anchor point=”LEFT” relativePoint=”RIGHT” relativeTo=”$parentHost”/>
  36.           </Anchors>
  37.         </FontString>
  38.     <FontString name=””$parentBlinds” justifyH=”LEFT”
  39. inherits=”GameFontNormalSmall”>
  40.       <Size>
  41.         <AbsDimension x=”60” y=”24”/>
  42.       </Size>
  43.           <Anchors>
  44.             <Anchor point=”LEFT” relativePoint=”RIGHT” relativeTo=”$parentPlayers”/>
  45.           </Anchors>
  46.         </FontString>
  47.   </Layer>
  48. </Layers>
  49. <Scripts>
  50.   <OnLoad>
  51.     getglobal(self:GetName()..”BG”):Hide()
  52.   </OnLoad>
  53.   <OnClick>
  54.     if not Poker_TableBrowser.IsSelected(self:GetID()) then
  55.        Poker_TableBrowser.SelectEntry(self:GetID())
  56.      end
  57.   </OnClick>
  58.   <OnDoubleClick>
  59.      Poker_TableBrowser.JoinSelectedTable()
  60.   </OnDoubleClick>
  61.   <OnEnter>
  62.      getglobal(self:GetName()..”BG”):show()
  63.   </OnEnter>
  64. <OnLeave>
  65.    if not Poker_TableBrowser.IsSelected(self:GetID()) then
  66.       getglobal(self:GetName()..”BG”):Hide()
  67.    end
  68. </OnLeave>
  69. </Scripts>
  70.     <NormalTexture name=”$parentBG”>
  71.    <Color r=”0”  g=”0”  b=”0.5”  a=”0.25”>
  72. </NormalTexture>
  73.   </Button>
复制代码
尽管代码很长,但是并不复杂。它定义了一个尺寸,一个蓝色的背景纹理和一个层。背景纹理只有在元素被选中或者悬停的时候才会显示。该层包含四个大小与标题匹配的字体字符串,字体字符串使用属性justifyH。此属性控制字体字符串的水平对齐方式;它可以是LEFT,RIGHT或者CENTER(默认的)。
我们还有一些脚本处理程序。处理程序OnLoad可能看起来很奇怪,因为它在加载时隐藏了背景纹理。纹理作为突出显示使用,因此理应在被选中或者悬停时才显示。因此默认状态是隐藏。也可以在<NormalTexture>的定义中使用属性hidden=”true”。然而,属性hidden虽然有效但不产生效果。我不知道这是否是一个Bug或功能,它就是不起作用。因此我们必须用OnLoad处理程序。
OnClick处理程序调用一个函数,该函数选择当前条目(如果尚未选择)。双击按钮调用函数,这个函数链接当前选定的表格。因为双击总是首先涉及到一次单击,所以它总是在调用OnDoubleClick处理程序之前调用OnClick处理程序。这意味着属于按钮的条目总是在这里被选中。
OnEnter处理程序展示用于高亮的材质纹理,当按钮未被选中,OnLeave处理程序就会隐藏它。我们现在可以使用这个模板并从中创建实际的按钮。通过做一点数学计算,我们可以计算出我们的框架有空间容纳八个这样的按钮。对于从同一模板创建的那么多框架,最好使用Lua创建它们。
创建按钮
如前所述,CreateFrame的第四个参数是模板,该模板用于从中创建框架。所以我们需要创建一个框架,通过调用方法frame:SetID(id)来设置它的ID,并设置它的位置。该ID稍后将由脚本处理程序用于识别框架。
使用lua创建框架
下面的代码创建按钮,应该放在TableBrowser.lua中:
Code c:

  1. local MAX_TABLES =  8 -- maximum number of entries that can be displayed
  2. do
  3.    local entry=CreateFrame(“Button”,”$parentEntry1”,Poker_TableBrowserTableList,
  4. ”Poker_TableBrowserEntry”)
  5. entry:SetID(1)
  6. entry:SetPoint(“TOPLEFT”,4,-28)
  7. for i = 2,MAX_TABLES do
  8.    local entry = CreateFrame(“Button”,”$parentEntry”..i,
  9. Poker_TableBrowserTableList,”Poker_TableBrowserEntry”)
  10. entry:SetID(i)
  11. entry:SetPoint(“TOP”,”$parentEntry”..(i - 1),”BUTTOM”)
  12. end
  13. end
复制代码
第一行创建一个局部变量,该变量将在整个文件中可用,并保存可在框架中显示的最大条目数。随后do-end代码块以ID1创建了第一个按钮。锚定在父框的顶左端(父框就是我们名单的框架),以下所有按钮都在一个循环中创建,并锚定到上一个按钮。注意框架只是表格,因此代替使用frame:SetID(id)和frame:GetID(id),我们可以简单地使用表中的一个字段。所有,我们还可以使用以下代码来设置标识符:
Code c:
  1. entry.id=i
复制代码
任何在之前的脚本处理程序中出现的self:GetID()之后都需要由self.id代替。将框架用作表格并在其中存储信息的优点是,我们可以在其中放置任何值,例如另一个包含大量信息的表格。通过XML(或SetID)定义的ID只能是数字。XML-IDs的最大优点是它们可以通过使用XML属性来定义。
尽管我们这里不使用XML来创建这些按钮,但我们仍然使用SetID和GetID。为什么?我们选择哪种方式来识别框架并不重要,因为我们只是在存储一个数字。如果我必须存储数字并且框架主要由XML代码创建,我更喜欢使用SetID和GetID。
为按钮创建内容
我们的按钮还是空的。我们必须用内容填充它们才能得到可见的结果。我们需要一个表格,存储所有可用的扑克室,其中每个条目也由一个表格表示。这种表示扑克表的表格的良好结构类似于以下代码段(此代码不放在文件中;它只是显示表中的条目的外观):
Code c:
  1. local testEntry = {
  2.    [1]= name,
  3.    [2]= host,
  4.    [3]= players,
  5.    [4]= maxPlayers,
  6.    [5]= smallBlind,
  7.    [6]= bigBlind
  8. }
复制代码
此表中的索引是标头的ID,这对于排序列表的函数很有用。这些条目将由客户端的通信模块创建并放入表中。这部分代码将在每次更改表中的某些内容时调用更新函数。这个更新函数随后会更新GUI。
填充按钮
接下来我们需要的东西是update(更新)函数,它从扑克室表中读取条目。下面的代码展示一段基础的update函数,此函数从表格中读取第一个MAX_TABLES条目。然后显示相应的按钮,并设置其字体字符串(如果条目存在)。否则按钮将隐藏。
Code c:
  1. Poker_TableBrowser = {} -- 此表格存储所有函数
  2. Poker_TableBrowser.Tables = {} --所有可获得的扑克表格

  3. function Poker_TableBrowser.Update()
  4.     for i = 1,MAX_TABLES do
  5.        local entry = Poker_TableBrowser.Tables[i]
  6.        local frame = getglobal(“Poker_TableBrowserTableListEntry”..i)
  7.        if entry then
  8.           frame:Show()
  9.             getglobal(frame:GetName()..”Name”):SetText(entry[1])
  10.             getglobal(frame:GetName()..”Host”):SetText(entry[2])
  11.             getglobal(frame:GetName()..”Players”):SetText(entry[3]..”/”..entry[4])
  12.             getglobal(frame:GetName()..”Blinds”):SetText(entry[5]..”-”..entry[6])
  13.             if entry.isSelected then
  14.               getglobal(frame:GetName()..”BG”):Show()
  15.             else
  16.               getglobal(frame:GetName()..”BG”):Hide()
  17.             end
  18.          else
  19.             frame:Hide()
  20.          end
  21.     end
  22. end
复制代码
稍后你将看到如果有比MAX_TABLES更多的表可用,我们可以做什么。该函数遍历所有按钮,并将其内容设置为表中条目的值。如果条目设置了字段isSelected,它将被高亮显示。
但是这个表仍然是空的,因为我们还没有进行插件通信,所以让我们用一些测试值来填充它。下面的代码生成一些伪条目,然后调用update函数。将它放在文件的末尾,稍后我们将在构建通信模块时删除它。
Code c:
  1. for i = 1,MAX_TABLES do
  2.   table.insert(Poker_TableBrowser.Tables,{
  3.      “Test Table ”..i,
  4.      “Host ”..(MAX_TABLES - i)
  5.      i % 3 + 1,--只是虚拟值
  6.      10,
  7.      i * 10,
  8.      i * 20,
  9. })
  10. end

  11. Poker_TableBrowser.Update()
复制代码

如果现在在游戏中加载插件,你会看到一个所有表格的列表。但将鼠标移到按钮上,甚至单击任何按钮都会产生错误消息,我们还没有实现很多功能。让我们从函数Poker_TableBrowser.SortTables(id)开始。

回复

使用道具 举报

快速回复高级模式
您需要登录后才可以回帖 登录 | 立即注册

本版积分规则

快速回复 返回顶部 返回列表