找回密码
 点一下
查看: 3000|回复: 10

数据编辑器+触发器混合实现英雄经过物品时物品自动进包。

[复制链接]
发表于 2011-3-4 03:07:52 | 显示全部楼层 |阅读模式
刚才发了个数据编辑器版本,是通过在英雄的指令队列之前插入一个拾取指令实现的,不过若是要不给英雄发布指令让物品立刻进包呢,那数据编辑器就实现不了。要靠触发器。

不过目前,触发器里并不存在可以直接把一个物品给英雄的函数(玻璃渣说以后会加但是还不知道啥时候)。只有一个直接在英雄物品栏里创建物品的函数,这个我们不能用因为我们希望是让已有的物品进包而不是创建一个新的。

所以,需要做个变通的方案,也就是创建临时单位,临时单位拥有一个拾取距离和丢弃距离都是500的物品栏,让临时单位拾取物品,然后再命令临时单位把物品交给英雄,这样英雄是被动接受物品,就不会接到额外的命令了。数据编辑器里由于无法发布“把物品交给英雄”这样的命令,所以必须要用到触发器。


不过就算是如此,我们也不用全程使用触发器实现这个效果。毕竟galaxy虽然比起jass快的多,但是它的执行效率还是要比数据编辑器低多了。因此混合使用数据编辑器和触发器可以让效果实现得更完美。




一 事件的设计


首先一点,捕捉英雄经过物品的事件的问题。

由于物品也是单位,我们固然可以使用单位接近指定单位XXXX距离的事件。但是这里有个问题,该事件作为中心的单位必须是指定的,虽然galaxy中绝大部分特定事件都可以填null来变成全局事件,但它是个少有的特例。因此我们真的要用这个事件的话只有靠动态注册。但是就算是这样,由于任何单位接近任何单位都会触发事件,这个触发器被触发的频率就太高了,每次触发我们都得进行一次判断,看看接近的单位是不是英雄被接近的单位是不是我们想要它能自动进包的那类物品。这样的做法就会很土。

所以这地方我们完全可以借助数据编辑器。可以考虑的方案有两个。1是技能,2是行为。

若是用技能,那么给我们的物品一个会自动释放的技能,会对自己周围指定半径内任何符合我们的英雄条件的单位释放,那么我们只要捕捉这个技能的释放事件就行了。施法者就是物品,目标就是英雄。注意,SC2的物品也是单位,所以SC2的物品是可以自己施放技能的,大家千万不要不要被war3的限制给限制住了思维。

若是用行为,给我们物品一个buff,会周期性地搜索周围区域然后对区域中符合条件的(找到一个就行)英雄产生一个毫无影响的效果,比如提高生命最大值0点什么的,只要能被触发器捕捉就行了。那样触发器捕捉这个效果事件,施法者就是物品,目标就是英雄了。

技能和行为各有好处,用技能的话,由于你只需要一个效果类技能,甚至不需要制作实际的效果,自动释放,目标过滤什么的也都能在单一的一个模板里实现,所以制作起来方便。而用行为的话,会需要一个区域搜索效果还要一个触发事件用的伪效果,所以需要3个模板。但是好处是行为是可以动态随时附加和删除的。所以两边各有好处。我这个演示里用的是自动释放的技能来触发事件。大家可以按照实际情况来作些修改的。


二 包包是否已满的条件判断


然后下面这部分是,关于物品栏已满的判断。考虑到我们想要实现的东西的具体原理,我们势必需要一个“物品栏已满”的判断。因为我们毕竟不是让英雄自己去拾取,而是让临时单位去拾取,然后扔给英雄。那么如果那时候英雄身上东西满了,物品显然就会留在隐藏单位的身上了。

所以这里需要进行一个物品栏已满的判断,(其实就算物品栏没满,物品的类型和剩下的物品栏需求不符还是照样没法交给英雄)所以简单地说,就是必须判断英雄到底能不能拾取这件物品呢。触发器里固然是有办法判断的。但是如果你的包包满了,靠近地上的物品时还是会不断触发技能事件,于是我们每次都得进行条件判断看看英雄到底能否拾取这件物品。那样还是很浪费脚本效率的。最理想的状况是一个事件捕捉到后我们根本不需要进行任何条件判断,可以直接执行下去了,把所有的条件判断都交给数据编辑器去做。

