找回密码
 点一下
查看: 17383|回复: 27

1.5UI系列教程第一部——使用触发器来完全控制游戏界面元素~~让我们来制造一个永远开启状态的背包栏吧~~

[复制链接]
发表于 2012-6-28 17:57:18 | 显示全部楼层 |阅读模式
本教程预计会分3-4篇,这里是入门篇:

简单来说,1.5允许你使用触发器来动态修改几乎所有的游戏界面元素,把它们全部当成触发器对话框控件来进行操作。而这第一部就是教你如何这样做的。这是这一系列教程中最简单,但同时也可能是最长的一篇。


本教程假设你已经了解所有有关“对话框模板”的基础知识,至少知道如何使用UI模板来创建模板对话框控件。比如,知道如何创建一个CommandPanel。

在本教程中并不需要创建对话框模板,但是懂得正确创建对话框模板的知识,才能更容易看懂本篇教程。(不过这是系列第一部,所以超简单的啦)

就算你已经具备了这些基础知识,为了让这个教程尽可能的易于理解,希望大家先记住一条结论,我把它称为

头目的UI公理:在UI层面,触发器对话框和游戏界面是平权的。

简单来说,构成触发器对话框的元素和构成游戏界面的元素并没有任何本质区别。在UI层面,所有适用于游戏界面的规则全部适用于触发器对话框。反过来亦然

事实上,我们甚至可以在TriggerDialogFrame.SC2Layout文件里看到构成触发器对话框的那些界面模板。(本篇内不会涉及,但是会在第二集中派上用场)


虽然,如果我只是单纯地讲解步骤,这篇教程大概可以很短,不过我向来认为,了解原理再看一两个例子,远比不了解原理看十个例子有效的得多。所以我并不只希望大家看了这个教程以后能做出我演示图中的效果,更希望大家了解其中的原理。

了解原理,举一反三,才是学习编辑器的捷径。那么先看一下这张图。




因为考虑到还得给老外发一次,所以我干脆给上面每个地方都标注了英文。

上图说明了触发器、触发器对话框、界面元素这三者之间的关系,是我根据自己的经验和理论总结而成,它可能不是100%标准,但完全可以帮助你以正确地思路来了解其中的运作机制。图片中部用虚线框起来的部分就是触发器对话框的完整结构。

你可以看到,其实除了界面部分外,触发器对话框还存在一个“触发器数据”部分,它存在于脚本层而不是界面层。它的存在只是为了方便触发器来控制对话框UI。


界面元素映射


实际上,触发器并无法直接控制界面,只有游戏逻辑本身可以做到。你可以看到,在图中,触发器并没有直接指向界面层的箭头。它只能通过对脚本层的一些数据进行读写来达到间接控制界面的目的。而真正去使用这些数据来对界面做出更改的,是游戏逻辑本身。

每一个对话框控件的界面部分都有一组与之对应的触发器数据。我们使用的各种操作对话框的触发器动作,实际上都是对触发数据部分进行的操作,然后游戏逻辑则会实际地使用这些数据,去操作和这组数据关联的界面元素。


但要注意的是,“触发器数据”这东西对一个界面元素来说,其实并不是必要的东西,只有对话框控件有这些东西。普通的界面是不存在与之对应的触发器数据的。

——反过来说,如果我们给一个UI元素安上对应脚本层触发数据,那么它就会变成一个对话框控件!从而能被触发器所控制。


而1.5之所以允许我们用触发器来操作大部分的元素,原因也很简单,并不是因为它增加了一大堆控制界面的函数。虽然1.5加入了不少新的触发器控件和对话框类动作,但最关键的一个新增函数只有一个:

[codes=galaxy]
native int      DialogControlHookupStandard (int type, string inTemplate);
[/codes]

对应的触发器动作为“Action - Hookup Standard Dialog Item”(目前暂无中文版),它允许我们为指定的界面元素创建对应的触发数据,从而将其映射成一个“对话框控件”,令触发器能够操纵它们。

