找回密码
 点一下
查看: 6126|回复: 9

  T的进阶教程

[复制链接]
发表于 2009-3-23 09:18:39 | 显示全部楼层 |阅读模式
                                     ——如何在T范围内完成更多
写在前面:
     这个教程应该算是对T的告别了,之后开始研究J了,一般应该不会再写跟T相关的教程或研究了
     恩对于我自己来说,我是喜欢把手上的工具发挥最大价值的,所以才会无聊的研究如何用T实现J的部分功能
     如果是其他人相信早就放弃T而学J了吧
     好吧我承认,如果用T来像J一样实现局域化和参数延时传递时很麻烦的,并且也没有什么效率,使用它完全是没有意义的
     研究它莫不如直接研究J
          不过个人爱好……大家就不必埋汰我了……
     其实这篇教程应该是还有可写的内容……
     不过也许不会更新了……
     以上废话!下面不是!
     恩,这篇教程是描述T的某些应用方法的
     并没有介绍实现什么实际的东西,
     主要的方面有动态注册简单局域化参数传递精确等待

动态注册:这个是比较常用的了,大部分在制作被动技能时会用得到。在正常情况下,如此使用是为了提高效率,因为任意开头的事件实在是效率太低了。
[trigger]
    事件
        时间 - 当游戏逝去 0.00 秒
    条件
    动作
        单位组 - 选取 ((可用地图区域) 内的所有单位) 内所有单位 (触发器 - 为 Trigger_XXX <预设> 添加事件: (单位 - unit YYY))
[/trigger]
[trigger]
    事件
        单位 - 任意单位进入 (可用地图区域)
    条件
    动作
        触发器 - 为 Trigger _XXX <预设> 添加事件: (单位 - unit YYY)
[/trigger]
上面是以单位的一个例子,实现的效果类似任意单位事件,这个事件将执行的触发是:Trigger _XXX<预设>,具体事件是:单位 – unit YYY。YYY是代表具体的事件内容,比如死亡,被攻击,受到伤害等。

可以这样使用的事件包括,单位-指定单位事件单位-进入指定单位范围(对于指定单位的注册)、单位-生命值事件单位-魔法值事件对话框-对话框被点击可破坏物-物件死亡时间-归零的计时器玩家-输入的聊天信息物品-物品被破坏

一般我们主要使用指定单位事件的动态注册来达到或者完善任意单位事件。其他事件的动态注册很少会用到。特别要说的是,因为可破坏物-地图范围中的物件死亡事件最多响应64个可破坏物,所以一般我们都是用这样的动态注册来模拟的。还有指定单位受到伤害事件,我们用事件响应-触发单位来响应被伤害的单位i,而施加伤害的应该是用事件响应-伤害来源来响应。
另外,我们在使用这样的注册方式时要注意使用对应的响应。比如指定单位事件中的单位要用事件响应-死亡单位来响应可破坏物-物件死亡要用事件响应-死亡的物件响应。具体的响应可以在触发器的说明中找到。如图中红圈的位置:
未命名.JPG
如果说明文字过多而导致部分不可见,可以通过拉大窗口的方法来使其全部显示出来。而如果是像指定单位事件中的那些选项,并没有文字说明的话,可以在事件响应里寻找,如果找不到的话,一般使用通用的就可以,如事件响应-触发单位事件响应-触发玩家……

下面是一个判断杀死熊猫全部分身时的凶手的T(正常杀死全部分身导致熊猫死亡时,凶手为NULL):

[trigger]
NewTrigger 002
    事件
        单位 - 任意单位进入 (可用地图区域)
    条件
        Or - 任意条件成立
            条件
                ((进入的单位) 的类型) 等于 大地
                ((进入的单位) 的类型) 等于 火焰
                ((进入的单位) 的类型) 等于 风暴
    动作
        触发器 - 为 NewTrigger 003 <预设> 添加事件: (单位 - (进入的单位) 死亡)
[/trigger]
[trigger]
NewTrigger 003
    事件
    条件
        (((可用地图区域) 内满足 ((((匹配单位) 的类型) 等于 大地) 且 (((匹配单位) 是存活的) 等于 TRUE)) 的所有单位) 中的单位数量) 等于 0
        (((可用地图区域) 内满足 ((((匹配单位) 的类型) 等于 火焰) 且 (((匹配单位) 是存活的) 等于 TRUE)) 的所有单位) 中的单位数量) 等于 0
        (((可用地图区域) 内满足 ((((匹配单位) 的类型) 等于 风暴) 且 (((匹配单位) 是存活的) 等于 TRUE)) 的所有单位) 中的单位数量) 等于 0
    动作
        如果(所有的条件成立) 则运行 (Then - 动作) 否则运行 (Else - 运作)
            If - 条件
                (凶手单位) 不等于 没有单位
            Then - 动作
                游戏 - 对 (所有玩家) 发送文本信息: (转换 ((凶手单位) 的类型) 为字符串)
            Else - 动作