数据编辑器要怎么判断英雄能否拾一件物品呢?虽然从没出现过具体的例子,但是答案其实是可以……那就是CValidatorUnitOrder这个类,从来没有任何实际的用法例子,不论是官方还是网上。但是经过我测试这其实是一个超好用的验证器:判断单位当前能否执行某个命令——但是却不实际地去发布这个命令或者执行它。比方说有时候你想知道某个技能的cd、魔耗之类的要求是否已经满足条件了你可以用这个验证器,而不需要实际地去发布那个技能命令。

于是那就好了,我们在物品身上的那个用来触发事件的自动施放技能上加一个自动释放验证:目标可以对施法者发布拾取命令。于是一切就OK!

轻松解决了。


注:这个验证器的做法大家可以打开演示来查看。


三 动作


上面既然说了事件和条件,那么第三项自然是动作。具体实现我们需求的动作说起来非常简单。基本上就是以下三步

1,创建隐藏的临时单位
2,让临时单位拾取施法者(也就是物品)
3,让临时单位把施法者交给施法目标单位(也就是英雄)

这里对临时单位有不少要求:拥有一个拾取范围和丢弃范围都是500的特殊物品栏(这样就移动过去拾取和丢弃的时间就没掉了)、无法转向(这可以转身去拾取物品的时间就没掉了,和前一个合起来可以实现瞬间拾取\丢弃) 、无敌、隐身、无碰撞(而且碰撞地图是“空中”)、无威胁、无死亡事件(免得触发器捕捉到它的死亡事件,又多一些无谓的判断)、不绘制、无法选中、无法作为目标、无分数、无被杀计数等等之类的。反正都是可以在单位编辑器的flag里勾出来的。总之尽量就是减少它在地图上的存在感,就像是根本没这个单位一样就圆满了。所以我这个演示里是直接新建了一个单位模板来当临时单位的。

当然实际上,这个临时单位根本不需要actor。只是1.3.0里的debug工具会在没有actor的单位创建的时候刷提示信息,所以我干脆给它加了个无模型的actor了。


然后一般来讲,当我们交给临时单位做的事情都可以瞬间完成的时候,为了节省资源,我们通常会使用单一的一个隐藏单位。然后它来拾取所有的物品,交给所有对应的英雄,这样我们完全可以省去创建隐藏单位这步,因为拾取和转交物品都可以瞬时完成,所以我们只需要在地图上放一个隐藏单位就够所有人用了。可以有效地利用单位资源。但是这个是教程,为了更清楚地体现出设计原理,我这里使用的是一个物品对应一个临时单位的方案,在事件触发时创建对应的临时单位。只做让他们做两件事之后就干掉它们。但是在实际应用过程中,推荐大家还是使用更节省资源的单一隐藏单位。

然后这里又出现难点了,首先我们要发布的第一个命令:拾取。大概就会有几个同学卡住,不知道怎么做。但是这些肯定是少数。不过我们要发布的第二道命令:让临时单位把自己物品栏里的物品交给英雄这个指令,相信知道怎么做的同学非常少。

——因为实际上用触发器GUI根本就做不到。这是必须用自定义脚本来实现的。而且Galaxy构造指令的方挺复杂的。像“让临时单位把自己物品栏里的物品交给英雄”这个指令其实涉及了三个实体:临时单位、物品、英雄。默认的GUI只能操作两个实体。

所以这个演示里也用到了自定义脚本来实现这个指令的发布。不过这里顺便要说的是,我打算在GAx3 Mod里强化一下GUI的指令构造,这样大家以后就可以用GUI来构造涉及三个实体的命令了,比如英雄对某点\某单位使用某个物品,英雄把物品栏里的某物品交给另一个英雄,之类的。


四 完整的触发器