那么这个函数怎么用呢?第一个参数是你希望把目标界面元素映射成什么类型的对话框控件,第二个参数指定你想要操作的界面路径。这个函数将返回一个整数,作为新建立的对话框控件的句柄。你也可以在这个动作之后立刻使用“最后创建的对话框项”来获得这个句柄。

以下详细说明两个参数:

界面路径参数(inTemplate)

我们知道,所有的官方界面元素都是GameUI这个界面框架的一部分,因此第二个参数用来指定被映射的具体界面元素的路径。我们可以在各个SC2Layout文件中看到各个界面元素的相对路径,不过要注意的是,此处需要设置的是该界面元素的绝对路径。即其相对于GameUI的路径。

例如,我们若要映射单位按钮面板上的第一个按钮(即CommandButton00这个界面元素),我们不能直接填写“CommandPanel/CommandButton00”,而必须从填写其从GameUI开始的绝对路径,由于CommandPanel本身是GameUI.SC2layout中明确定义的一个元素,因此我们的CommandButton00的绝对路径应该是

"UIContainer/ConsoleUIContainer/CommandPanel"+"CommandPanel/CommandButton"=

UIContainer/ConsoleUIContainer/CommandPanel/CommandButton

+1.jpg

控件类型参数(type):

要明白的是,正如刚才说的,触发器不能直接控制UI。这同时也意味着,他无法读取界面的数据。甚至不知道一个指定的界面元素到底是什么类型的。

当你使用触发器新建对话框项的时候,由于已经指定了控件类型,因此游戏程序就在UI层绘制一个对应类型的界面元素。同时,脚本层会新建一个一个与之对应触发器数据的结构,让它单向连接到这个元素,然后把你填写的控件类型丢到触发器数据里的控件类型这一块里去。此后,触发器会就会把这个控件当成触发器数据中所保存的类型来对待。

但我们现在要做的,是希望为一个现存界面新建对应的触发数据,因此,我们需要手动给出控件类型,触发器才能知道需要把目标界面元素当成何种控件来操作。

但我们都知道,对话框控件的类型数量是有限的,而SC2Layout文件中的界面元素的类型数量则多达数百种。那么,这是否说明触发器可以操作的界面元素是有限的呢?

不是的,接下去我们就要引入UI类型中的“继承”和“多态”概念了:

实际上,和数据编辑器里的“类”类似,界面类型之间也存在继承关系。子类型会继承父类型的所有属性和操作方法,并可能增加一些自己独有的属性和操作方法。例如,Frame类型的界面是所有其它界面类的祖先,因此任何界面类型都包含Frame类型的所有属性(如Height Width Anchor Enabled Visable)。而诸如CommandButton之类的按钮都是Button类的子孙类。实际上,界面类型之所以有数百种之多,大多是因为游戏逻辑层需要根据界面元素的类型来决定其背后的控制逻辑,实际上绝大多界面类型都只是单纯地继承了一些基础界面类型,而没有增加任何新的属性。

在进行触发器映射时,任意的界面元素都可以被映射为自身的当前类型,或是自身类型的任意一个父类。因此,所有界面元素都可以被映射为Frame类型(即触发器中的面板控件类型-Panel)。

但要注意的是,当一个界面元素被映射为它的父类时,触发器就只能对其进行和这个父类相关的操作,而无法对其进行该界面类型所独有的操作。

例如,如果我将一个图片映射为Frame类型,那么你就只能设置它的尺寸等属性,无法设置其图片属性。如果我将一个按钮映射为Frame,那么我将无法捕捉它的点击事件——因为触发器不认为它是一个按钮。

要注意的是,这一“多态”映射并不会使被映射的界面元素本身降级,在UI层,按钮还是那个按钮,只是触发器认为它是个Frame。

由于每个界面元素最多只能被映射一次,因此如果我们想要实现对某个界面元素的最大控制,那么就需要将其映射为最接近的类型。但在实际操作中,我们只需要视乎需求来设置就好。

在这里尚无法列出完整的界面类继承树。但基本上,只要是按钮都可以映射为Button类型,所有图片都可以映射为Image类型,等等。当然,任何界面都可以映射为Frame类型。