[/trigger]
很简单的应用吧,为分身注册死亡事件!

循环跳出与越过:T并不支持循环跳出,它没有如其他编译语言中的Break、Continue这样功能的语句。而在实际应用过程中,我们有时是需要达到满足某种条件后跳出或者放弃后面动作的效果的。那么我们要怎么做呢?

FOR循环是一个计数循环(整数),每次循环变量的变化是1,那么我们就可以通过使用修改循环变量的方法来达到跳出与越过的效果。
[trigger]
    事件
    条件
    动作
        循环动作[I]从 M 到 N, 运行 (Loop - 动作)
            Loop - 动作
                如果(所有的条件成立) 则运行 (Then - 动作) 否则运行 (Else - 运作)
                    If - 条件
                        XXX
                    Then - 动作
                        Set I = N
                    Else - 动作
[/trigger]
上面的例子是一个简单的跳出循环。当满足条件XXX后,直接设置循环变量I 等于循环变量的结束值N。这样因为在下一次循环时,循环变量I已经大于了结束值N,所以循环就结束了。
而越过更简单:
[trigger]
    事件
    条件
    动作
        循环动作[I]从 M 到 N, 运行 (Loop - 动作)
            Loop - 动作
                如果(所有的条件成立) 则运行 (Then - 动作) 否则运行 (Else - 运作)
                    If - 条件
                        XXX
                    Then - 动作
                        YYY
                    Else - 动作
[/trigger]
只要条件XXX无法满足,IF中Then后面的动作都不会执行。
越过估计大家都用过,只是没有特别的思考罢了,而跳出则一般很少有人用过。跳出可以用来提高效率,特别是通过循环判断某种东西类别的时候。
[trigger]
    事件
        单位 - 任意单位 死亡
    条件
    动作
        循环动作[I]从 1 到 100, 运行 (Loop - 动作)
            Loop - 动作
                如果(所有的条件成立) 则运行 (Then - 动作) 否则运行 (Else - 运作)
                    If - 条件
                        ((死亡单位) 的类型) 等于 Unittype[I]
                    Then - 动作
                        Set number = I
                        Set I = 100
                    Else - 动作
[/trigger]

上面的触发器就是当一个单位死亡,判断这个单位的类型在Unittype单位类型数组中的序号。如果没有Set I = 100这句。触发会直接运行100次循环才结束;而加上这句,当满足((死亡单位) 的类型) 等于 Unittype[I]这个条件的时候,就会直接跳出循环。当然,如果仅仅是100次循环的话,跳出与否是无所谓的,并不差那么一点的运行效率。但是如果循环次数很大的话……不过一般也不会用到那么大的循环。

同样的用这种方法,我们也可以使一个循环不会结束。

[trigger]
    事件
    条件
    动作
        循环动作[I]从 1 到 1, 运行 (Loop - 动作)
            Loop - 动作
                如果(所有的条件成立) 则运行 (Then - 动作) 否则运行 (Else - 运作)
                    If - 条件
                        XXX
                    Then - 动作
                        YYY
                        Set I = 0
                    Else - 动作
[/trigger]
如上,这样的话,只要条件被满足,那么这个循环永远不会跳出。不过这种循环是有上限的。据zyl910大大的研究操作数300000是上限(点击这里打开)。超过后触发停止运行。

这种根据条件停止的触发一般是用在不知道循环多少次的时候。

简单局域化:
局域化,一直是T范围内的一个难题,它是使我们写的触发(T或J)可以集成使用而不会冲突的技术。为了不需要给所有可能使用到同一个系统或技能的单位与玩家各自写一个同样的T,我们必须想办法将我们的T局域化。当然,局域化主要是为了避免同时调用T产生的变量冲突而使用的,只有当有多个玩家、单位使用的T才会考虑这个方面的问题,特别是有可能会同时使用的时候。

局域化,由名可知,它是建立在局域变量基础上存在的,而不幸的是,除非使用自定义代码,我们无法使用局域变量,因此,T的局域化一直是个难题。

如果使用自定义代码,我们是可以使T局域化的,不过为了我们不需要把大部分动作转化为自定义代码,我们可以使用同名局域变量覆盖全局变量的方法来达到目的。只要我们使用这样一个局域变量声明的语句local  <类型>  <udg_变量名>就可以了,然后注册一个同名的全局变量(就是正常T使用的变量)这样我们就可以使T中的一个变量局域化了。