由于大量条件判断都由数据编辑器实现了,所以我们所需要写的触发总共就是以下这几句。
[trigger]
Give Item to Hero
    Events
        Unit - Any Unit uses  Collect at Generic3 - Execute stage (Ignore shared abilities)
    Local Variables
        Item = (Triggering unit) <Unit>
        Hero = (Triggering ability target unit) <Unit>
        Dummy = No Unit <Unit>
        PickOrder = ( Load targeting (Triggering unit)) <Order>
        DropOrder = ( Unload (Tauren Outhouse) targeting Hero) <Order>
    Conditions
    Actions
        General - Custom Script: OrderSetTargetItem(lv_dropOrder,lv_item);
        Unit - Create 1 Dummy Unit for player 1 at Point 001 facing 270.0 degrees (Ignore Placement)
        Variable - Set Dummy = (Last created unit)
        Unit - Order Dummy to PickOrder (Replace Existing Orders)
        Unit - Order Dummy to DropOrder (After Existing Orders)
[/trigger]

注意以下,第一指令的发布方式是“替代现有指令” (Replace Existing Orders)。这个会清除单位身上的指令队列,然后把新的指令发过去。一般来说刚创建的单位身上也不会有任何指令,不过这是保险起见。
第二个指令的发布方式是"跟在现有的指令之后"(After Existing Orders),也就是说在指令队列的末尾插入一个指令。这样第二个指令“把物品交给英雄”就会在第一个指令“拾取物品”执行完毕之后再执行了。这就是指令队列,和我们用Shift键发布的指令队列是一样的。


五 垃圾清理


范蠡和韩信都说过:"蜚鸟尽,良弓藏。狡兔死,走狗烹。"

我们自然不能让已经完成了任务的临时单位们一直呆在内存中空耗资源咯。要知道SC2的单位占的资源可是很多的,毕竟有数量如此之多的单位属性和自定义属性在那里。虽然galaxy本身有个垃圾回收机制,我们在编写触发器和galaxy的时候完全不用考虑war3时那种“清理点”、“清理特效”、“清理某某某”之类的问题,因为它们会自动消失。不过单位这东西,可不是放在那里就会凭空消失的——也不应该如此,假设你造了200人口兵,一段时间不去动它们它们就自动被从内存中删除了,那这游戏还玩什么啊!

所以临时单位的清理还是要自己动手的。不过上面既然都已经利用了这么多次的数据编辑器了,我们不妨便让删除临时单位这活儿也交给数据编辑器做得了。首先考虑,我们这些临时单位什么时候可以“烹之”了呢。显然,是把物品交出去以后,对吧。反正这个临时单位活着只是为了接受两个命令。而且我们在触发器中可以看到,这个单位一创建就立马被发布了两个指令。

于是我们完全可以在单位编辑器里给临时单位放一个天生buff。这个buff会在被移除时删除这个单位本身。而移除验证器里面填“施法者拥有指令”。因为天生buff的施法者都是单位自己,所以当临时单位身上没有任何指令的时候(两个指令都执行完了),它就会自杀并移除自己了。

我们也省得用触发器来做什么延时删除(等待足够长的时间,可以确保临时单位能完成所有的指令,然后再删除)这么麻烦。轻松方便。




最后的附:


——但是,其实这里真的需要延时删除吗?大家可以试下,在我这个演示的触发的最后,直接加上一句“删除临时单位”的动作。大家会发现物品还是能正常进入包包。因为前面我对临时单位进行了各种特殊设置,以至于拾取和转交这两个指令都是瞬时完成的,发布完这两个指令之后马上就可以直接删了临时单位了。所以那个“等单位身上的指令队列空掉以后就删除自己”的buff在这里根本就是无必要的。
但是作为一个演示,我还是这样做了,毕竟大家学习的时候需要能举一反三。这个拾取和转交物品的例子正好能使得所有指令能在瞬间完成。但是显然,并不是所有的技能和指令都是可以瞬间完成的。这个“等单位身上的指令队列空掉以后就删除自己”的清理临时单位方案虽然在这个例子里是不必要的,但是大家肯定可以在其余地方用到它。尤其是当你的临时单位需要执行一连串的无法瞬间完成的指令的时候。


另外,我这个演示里还有个被禁用的触发,触发名叫Debug。如果打开它的话,它就会在任意单位被删除的时候显示“Removed Successfully.” 这是我用来Debug临时单位是否已经被正常删除用的。毕竟这个临时单位是看不见的。