操作和读写界面元素的注意事项


一旦映射成功,我们就可以像操作普通的对话框控件一样,使用触发器来操作被映射的界面元素,并捕捉它们的各种事件。

但在对界面元素进行操作时,我们仍有几点注意事项:

    1]使用触发器来“读取”到的界面数据是不可信的。正如上文所言,触发器实际上无法直接读取界面元素的属性数据,它仅仅是将“上一次设置的值”记录了下来。因此如果目标界面元素的尺寸、文字、图片等属性被游戏逻辑给修改了,触发器是无法得知的。事实上,就算在1.5以前,使用过对话框的同学可能就已经发现过一个奇怪的现象,在对话框系列函数中有一个判断对话框是否可见的函数,但是若你在设置对话框的可见性之前就使用这个函数,这个函数将永远返回空并报错。这一点进一步证明了触发器对话框和普通界面元素实质上是相同的这一推论。(1.5加入了一个全新的单独函数以正确地返回指定的对话框控件是否可见)

    2]部分界面元素可能关系到游戏的核心逻辑,因此在操作时请注意安全。

    3]在SC2layout文件里被标注为Internal的界面元素无法被直接映射。但这类元素并不多。(实际上可以有办法解除这一限制,但这作为UI系列教程的第一部,我们暂且不会接触这一块,因为实在太偏了。)

    4]所有被映射的对话框控件都被认为是包含在同一个虚拟的对话框中,我们可以获取这个对话框本身的句柄,并在其中创建新的控件。



作为一个例子,我们现在来实际地尝试对一些官方界面元素的操作吧。



操作实例:永远保持开启状态的物品栏背包和单位指令面板按钮事件的捕捉


“永远开启的物品栏背包”——这可是许许多多地图作者梦寐以求的功能。

什么?你问一个永远打开的背包面板能派什么用场?要知道,有了这个东西,就相当于我们的界面上多了最多6x8x8=384个技能栏,而我们还可以随时调整和分配这些技能栏所包含的技能。这相比以前一页最多15个技能,而且还无法动态调整的单位按钮面板可是方便+自由太多了。

有了这个我们甚至都不再需要动态添加和删除技能的功能。


接下来,我们就来实现这个伟大的背包界面吧——而这甚至只是界面元素映射最最基础的应用。

首先,既然我们希望控制英雄的背包界面的现实和隐藏状态,我们自然得先把这个东西映射为一个对话框控件。这个演示英雄的第一个背包界面作为例子。它的绝对路径为"UIContainer/ConsoleUIContainer/InventoryPanel/ContainerPanel00"

[trigger]
Hook Up Inventory Panel
    Events
    Local Variables
    Conditions
    Actions
        Dialog - Hooks up an existing Panel in the standard UI called "UIContainer/ConsoleUIContainer/InventoryPanel/ContainerPanel00"
        Variable - Set InventoryPanel = (Last created dialog item)
        Dialog - Hooks up an existing Button in the standard UI called "UIContainer/ConsoleUIContainer/InventoryPanel/ContainerPanel00/Close"
        Dialog - Hide (Last created dialog item) for (All players)
        Dialog - Disable (Last created dialog item) for (All players)
        Dialog - Hooks up an existing Button in the standard UI called "UIContainer/ConsoleUIContainer/InventoryPanel/InventoryButtons/Button00"
        Variable - Set ContainerButton = (Last created dialog item)
        Dialog - Move ContainerButton to (60, 60) relative to Right of dialog for (All players)
[/trigger]

我们将背包界面映射为Panel(即Frame)类型,这是因为我们只需要关心背包界面的尺寸和显示/隐藏状态,因此只需要它最低限度的属性。然后用InventoryPanel这个变量来保存它。