不过这种方法在使用中会出现一个BUG:Union Bug,(这个BUG的相关内容可以在点击这里中找到)。简单来说,就是在一个T中的多个不同的替换了全局变量的局域在赋值时会一起赋值,即使这几个变量的类型不同也会如此。这样我们在一个T中使用多个同名替换的局域变量是不行的,也许会有人说,我可以使用不与全局变量同名的局域变量啊,可是如果这个T对于这些局域变量调用的很频繁的话,那么我们写的T就与直接写J没什么区别了,特别是在使用循环的时候,更是如此。

那么还有其他办法么?答案是有!实际上T本身还是可以使用一些局域量的,这些局域变量就是那些事件响应。比如事件响应-触发单位、事件响应-触发玩家……相比于真正的局域变量,它们的缺陷是很明显的:首先是这些事件响应的运行效率比真正的局域变量要低很多,不过还是可以接受的;其次是这些局域变量是与T的事件有关的,并且事件对应的事件响应很少(一般只有一个,少数事件并没有对应的事件响应),我们不能像局域变量那样简单的定义和使用,更无法自由赋值。这第二个问题正是局域化主要的困难所在。但是无论怎样,这是唯一的办法。

以上说的都是局域化的一些基础知识,下面,我来简单的说明一下具体的思路和想法:
[trigger]
NewTrigger 001
    事件
        地图初始化
    条件
    动作
        可破坏物 - 选取 (可用地图区域) 内所有物件 (触发器 - 为 NewTrigger 001 <预设> 添加事件: (可破坏物 - (选取的物件) 死亡))
[/trigger]
[trigger]
NewTrigger 002
    事件
    条件
    动作
        等待 10.00 秒(游戏时间)
        可破坏物 - 复活 (死亡的物件) ,设置生命值为 ((最后创建的物件) 的最大生命值) 并 显示 生长动画
[/trigger]

上面的NewTrigger 002就是一个简单的局域化了的T(NewTrigger 001是为了动态注册),整个的效果是复活可破坏物。这两个T如果你真正的写在一个地图里,就会发现当树被破坏后,隔一小段时间后就会复活,而且这个复活是对应每棵树的,并不会因为在等待时间内再次调用而出现问题。这也就是局域话的效果。这个例子中的局域化是怎么实现的呢?就在于死亡的物件这个事件响应。因为它是局域的,所以不会有变量冲突。
当然这个例子是很简单的,不过即使是复杂的T也一样。

也许你会出现这样的疑问,正如我上面所说,使用这样的事件响应是有两个缺陷的,其中一个缺陷就是这样的事件响应在一个T中只能使用一个,并且不可赋值。不过我们可以利用一些办法来弥补。

大家都知道,单位是可以附带一个整数变量——自定义值的。我们可以将一些变量预先注册到数组里,通过单位的自定义值来调用,如果使用者可以用玩家来区别,那么直接使用(((单位) 的所有者) 的玩家索引号) 来获取。我们将事件响应变换成(或者说是连接到)整数变量并不是局域化关键,关键在于我们可以用这个数组来索引索引数组。通过数组来局域化。

下面是一个这样局域化的例子:
[trigger]
NewTrigger 001
    事件
        时间 - 当游戏逝去 0.00 秒
    条件
    动作
        单位组 - 选取 ((可用地图区域) 内的所有单位) 内所有单位 (触发器 - 为 NewTrigger 003 <预设> 添加事件: (单位 - (选取单位) 死亡))
[/trigger]
[trigger]
NewTrigger 002
    事件
        单位 - 任意单位进入 (可用地图区域)
    条件
    动作
        触发器 - 为 NewTrigger 003 <预设> 添加事件: (单位 - (进入的单位) 死亡)
[/trigger]
[trigger]
NewTrigger 003
    事件
    条件
        (凶手单位) 不等于 没有单位
    动作
        如果(所有的条件成立) 则运行 (Then - 动作) 否则运行 (Else - 运作)
            If - 条件
                (凶手单位) 不等于 没有单位
            Then - 动作
                Set killed[(((凶手单位) 的所有者) 的玩家索引号)] = (killed[(((凶手单位) 的所有者) 的玩家索引号)] + 1)
                漂浮文字 - 创建漂浮文字: (|cffffcc00 + (((凶手单位) 的名字) + |r)) 在 (死亡单位)的头顶, Z轴偏移 0.00,字体大小: 8.00 ,颜色值:(100.00%, 100.00%, 100.00%) ,透明度: 0.00%
                漂浮文字 - 设置 (最后创建的漂浮文字) : 禁用 永久显示.
                漂浮文字 - 设置 (最后创建的漂浮文字) 的显示时间为 5.00 秒
                漂浮文字 - 设置 (最后创建的漂浮文字) 的文字淡化时间为 2.00 秒
            Else - 动作