好了说了一大堆,大家可以下演示了。演示在附件里。
Screenshot2011-03-04 00_56_21.jpg

Item.SC2Map

18 KB, 下载次数: 60

 楼主| 发表于 2011-3-4 03:25:48 | 显示全部楼层
这里上传另外一个版本。

这个版本顶楼的版本不同的地方就是使用单一的隐藏单位来做中介,一开始就放在地图上,全程不删除。而不是每个物品对应一个临时单位。


[trigger]
Give Item to Hero
    Events
        Unit - Any Unit uses  Collect at Generic3 - Execute stage (Ignore shared abilities)
    Local Variables
        Item = (Triggering unit) <Unit>
        Hero = (Triggering ability target unit) <Unit>
        PickOrder = ( Load targeting (Triggering unit)) <Order>
        DropOrder = ( Unload (Tauren Outhouse) targeting Hero) <Order>
    Conditions
    Actions
        General - Custom Script: OrderSetTargetItem(lv_dropOrder,lv_item);
        Unit - Order Dummy to PickOrder (Replace Existing Orders)
        Unit - Order Dummy to DropOrder (After Existing Orders)
[/trigger]

由于没了创建单位,触发器也变得更短了。





说起来之所以上面的演示是用每个物品对应一个临时单位而不是用单一的隐藏单位,还有另一个原因就是我打算把“直接把物品物品给英雄”的自定义函数加入GAx3 Mod,实现原理就是这帖子这样。由于是通用的自定义函数,所以是不能用全局施法者的,只能自己创建临时单位并删除之了。

GiveItemToHero.SC2Map

17 KB, 下载次数: 29

回复

使用道具 举报

发表于 2011-3-4 13:12:07 | 显示全部楼层
這個比數據版的那個好多了
至少不用老是神奇的回眸一笑了~
回复

使用道具 举报

发表于 2011-3-4 16:29:47 | 显示全部楼层
头目郝萌。
回复

使用道具 举报

发表于 2011-3-4 21:38:08 | 显示全部楼层
这个折腾啊
咋感觉还不如触发器写个物品系统呢
回复

使用道具 举报

发表于 2011-3-4 21:55:52 | 显示全部楼层
自己写物品系统也存在捡取物品的问题吧,折腾无非是头目实现的要求比较苛刻一些而已。

不过教程里有很多扩展性的技术和技巧,真是大开眼界。
回复

使用道具 举报

发表于 2011-3-4 21:57:50 | 显示全部楼层
膜拜头目~
因为很不了解数据编辑器所以想问一下galaxy为什么比数据编辑器慢很多呢?
回复

使用道具 举报

 楼主| 发表于 2011-3-4 22:10:58 | 显示全部楼层
因为这就是事实啊。

galaxy是解释型的脚本语言,脚本语言的效率都是很差的,而数据编辑器严格来说是个数据库。所有类的内容都是事先编译好的。而且galaxy很多地方需要同步,在联机时比较耗费效率。

你可以试试看我以前发的那个纯数据编辑器版的致命一击。如果用触发器写绝对的卡死你。


实际上单兵AI文件里的注释就写着。在可能的情况下,尽量使用数据版的单兵AI,因为galaxy的效率比较低。




但是反正比jass高就是了。
回复

使用道具 举报

发表于 2011-3-4 22:14:04 | 显示全部楼层
哦~谢谢头目。
于是搜索资源去。
回复

使用道具 举报

 楼主| 发表于 2011-3-4 22:44:18 | 显示全部楼层
即便是用数据编辑器写AI也比用galaxy快~~

//    //  Reaper AI is left in TactTerrAI to serve as an example of two different ways to write the same
//    //  AI.  To see the xml equivalent of the Reaper AI, look in TacticalData.xml and TargetFindData.xml.
//    //  In general, AI implemented in galaxy scripts will be slower than AI implemented in data.
回复

使用道具 举报

发表于 2011-4-14 13:05:27 | 显示全部楼层
数据编辑器可以做这么多事情 看来要好好研究一下
回复

使用道具 举报

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

本版积分规则

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

GMT+8, 2024-4-27 14:03 , Processed in 0.244381 second(s), 21 queries .

Powered by Discuz! X3.5

© 2001-2023 Discuz! Team.

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