此处我们还映射了另外两件东西,一个是这个背包界面右上角的“关闭”按钮。并将其隐藏并禁用掉,这样玩家就无法通过点击关闭按钮来关闭背包界面了(直接修改SC2Layout文件也可以达到这一目的,但是这里特别作为一个使用触发器来控制界面元素的例子出现)。另一个被映射的是这个背包界面所对应的背包按钮,由于这个按钮可以被用来打开和关闭背包,因此我们同样需要对其进行操作,但是我们不能单纯地像禁用关闭按钮一样禁用背包按钮,因为背包按钮的可见状态和可用状态是直接与背包界面挂钩的,因此这里我们先把它移动到屏幕外,让玩家无法点击到它,然后再捕捉它的快捷键事件,并强制显示背包界面,以避免玩家通过点击背包按钮的快捷键来关闭背包。

想要用触发器将背包界面直接显示出来是非常简单的,由于我们已经将其映射成了一个对话框,因此只要使用“显示对话框控件”动作就可以了。但若我们希望它一直显示,无法被关闭,就需要使用一些法子了。一般来说,我们可以每0.0625秒设置一次对话框控件可见,但是这样的触发器太浪费执行效率了。比较科学的方案是捕捉所有可能造成背包界面可见状态改变的事件,然后在动作中重新显示它。

[trigger]
Display Inventory Panel
    Events
        UI - Player Any Player presses None key Down with shift Allow, control Allow, alt Allow
        Unit Selection - Any Unit is Selected by player Any Player
        Unit Selection - Any Unit is Deselected by player Any Player
    Local Variables
    Conditions
    Actions
        Dialog - Show InventoryPanel for (All players)
[/trigger]

由于普通单位不带物品栏,所以在发生选择事件的时候,有可能导致背包界面关闭,因此我们加入了任意单位选择和取消选择事件。另外,同时选中英雄和普通单位时,如果按tab键切换到普通单位子组,也会导致背包关闭。因此这里还加入了任意按键事件,这一事件同时也把背包按钮的快捷键也包含进去了。

执行了这些操作以后,一个永远开启的背包界面就已经完成了,很简单吧。

我在附件中加入了一个演示,展示了永远开启的背包。只要你选中了英雄,这个背包就永远都不会关闭。虽然演示中只操作了一个背包,但你可以对其它五个背包进行如法炮制,甚至可以用触发器或者修改SC2Layout把所有背包的物品格子全部合并到一起,实现可以完全自由定义和操作的384格超级技能栏。

另外,这个演示里还加入了使用触发器来映射界面元素的其它例子:使用触发器来捕捉任意一个单位技能栏按钮的事件。包括鼠标移入按钮、移出按钮、点击等等事件。你也可以试着对这些界面元素进行各种乱七八糟的修改以查看修改效果:)




视频看不了的可以下载去看:
http://bbs.islga.org/Downloads/Renee/EverOpenInventory.wmv

演示在附件里




下集预告:在今后的1.5UI系统的系列教程中,我将进一步深入讲解触发器对话框和界面元素,下一期的主题是对界面元素的高级操作。我将在下一期里讲解如何实现动态更改物品和技能的提示信息(两个相同类型的物品也可以显示不同的提示信息哦,这是随机属性物品系统最重要组成部分),以及如何用触发器来创建和控制华丽的按钮冷却效果。

HookUpSample.SC2Map

115 KB, 下载次数: 531

 楼主| 发表于 2012-7-4 01:06:51 | 显示全部楼层
终于完成了!

这还只是第一篇……一想到这系列后面还有三篇~~以及还翻译成英文发给老外们学习就觉得很无力啊~~


而且最近事情好多~~距离完成目标遥遥无期啊~~

点评