[/trigger]
[trigger]
NewTrigger 004
    事件
        玩家 - 玩家1(红色) 输入 -di ,信息过滤方式 完全匹配
        玩家 - 玩家2(蓝色) 输入 -di ,信息过滤方式 完全匹配
        玩家 - 玩家3(青色) 输入 -di ,信息过滤方式 完全匹配
        玩家 - 玩家4(紫色) 输入 -di ,信息过滤方式 完全匹配
        玩家 - 玩家5(黄色) 输入 -di ,信息过滤方式 完全匹配
        玩家 - 玩家6(橙色) 输入 -di ,信息过滤方式 完全匹配
        玩家 - 玩家7(绿色) 输入 -di ,信息过滤方式 完全匹配
        玩家 - 玩家8(粉红) 输入 -di ,信息过滤方式 完全匹配
        玩家 - 玩家9(灰色) 输入 -di ,信息过滤方式 完全匹配
        玩家 - 玩家10(淡蓝) 输入 -di ,信息过滤方式 完全匹配
        玩家 - 玩家11(暗绿) 输入 -di ,信息过滤方式 完全匹配
        玩家 - 玩家12(棕色) 输入 -di ,信息过滤方式 完全匹配
    条件
    动作
        游戏 - 对 (将 (触发玩家) 转为玩家组) 发送显示 10.00 秒的文本信息: (玩家已经杀死 + ((转换 killed[((触发玩家) 的玩家索引号)] 为字符串) + 个单位!))
[/trigger]

这是一个简单的系统,效果是当杀死一个单位时显示凶手单位的名字并计数,玩家可以使用-di来查看自己的杀敌数目。这个演示很简单,看起来没什么,但是它也被局域化了NewTrigger 003和NewTrigger 004两个T,被局域话的变量是死亡单位和触发玩家。

我们可以看到记录杀人数的整数变量killed是一个数组,我们分别通过((凶手单位) 的所有者) 的玩家索引号(触发玩家) 的玩家索引号来索引数组。这个就是用数组和事件响应来局域化的方法。

如果你看一些关于J的教程,也可以通过UnionBug来转换Handle,并用绑定缓存的方法,详细看下面参数传递的两个帖子吧。

参数传递:这个内容被单独提出研究了,请看T的局域化与变量传递研究演示技能——近战抵抗护盾T的延时参数传递方法,前一个使用的是单位的生命周期,后面使用了Unionbug
精确等待:就是在触发中进行精确的等待。总所周知,等待(Wait)是相当不精确的,其最小延时时间一般大约0.1秒,所以如果使用等待来做击退类的位移技能就会出现效果上的问题。解决的方法是使用计时器,或者时间 - 每当游戏逝去 2.00 秒这个事件来代替,但是需要局域化并想办法传递参数。
……
后面就没有了,如果有很重要的T相关的内容,我会附上来的。

评分

参与人数 1威望 +78 收起 理由
hydralisk + 78

查看全部评分

发表于 2009-3-23 09:22:01 | 显示全部楼层
提示:每当游戏逝去 2.00 秒《--这个事件存在恐怖的泄漏
回复

使用道具 举报

 楼主| 发表于 2009-3-23 12:04:49 | 显示全部楼层
没办法
难道用T自定义代码使用timerstart?
正常的T是不考虑泄露和效率的
因为实在考虑不了
回复

使用道具 举报

发表于 2009-4-26 17:55:58 | 显示全部楼层
話說我也想知道T的等待方面到底有啥好解決辦法
回复

使用道具 举报

 楼主| 发表于 2009-4-26 18:37:50 | 显示全部楼层
基本上是没方法
……
除非是为当前触发添加计时器到期事件
然后通过计时器来延时
通过某个全局变量来获得执行到的部分
比如整数i
当i=0时执行什么,
i=1时执行什么……
用IF区分
不过这样的触发无法局域化
除非用存储系统……
不过除了缓存,用存储系统都是J的范围了……
回复

使用道具 举报

发表于 2009-4-26 19:09:17 | 显示全部楼层
t的等待就是用了一个计时器,然后每0.1秒判断计时器是不是到期。。。
回复

使用道具 举报

发表于 2011-2-19 01:29:21 | 显示全部楼层

Re:&nbsp;&nbsp;T的进阶教程

真是和这个帖子相见恨晚啊,和我用t的习惯完全一样,早看了它一个月前就该有这个思想了,呵呵,虽然t很局限,但是还是很直观嘛~~
回复

使用道具 举报

发表于 2015-8-11 14:13:46 | 显示全部楼层
演示链接全部失效
回复

使用道具 举报

发表于 2015-8-11 14:14:09 | 显示全部楼层
演示链接全部失效
回复

使用道具 举报

发表于 2017-9-10 17:37:49 | 显示全部楼层
萌新表示看不懂
回复

使用道具 举报

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

本版积分规则

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

GMT+8, 2024-12-25 13:32 , Processed in 0.091946 second(s), 25 queries .

Powered by Discuz! X3.5

© 2001-2023 Discuz! Team.

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