后面的三篇写完了吗?怎么找不到呢。。。~~~~(>_  详情 回复 发表于 2014-11-12 23:10
回复

使用道具 举报

发表于 2012-7-4 01:14:26 | 显示全部楼层
头目已经完成了UI类的解析了吗?
回复

使用道具 举报

发表于 2012-7-4 01:18:42 | 显示全部楼层
虽然不懂,但还是近距离一下
回复

使用道具 举报

发表于 2012-7-4 03:29:07 | 显示全部楼层
前排支持下

内容来自[手机版]
回复

使用道具 举报

发表于 2012-7-4 07:22:16 | 显示全部楼层
貌似界面重载白看了?。。。
回复

使用道具 举报

发表于 2012-7-4 08:05:21 | 显示全部楼层
我晕,貌似新技术哎。
回复

使用道具 举报

发表于 2012-7-4 09:03:29 | 显示全部楼层
头目辛苦了
回复

使用道具 举报

发表于 2012-7-4 09:36:03 | 显示全部楼层

回 麦德三世 的帖子

麦德三世:终于完成了!

这还只是第一篇……一想到这系列后面还有三篇~~以及还翻译成英文发给老外们学习就觉得很无力啊~~


....... (2012-07-04 01:06)
   加油
回复

使用道具 举报

 楼主| 发表于 2012-7-4 10:42:20 | 显示全部楼层
之前的视频格式有点问题,重新转码了一遍。
回复

使用道具 举报

发表于 2012-7-4 10:58:21 | 显示全部楼层
期待后面的教程~~~~~~~~
回复

使用道具 举报

发表于 2012-7-4 20:45:19 | 显示全部楼层
前排学习知识 头目v587
回复

使用道具 举报

发表于 2013-1-18 09:51:07 | 显示全部楼层
mark!!!
回复

使用道具 举报

发表于 2014-11-12 23:10:56 | 显示全部楼层
麦德三世 发表于 2012-7-4 01:06
终于完成了!

这还只是第一篇……一想到这系列后面还有三篇~~以及还翻译成英文发给老外们学习就觉得很无力 ...

后面的三篇写完了吗?怎么找不到呢。。。~~~~(>_<)~~~~
回复

使用道具 举报

发表于 2014-11-15 03:35:01 | 显示全部楼层
  1.         if(n == 1) return A[0];
  2.         
  3.         int maxPre = A[0];
  4.         int minPre = A[0];
  5.         int result = A[0];
  6.         
  7.         for(int i = 1; i < n; ++i) {
  8.             int maxCopy = maxPre;
  9.             maxPre = max(max(A[i] * maxPre, A[i]), A[i] * minPre);
  10.             minPre = min(min(A[i] * maxCopy, A[i]), A[i] * minPre);
  11.             result = max(result, maxPre);
  12.         }
  13.         
  14.         return result;
复制代码
回复

使用道具 举报

发表于 2014-11-15 17:41:08 | 显示全部楼层
发现一个问题,现在不选中单位时,无法显示那个单位的物品栏了,是不是游戏更新封杀这一技术了?
回复

使用道具 举报

发表于 2015-1-31 20:31:32 | 显示全部楼层
视频看不了
回复

使用道具 举报

发表于 2015-1-31 20:38:52 | 显示全部楼层
基本上理解了,
回复

使用道具 举报

发表于 2015-4-13 21:33:32 | 显示全部楼层
本帖最后由 solon58552 于 2015-4-14 07:45 编辑

看懂了头目的教程和演示,我试图对这个东西有更深的了解,所以打开编辑器仔细看了一下控制界面UI和物品容器这一块,我发现物品容器和UI里面的代码没有直接的关联,那UI的代码是怎么影响到物品容器的样式和结构的?
比如我想改动默认物品栏在屏幕上的显示位置,容器和物品里面都没有有关参数,在UI代码里面看到了类似关于锚点的参数,但是因为搞不清各种containerpanel和容器之间代码的生效关系,不敢随便修改.
后来用xml查看了容器项目的代码,发现了"ItemContainer"这个类,貌似是某种界面模板的派生,但是我无法在UI界面下找到这种结构的定义.
所以怎么制作新的物品栏UI,比如附着修改位置,更改结构乃至细节?
回复

使用道具 举报

发表于 2017-11-5 15:20:29 | 显示全部楼层
新手,过来学习
回复

使用道具 举报

您需要登录后才可以回帖 登录 | 点一下

本版积分规则

Archiver|移动端|小黑屋|地精研究院

GMT+8, 2024-11-14 09:29 , Processed in 0.274902 second(s), 26 queries .

Powered by Discuz! X3.5

© 2001-2023 Discuz! Team.

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