找回密码
 点一下
查看: 13203|回复: 16

【原创】JASS技能教程

[复制链接]
发表于 2009-2-15 10:12:33 | 显示全部楼层 |阅读模式
(被自己忍无可忍的和谐掉了)



一.    初涉JASS

1.JASS,何谓JASS?套用everguo的话说,就是:
JASS是魔兽3的程式语言,用於控制游戏和地图的进行,也是魔兽游戏和地图的基础。 地图编辑器中摆放的单位(Unit),不规则区域(Region) ,触发(Trigger)……等,最终都会被翻译成JASS语言存在地图档里,在游戏时被使用。JASS在语法结构上比较接近Basic,同时也引用了许多C的东西。如果读者有接触过这二种编程语言,相信一定能很快上手!
(PS:请不知道 何谓编程语言 的人自动略过。)
2. 为什么要学习JASS:
某些功能只靠GUI Trigger无法完成, 必须用JASS来实现。例如对指定玩家播放音效,或者替单位加上永不消失的被动物品技能等。
      JASS可以定义区域变数及自订的函数,增加设计的便利性,也提供更简单可行的演算法。GUI虽然能完成几乎所有的功能,但是对於记忆体释放的能力太差,容易增加电脑不必要的负担。用JASS可以写出比GUI效率更高的程式码,对执行速度有不小的帮助。
(血戮小评:GUI:就是触发器功能。GUI对内存泄露只能用 【全局变量(就是Ctrl+B打开的东东了)】 来解决,但是全局变量太多了,会让人烦的。
举例:
[trigger]
对战初始化
    事件
        地图初始化
    条件
    动作
        设置 point = ((可用地图区域) 的中心点)
        单位 - 创建 1 个 步兵 给 玩家1(红色) 在 point ,面向角度为 默认建筑朝向 度
        点 - 清除 point
        自定义代码: set udg_point = null
[/trigger]
这里就用了一个 点变量 。而 单位组变量 则经常用于在各个触发器中传递数据。
拿技能来说就是,每个技能都可能有一个单位组变量,这就很难看了。
小提示:一般来说,高于5个全局变量的东东,很多人就直接放弃不看了。)
3.先来看看一个JASS吧。
[codes=jass] function Trig_A_Actions takes nothing returns nothing
    set udg_point = GetRectCenter(GetPlayableMapRect())
    call CreateNUnitsAtLoc( 1, 'hfoo', Player(0), udg_point, bj_UNIT_FACING )
    call RemoveLocation( udg_point )
    set udg_point = null
endfunction

//===========================================================================
function InitTrig_A takes nothing returns nothing
    set gg_trg_A = CreateTrigger(  )
    call TriggerAddAction( gg_trg_A, function Trig_A_Actions )
endfunction [/codes]
这就是上面那个触发器的翻译成JASS的东东了。
4.了解JASS的基础(来自eGust):
女生看的(没有其他选择):
请看 JASS培训班教程.rar (4.95 MB, 下载次数: 4150) (附件)的【JASS教程——基础指南——jass使用教程(eGust)】
(请务必研究并看完这一页教程,如果不看的话,以下的教程将进行不下去的…………)
当然,如果你年龄够大,或自制力丰富,且没有人在你看教程时,审查你,还有,你是个男的,那么,强烈向你推荐
【JASS教程——基础指南——[月协出品]JASS教程-变量篇】
【JASS教程——基础指南——[月协出品]JASS教程-函数篇】
首先给你打YD预防针:
这些JASS教程,均出自月协会长之手,他以天马行空的手法给我们展现了一个奇妙荒诞的JASS世界,让我们在大笑过后不但消除了对JASS的恐惧更增进了对JASS的了解。在第一卷讲变量时,他以三名妓女来分别比喻全局变量、局部变量和全局变量数组,并在篇末讲解了一种通过“阉割全局变量”,让技能可以多人使用的方法;在第二卷讲函数时,以嫖客和妓女为例讲解函数的调用,情节丝丝入扣,顺带介绍了一种快速查看地图代码的小技巧;在第三卷讲GameCache时,以木子美的性爱日记为参照讲解GameCache的使用,更把GameCache跟Return Bug和Timer的关系比作淫乱的3P...
(你要能忍受这个片段,那么就可以看  [月协出品]JASS教程-变量篇 和 [月协出品]JASS教程-函数篇 了)

(所有的变量或指针都会有一个自己的地盘用来存储自己的数据,而这个地盘就全部在内存里)


(顺便说一下,每创建一个handle实例,war3就会为这个handle实例在内存里分配一块地方,每个地方都有一个特殊的独一无二的地址,电脑可以通过这个地址来访问这个地方,继而获得这个handle实例)
(地址是一个integer)
(给你们个图看:
PIC1-2.jpg
举例:上面的100就是下面的单位实例的编号)
(而变量则可以保存这个地址(此时他就指向了handle实例):
PIC1-3.jpg
,函数调用这个变量,然后函数就去这个指针指向的地点(标号为100的地址),就获得了这个handle(单位)的实例,弄个比喻就是:
有个帮忙给观众找到自己位置的美女志愿者,她现在脑子里空空的,然后,来了个观众。观众把观众的门票给了这个美女志愿者,美女志愿者看这张门票,这门票上写着:座位号:100,然后她就带这个观众,到了100号这个座位。当然这个比喻不太恰当忽视就可以o(∩_∩)o…)
(举例:
[codes=jass]local unit u = CreateUnit(Player(0),’hfoo’,10,10,0)
SetUnitX(u,0)[/codes]其实就是:
声明一个局部变量u,在内存中为他分了块地,变量地址假设为300,然后创建了一个单位(并给他分了块地,假设单位内存中的地址为100),并把他赋给了u,那么现在:
PIC1-4.jpg
然后,SetUnitX调用了u,看到u的包含的值其实是100,就去内存中的地址为100的这个地方,就获得了这个单位的实例——一个步兵,然后又将这个步兵的X坐标变成了0,结束。)


//LZ专用分割线………………………………………………………………………………
(整个教程的文字版我也放在这个LZ贴里了)

JASS技能教程.txt

73 KB, 下载次数: 474

评分

参与人数 2威望 +151 收起 理由
crazyforyou + 150
扎克斯fair + 1

查看全部评分

 楼主| 发表于 2009-2-15 10:14:42 | 显示全部楼层

2

【小试身手】好,现在我们就来接触一下JASS。
问:怎样弄出来一个有 随机数值 伤害的 风暴之锤?

你肯定说:
[trigger]
未命名触发器 001
  事件
    单位 - 任意单位 发动技能效果
  条件
  动作
    如果所有条件成立则做动作1,否则做动作2
      If - 条件
        (施放技能) 等于 风暴之锤
      Then - 动作
        单位 - 命令 (触发单位) 对 (技能施放目标) 造成 (随机实数,最小值: 12.00 最大值: 55.00) 点伤害,攻击类型: 法术 伤害类型: 普通
      Else - 动作
[/trigger]
是这样吧。
但是转换成JASS以后是什么样呢?
[codes=jass]
function Trig____________________001_Func001C takes nothing returns boolean
    if ( not ( GetSpellAbilityId() == 'AHtb' ) ) then
        return false
    endif
    return true
endfunction

function Trig____________________001_Actions takes nothing returns nothing
    if ( Trig____________________001_Func001C() ) then
        call UnitDamageTargetBJ( GetTriggerUnit(), GetSpellTargetUnit(), GetRandomReal(12.00, 55.00), ATTACK_TYPE_NORMAL, DAMAGE_TYPE_NORMAL )
    else
    endif
endfunction

//===========================================================================
function InitTrig____________________001 takes nothing returns nothing
    set gg_trg____________________001 = CreateTrigger(  )
    call TriggerRegisterAnyUnitEventBJ( gg_trg____________________001, EVENT_PLAYER_UNIT_SPELL_EFFECT )
    call TriggerAddAction( gg_trg____________________001, function Trig____________________001_Actions )
endfunction
[/codes]
首先,我们从上面看起:
if XXXXXXXXXX then
   XXXXXXXX
endif
这不是if....then....endif语句吗?
恩恩,这个在教程里学了,就是:
如果 怎样怎样 那么
怎样怎样
如果不是
怎样怎样
结束
not....这个好像不清楚啊~~~
这个not就是:
如果不是 怎样怎样
(求反)的意思。
然后
GetSpellAbilityId()就是
获得【释放技能的ID】,其实就是在物品编辑器中按Ctrl+D看到的内容(在左边列表里)
'AHtb'就是风暴之锤的ID了。
return false
就是返回【假】
return true
就是返回【真】
仔细看一下,是不是有些写的繁琐呢?
那我们来简化一下:
[codes=jass]
function Trig____________________001_Func001C takes nothing returns boolean
    return (GetSpellAbilityId() == 'AHtb')==true
endfunction



其实呢,这个==true也可以去掉,因为“GetSpellAbilityId()=='AHtb'”就已经是个boolean了,如果再加一个==true反而有些画蛇添足
那么就可以这样写:
function Trig____________________001_Func001C takes nothing returns boolean
    return GetSpellAbilityId() == 'AHtb'
endfunction
[/codes]
【PS:请大家记住GetSpellAbilityId这个函数,他在技能的制作中是一个使用极为频繁的函数】
然后再看:
[codes=jass]function Trig____________________001_Actions takes nothing returns nothing
    if ( Trig____________________001_Func001C() ) then
        call UnitDamageTargetBJ( GetTriggerUnit(), GetSpellTargetUnit(), GetRandomReal(12.00, 55.00), ATTACK_TYPE_NORMAL, DAMAGE_TYPE_NORMAL )
    else
    endif
endfunction[/codes]
这个 Trig____________________001_Func001C() 到底是什么东东?
往上看,不就是包含GetSpellAbilityId()=='AHtb'的函数吗?
不过GetSpellAbilityId()=='AHtb'和Trig____________________001_Func001C()都是一样的内容,可是Trig____________________001_Func001C()却多出了3行,罪不可赦,把他给和谐了!

把GetSpellAbilityId()=='AHtb'直接替换到if ( Trig____________________001_Func001C() ) then的Trig____________________001_Func001C()这里。
就变成了:
[codes=jass]
function Trig____________________001_Actions takes nothing returns nothing
    if ( GetSpellAbilityId() == 'AHtb' ) then
        call UnitDamageTargetBJ( GetTriggerUnit(), GetSpellTargetUnit(), GetRandomReal(12.00, 55.00), ATTACK_TYPE_NORMAL, DAMAGE_TYPE_NORMAL )
    else
    endif
endfunction

//===========================================================================
function InitTrig____________________001 takes nothing returns nothing
    set gg_trg____________________001 = CreateTrigger(  )
    call TriggerRegisterAnyUnitEventBJ( gg_trg____________________001, EVENT_PLAYER_UNIT_SPELL_EFFECT )
    call TriggerAddAction( gg_trg____________________001, function Trig____________________001_Actions )
endfunction
[/codes]
那么下面的
[codes=jass]
        call UnitDamageTargetBJ( GetTriggerUnit(), GetSpellTargetUnit(), GetRandomReal(12.00, 55.00), ATTACK_TYPE_NORMAL, DAMAGE_TYPE_NORMAL )
[/codes]
是什么意思呢?
首先,这个UnitDamageTargetBJ的字面意思好像是:
Unit--单位
Damage--伤害
Target--目标(英文通用译法为靶子,不过在war3里就是目标的意思)
BJ--Blizzard JASS(这个东东的中文意思…………应该都知道吧…………)
(你看暴雪这些人,真是不厚道,弄个函数还把自己公司名写进去,真是不懂得尊重他人…………%——%)
我们再来看看这个BJ函数(即后缀为BJ的所有函数)的真身:
[codes=jass]function UnitDamageTargetBJ takes unit whichUnit, unit target, real amount, attacktype whichAttack, damagetype whichDamage returns boolean
    return UnitDamageTarget(whichUnit, target, amount, true, false, whichAttack, whichDamage, WEAPON_TYPE_WHOKNOWS)
endfunction[/codes]
o(∩_∩)o...原来如此,Blizzard你真会做广告,原版的不就多了一个[codes=jass]WEAPON_TYPE_WHOKNOWS[/codes]这个东东吗   &——&   直接把“return UnitDamageTarget(whichUnit, target, amount, true, false, whichAttack, whichDamage, WEAPON_TYPE_WHOKNOWS)”先Copy到Trig____________________001_Actions里面去,再来对比一下:
[codes=jass]function Trig____________________001_Actions takes nothing returns nothing
    if ( GetSpellAbilityId() == 'AHtb' ) then
        call UnitDamageTargetBJ( GetTriggerUnit(), GetSpellTargetUnit(), GetRandomReal(12.00, 55.00), ATTACK_TYPE_NORMAL, DAMAGE_TYPE_NORMAL )
        return UnitDamageTarget(whichUnit, target, amount, true, false, whichAttack, whichDamage, WEAPON_TYPE_WHOKNOWS)[/codes]
恩恩,这个whichUnit应该就是上面的GetTriggerUnit()了,然后target就是GetSpellTargetUnit(),把他们(GetTriggerUnit()和GetSpellTargetUnit())覆盖到whichUnit和target上
(为什么要这样覆盖呢?为什么不能把whichUnit和target覆盖到GetTriggerUnit()和GetSpellTargetUnit()上呢?因为whichUnit和target只是一个储存东东的变量或参数,而你在Trig____________________001_Actions里既没有给他们声明(变量和参数都需要声明才能使用,因为变量和参数都要储存一个值,那么它就需要地方来放这个值,但变量他没有这块地方,得你通过声明这个方式才能让他获得存储的地方),也没有给他们赋值,那怎么能加到把whichUnit和target加到Trig____________________001_Actions这里去呢?)。
接下来amout是什么意思?去上面找找,好像是UnitDamageTargetBJ的第三个参数,嗯…………应该就是我们要给那个目标的伤害,也就是“GetRandomReal(12.00, 55.00)”,,true,false...两个Boolean值....不知道,不管他,看后3个东东,恩…………whichAttack,whichDamage,最后就是WEAPON_TYPE_WHOKNOWS这是什么东西?哦对了老狼版的触发器好像有一条:
PIC2-1.jpg
原来是这样。是不是远程伤害,当然选是了。恩恩………………攻击类型…………这个好像跟伤害时的伤害系数计算有关…………恩…………那就改成【混乱】型吧,这样就不会有什么伤害的额外或减免的伤害了,恩……再来看伤害类型……………………哦哦哦哦哦哦,居然有这么多类型!!哦哦哦哦哦哦,心花怒放ingo(∩_∩)o...o(∩_∩)o...o(∩_∩)o...:
PIC2-2.jpg
那我们就选一个【力量】吧。
装甲类型…………好像只是音效而已…………就选无吧。
然后,我们点击:
PIC2-3.jpg
T转J。
这个功能转换成JASS好像是:
[codes=jass]call UnitDamageTarget( GetTriggerUnit(), GetSpellTargetUnit(), GetRandomReal(12.00, 55.00), true, true, ATTACK_TYPE_CHAOS, DAMAGE_TYPE_FORCE, WEAPON_TYPE_WHOKNOWS )[/codes]
这个啊………………
算了,直接拿这个Copy过去好了。
那现在就是:
[codes=jass]function Trig____________________001_Actions takes nothing returns nothing
    if ( GetSpellAbilityId() == 'AHtb' ) then
        call UnitDamageTarget( GetTriggerUnit(), GetSpellTargetUnit(), GetRandomReal(12.00, 55.00), true, true, ATTACK_TYPE_CHAOS, DAMAGE_TYPE_FORCE, WEAPON_TYPE_WHOKNOWS )
    else
    endif
endfunction

//===========================================================================
function InitTrig____________________001 takes nothing returns nothing
    set gg_trg____________________001 = CreateTrigger(  )
    call TriggerRegisterAnyUnitEventBJ( gg_trg____________________001, EVENT_PLAYER_UNIT_SPELL_EFFECT )
    call TriggerAddAction( gg_trg____________________001, function Trig____________________001_Actions )
endfunction[/codes]
那个else貌似没有作用啊…………和谐了。
最后来看
[codes=jass]
function InitTrig____________________001 takes nothing returns nothing
    set gg_trg____________________001 = CreateTrigger(  )
    call TriggerRegisterAnyUnitEventBJ( gg_trg____________________001, EVENT_PLAYER_UNIT_SPELL_EFFECT )
    call TriggerAddAction( gg_trg____________________001, function Trig____________________001_Actions )
endfunction
[/codes]
这个………………恩……
CreateTrigger(  )好像是创建触发器的意思…………
这个TriggerRegisterAnyUnitEventBJ好像是  触发器  注册  任意 单位 事件 再来个BJ…………
哦,这应该就是
【任意单位 事件】
的原型了。
然后再看,好像是为gg_trg____________________001也就是刚刚创建的触发器,添加一个[codes=jass]EVENT_PLAYER_UNIT_SPELL_EFFECT[/codes]的东东
【PS:EVENT_UNIT_SPELL_EFFECT也是事件,不过应用于 指定单位 事件 里其实,有_PLAYER的就是  玩家单位  事件 和 任意单位  事件 所使用的EVENT,这些不可混淆】
【PS:[codes=jass]
EVENT_UNIT_DEATH                   //死亡
EVENT_UNIT_CHANGE_OWNER           //改变所有者
EVENT_UNIT_DAMAGED                 //接受伤害( 请大家用这个替代EVENT_UNIT_ATTACKED,当然有点麻烦,但是没有BUG )
EVENT_UNIT_HERO_LEVEL              //提升等级
EVENT_UNIT_HERO_SKILL              //学习技能
EVENT_UNIT_USE_ITEM                //使用物品
EVENT_UNIT_PICKUP_ITEM             //获得物品
EVENT_UNIT_DROP_ITEM               //丢弃物品
EVENT_UNIT_ATTACKED                //被攻击( 这个请大家一定不要使用,这个有BUG,攻击单位还未给予目标单位伤害,就触发了这个event )
EVENT_UNIT_SUMMON                  //召唤一个单位
EVENT_UNIT_SPELL_CHANNEL           //准备释放技能
EVENT_UNIT_SPELL_CAST              //开始释放技能
EVENT_UNIT_SPELL_ENDCAST           //停止释放技能
EVENT_UNIT_SPELL_EFFECT            //发动技能效果
EVENT_UNIT_SPELL_FINISH            //释放技能结束
EVENT_UNIT_ISSUED_TARGET_ORDER     //发布指定物体命令
EVENT_UNIT_ISSUED_POINT_ORDER      //发布指定点命令
EVENT_UNIT_ISSUED_ORDER            //发布无目标命令
这些都是出现频率比较高的EVENT,大家可以看一看。
[/codes]】
应该就是 发动技能效果了
然后TriggerAddAction应该就是 为触发器添加动作的意思了。
恩………………再来整理一下吧:
[codes=jass]function Trig____________________001_Actions takes nothing returns nothing
    if ( GetSpellAbilityId() == 'AHtb' ) then
        call UnitDamageTarget( GetTriggerUnit(), GetSpellTargetUnit(), GetRandomReal(12.00, 55.00), true, true, ATTACK_TYPE_CHAOS, DAMAGE_TYPE_FORCE, WEAPON_TYPE_WHOKNOWS )
    endif
endfunction

//===========================================================================
function InitTrig____________________001 takes nothing returns nothing
    set gg_trg____________________001 = CreateTrigger(  )
    call TriggerRegisterAnyUnitEventBJ( gg_trg____________________001, EVENT_PLAYER_UNIT_SPELL_EFFECT )
    call TriggerAddAction( gg_trg____________________001, function Trig____________________001_Actions )
endfunction[/codes]

当然了,根据老狼的著名研究,把Action放到Condition里,效率会有几倍的提升,那么再辛苦一下:
[codes=jass]function Trig____________________001_Actions takes nothing returns boolean
    if GetSpellAbilityId() == 'AHtb' then
        call UnitDamageTarget( GetTriggerUnit(), GetSpellTargetUnit(), GetRandomReal(12.00, 55.00), true, true, ATTACK_TYPE_CHAOS, DAMAGE_TYPE_FORCE, WEAPON_TYPE_WHOKNOWS )
    endif
    return false
endfunction

//===========================================================================
function InitTrig____________________001 takes nothing returns nothing
    set gg_trg____________________001 = CreateTrigger(  )
    call TriggerRegisterAnyUnitEventBJ( gg_trg____________________001, EVENT_PLAYER_UNIT_SPELL_EFFECT )
    call TriggerAddCondition( gg_trg____________________001, Condition( function Trig____________________001_Actions ) )
endfunction[/codes]
再废口舌一下:
Action如何改Condition
1.把Action的returns由nothing改为boolean
2.Action的最后,和所有的return,都加上/改为return false
3.在Action的最前头,加上:if <你的Condition> then
在最后(但不是在清空变量前)加endif
4.把下面的InitTrig_XXXXXXXXXXX中的:
call TriggerAddCondition………………这一行删掉。

把call TriggerAddAction( gg_trg_XXXXXXXXXXX, fucntion XXXXX )改为
call TriggerAddCondition( gg_trg_XXXXXXXXXXX, Condition( fucntion XXXXX ) )

PS:非real/integer/string/boolean类型变量,需要在函数结束前加一句:
set XXX = null
至于为什么,可以自己去别的区看看。
我也有贴提到:《【环保大师】教你“环保”(排泄+提高效率+一点小建议)》(综合区)

二.    ReturnBug
首先,我们来看ReturnBug最核心函数H2I
[codes=jass]function H2I takes handle h returns integer
return h
return 0
endfunction[/codes]
怎么样看不懂吧,从字面意思上讲,H2I好像就是把一个handle的值转换成integer类型的值。
首先,我们说一下War3的检查机制:
War3检查函数时,只会检查一个函数中最后一个return *****(在H2I中就是return 0)
的*****(0)的类型是否与function ………… returns XXXXX(returns integer)
的XXXXX(integer)这个类型相符合。
但是呢,实际运行情况是这样的:
某某函数调用了H2I——》》传递给了他一个handle类型——》》H2I开始运行——》》
第一行:return h
——》》H2I函数结束、并返回一个integer的值——》》某某函数继续运行
看出来了吧,return 0根本没有执行,原因是War3函数执行时,一遇到return,就会退出这个函数,执行调用他的函数下一条命令行。
而这个handle值赋给了integer,大家会问了:这会出问题吧,很可惜的是,你们猜错了,这个函数执行后,那个handle自己所占有的地方的独一无二的地址编号,就被我们揪出来了(很神奇吧)
那么,我们再来看另外一个最常用的ReturnBug函数:
[codes=jass]function I2U takes integer i returns unit
return i
return null     //null = 没有单位、没有触发器、没有字符串(跟空字符串是不同概念)、没有………………
endfunction[/codes]
试着自己分析一下,为什么这个函数在war3检查时不会报错。然后再想想这个函数运行时的步骤。







答案:
returns unit 的unit 与最后一个return null 的null相符合,所以没有报错。
某某函数调用了I2U——》》传递给了他一个integer类型——》》I2U开始运行——》》
第一行:return i
——》》I2U函数结束、并返回一个unit的值——》》某某函数继续运行。
你又要问了,不是integer类型吗?怎么可以赋到unit里去?回答是完全没有错误,但前提是你要把正确的数传递给I2U。
而这个正确的数…………又跟H2I有关系了。
记得教程里说什么吗?有integer,real,boolean,string,code以及handle。
先概括一下除了integer,real,boolean,string,code这几个类型以外,那么不管那个变量的类型是什么,他的原类型就是handle,这个类型只不过是“进化”而来的,而所有handle的指针排列都分在同一块内存的大区域(内存内分几个“种族”一是【局部变量族】,占了一块地方,二是【全局变量族】,也占了一块地方,三是【函数族】,又瓜分了一块地方,四是【实例族】…………分了很多,我就不一一说了,貌似某大大在某大大贴的某小小行到某大大行说了这个某大大小小的问题。PS:如果everguo在的话,肯定有用nP比喻的欲望…………)里。然后我们再来讲一下handle的编号。
比如说你先用CreateUnit()创建了一个单位,那么他所占有的地方的编号就是(假设)100,然后你又用InitGameCache()创建了一个游戏缓存,那么这个游戏缓存所占有的地方的编号是101,然后你又用Location(0,0)创建了一个点,那么这个点所占有的地方的编号就是102,然后,你又用RemoveUnit()把创建的单位给弄没了,那么地址为100这个地方就空出来了(也就代表这个编号为100的地址/地域没主人了),此时你又用CreateTrigger()创建了一个触发器,那么这个触发器所占有的地方就是地址为100的那片疆域了了,很神奇吧!

那么,如果我们又创建了一个单位,他所在地址就是103了(因为100、101、102都有人了嘛~~),然后我们写一个这个东东
[codes=jass]local integer i = H2I(< the unit>)
call BJDebugMsg(I2S(i))[/codes]
然后你会发现屏幕上显示的是
“103”
(因为是举例,所以假一点高手不要说我)

然后,I2U的功能是将这个H2I的功能反过来的说~~

差不多是:H2I得到handle所在地址,然后I2***再通过这个地址进行回访,获得这个地址的主人~~~

也就是,你用H2I(传递的handle的类型为unit)得到的数,再带进I2U里,那么得到的就是你刚刚给H2I传递的unit了,恩恩:
[codes=jass]globals
gamecache udg_gc = InitGameCache(“Kill.w3v”)
endglobals
function B takes nothing returns nothing
local unit target = I2U(GetStoredInteger( udg_gc , “TheTarget” , “His Handle Address”))
call KillUnit(target)
call FlushStoredInteger( udg_gc , “TheTarget” , “His Handle Address”)
//……………………
//some codes here.
//……………………
endfunction
function A takes nothing returns nothing
call StoreInteger( udg_gc , “TheTarget” , “His Handle Address” , H2I(CreateUnit(Player(0),'hfoo',0,0,0)) )
//……………………
//some codes here.
//……………………
endfunction[/codes]


如果你先运行A,然后等待2秒,再运行B,那么你就会发现那个在A中创建的步兵,死了。

这意味着什么呢?就是单位可以变换一种存在形式,装换为integer的形式暂时保存在某些地方,然后在适合的时候,再进行转回工作,就又变成了原先的单位了。
(当然,既然H2I是takes handle,那么所有的handle类型都可以像unit一样进行handle—>integer—>handle,而这个integer,我们)

为什么不用单位的自定义值来为

那么,你会想:用全局变量储存这些integer的值吗?NONO,这样那就失去变换handle值为integer值的意义了。


//L2专用分割线………………………………………………………………………………
回复

使用道具 举报

 楼主| 发表于 2009-2-15 10:17:30 | 显示全部楼层

3

我们的帮手是————

【GameCache】

!!!

这个GameCache到底是什么东东呢?

先给你们看一些函数:

(这是从老狼那Copy过来的,把跟技能没关的函数全踢走了)

[codes=jass]native InitGameCache takes string campaignFile returns gamecache
创建游戏缓存

native StoreInteger takes gamecache cache, string missionKey, string key, integer value returns nothing
native StoreReal takes gamecache cache, string missionKey, string key, real value returns nothing
native StoreBoolean takes gamecache cache, string missionKey, string key, boolean value returns nothing
native StoreUnit takes gamecache cache, string missionKey, string key, unit whichUnit returns boolean
native StoreString takes gamecache cache, string missionKey, string key, string value returns boolean
保存缓存数据

native GetStoredInteger takes gamecache cache, string missionKey, string key returns integer
native GetStoredReal takes gamecache cache, string missionKey, string key returns real
native GetStoredBoolean takes gamecache cache, string missionKey, string key returns boolean
native GetStoredString takes gamecache cache, string missionKey, string key returns string
native RestoreUnit takes gamecache cache, string missionKey, string key, player forWhichPlayer, real x, real y, real facing returns unit
读取缓存数据

native FlushGameCache takes gamecache cache returns nothing
删除并清空整个缓存
native FlushStoredMission takes gamecache cache, string missionKey returns nothing
删除并清空某个缓存分类

(下面的东东个人感觉没太大用)
native FlushStoredInteger takes gamecache cache, string missionKey, string key returns nothing
native FlushStoredReal takes gamecache cache, string missionKey, string key returns nothing
native FlushStoredBoolean takes gamecache cache, string missionKey, string key returns nothing
native FlushStoredUnit takes gamecache cache, string missionKey, string key returns nothing
native FlushStoredString takes gamecache cache, string missionKey, string key returns nothing
清空某个缓存项所储存的指定数据类型的数据,但并不会删除缓存项 [/codes]
注意!!
在使用GameCache之前,最好在前面加一句:
[codes=jass]call FlushGameCache( InitGameCache( <your File> ) )[/codes]
就是说不能直接用InitGameCache(),最好能这样改一下:
[codes=jass]function DebugInitGameCache takes string campaignFile returns gamecache
    call FlushGameCache( InitGameCache( campaignFile ) )
    return InitGameCache( campaignFile )
endfunction[/codes]
只要想创建一个GameCache,就最好用这个DebugInitGameCache,否则可能会有上一次的缓存未清空,而覆盖这次缓存数据的事发生(我就在这上面吃了亏,血的教训啊~~)。

恩恩…………其实GameCache就是个数据库,用来存放东西的~~~~

怎样存东西呢??

一般来说,用StoreXXXX的话,都会跟ReturnBug函数连在一起。

比较标准的格式:

[codes=jass]call StoreReal( udg_GC, I2S( H2I( <a handle> ) ), <the key>, < value > )

//.......some codes here

local real r = GetStoreReal( udg_GC, I2S( H2I( <the handle> ) ), <the key>)[/codes]

比方说,我要每4秒,存储一个单位的现有生命值,对上一次(4秒前)存储的生命值进行比较,每有100点生命值的差距,对这个单位造成40点的伤害,再造成上一次对这个单位造成的伤害一样数值的伤害(这一次总伤害=(生命差距/100)*40+上一次的伤害)。
共造成3次伤害。
那么,我们可以这样做:
[codes=jass]globals
    gamecache db_gc
endglobals

function Init takes nothing returns nothing
    call FlushGameCache( InitGameCache( "db.w3v" ) )
    set db_gc = InitGameCache( "db.w3v" )
endfunction
//先运行

function H2I takes handle h returns integer
    return h
    return 0
endfunction

function I2U takes integer i returns unit
    return i
    return null
endfunction

function DeBuffAbility_Action takes nothing returns nothing
    local timer ti = GetExpiredTimer()
    local string ti_h = I2S(H2I( ti ))
    local integer time = GetStoredInteger( db_gc, ti_h, "Time" )
    local unit u = null
    local real life = 0.0
    local real old_life = 0.0
    local real damage = 0.0
    if time > 3 then
        call FlushStoredMission( db_gc, ti_h )
        call DestroyTimer( ti )
        set ti = null
    else
        set u = I2U(GetStoredInteger( db_gc, ti_h, "Unit" ))
        set life = GetUnitState( u, UNIT_STATE_LIFE )
        set old_life = GetStoredReal( db_gc, ti_h, "UnitLife" )
        if old_life - life > 0.0 then
            set damage = GetStoredReal( db_gc, ti_h, "Damage" )
            set damage = I2R( R2I( old_life - life ) / 100 ) * 40.0 + damage
        endif
        call SetUnitState( u, UNIT_STATE_LIFE, life - damage )
        call StoreReal( db_gc, ti_h, "UnitLife", life )
    endif
    set u = null
    set ti = null
endfunction

function DeBuffAbility takes unit u returns nothing
    local timer ti = CreateTimer()
    local string ti_h = I2S(H2I( ti ))
    call StoreInteger( db_gc, ti_h, "Unit", H2I( u ) )
    call StoreReal( db_gc, ti_h, "UnitLife", GetUnitState( u, UNIT_STATE_LIFE ) )
    call TimerStart( ti, 4, true, function DeBuffAbility_Action )
    set ti = null
endfunction
//技能函数[/codes]

这个GameCache+ReturnBug的结合运用,使你可以把任何一个Handle/Code类型的变量的地址作为MissionKey,把剩下的一个名目作为Key来存储一些数据。也就是说,一个Handle/Code类型的实例,你可以通过GameCache+ReturnBug,对它捆绑一个指定含义的数据,以便你在其它地方使用这些数据。

对于那些使用GUI Trigger的人来说,这是不可能的…………
(我说的不包括专门改造过的GUI)

看到了timer,不知道大家知不知道这个timer的含义呢?
没错,就是GUI中的计时器。
至于everguo说的此timer非彼计时器的鬼(?)话,大可不必去信,timer就是计时器,计时器就是timer。
唯一的差别在于,
GUI的计时器,【开启计时器】只有三个参数:
计时器,是否循环,时间。
但Jass的timer,它的TimerStart,则是有四个参数:
timer,boolean,real,code。
timer:计时器,boolean:是否循环,real:时间,code:当到时间(每次循环都触发一次)时,执行这个code变量指向的takes nothing returns nothing的一个函数。
(code:函数指针类型 用于指向内存中的函数地址,只不过只能指向takes nothing returns nothing/boolean的函数)
一般来说,所有的传递给默认JASS函数的code变量,都是“function ”+你要指向的那个函数。
Trigger的Action是code,所有的condition和boolexpr都是通过Condition()将code转变的,timer到时后的执行函数是code,ForGroup的函数还是code。
code函数的参数为无,他真正的参数是通过各种各样的接口函数:如GetExpiredTimer(),GetTriggerUnit()………………传递的。
而code函数的参数取决于code传递到的那个函数(传递给Trigger则是看Trigger的Event(事件))。
TimerStart的参数只有一个:GetExpiredTimer()
就是【获得到时的计时器】的意思。

而我们通过这个timer,就可以从GameCache庞大的数据中找到我们所要的东西啦~~
比如刚才那个DebuffAbility,就存储了单位、单位上一次的生命、这个timer运行了几次等等的数据。

当然,可能上面这个演示看的你眼花缭乱,但是我为大家编了一个比较简单的演示:
改版随机伤害风暴之锤。
集合了GameCache+ReturnBug+Timer,大家可以参考。
我好像以前曾经发过这个东东,所以现在就长话短说吧:
原版随机伤害的风暴之锤,是在单位释放技能的时候就给予目标伤害。这是很不合理的。
但是现在我们有了timer这一利器,我们又可以从释放单位与目标距离和锤子的速率(以距离/秒为单位)算出锤子到达目标的时间。那么用timer将伤害拖后就行了。
函数如下:
[codes=jass]globals
    gamecache xlgc = null
endglobals

function H2I takes handle h returns integer
    return h
    return 0
endfunction

function H2S takes handle h returns string
    return I2S(H2I(h))
endfunction

function GetGC takes nothing returns gamecache
    if xlgc == null then
        call FlushGameCache(InitGameCache("XLGC.w3v"))
        set xlgc = InitGameCache("XLGC.w3v")
        return xlgc
    endif
    return xlgc
endfunction

function SetTimerUnit takes timer t, unit u, integer number returns nothing
    call StoreInteger(GetGC(),H2S(t),"Unit"+I2S(number),H2I(u))
endfunction

function GetTimerUnit takes timer t, integer number returns unit
    return GetStoredInteger(GetGC(),H2S(t),"Unit"+I2S(number))
    return null
endfunction

function GetTimerReal takes timer t, string key returns real
    return GetStoredReal(GetGC(),H2S(t),key)
endfunction

function DistanceBetweenUnits takes unit uA, unit uB returns real
    local real dx = GetUnitX(uB) - GetUnitX(uA)
    local real dy = GetUnitY(uB) - GetUnitY(uA)
    return SquareRoot(dx * dx + dy * dy)
endfunction

function Bolt_GetFlyTime takes unit u,integer ability_level, unit target returns real
    return DistanceBetweenUnits(u,target) / 200.00
endfunction

function Bolt_GetDamage takes unit u,integer ability_level,unit target returns real
    local real damage = I2R(GetHeroStr(u,true))*ability_level
    set damage = damage*250-I2R(GetHeroStr(target,true))
    set damage = damage*GetRandomReal(1,1.33)
    return damage
endfunction

function Bolt_Timer_Action takes nothing returns nothing
    local timer ti = GetExpiredTimer()
    local unit u = GetTimerUnit(ti, 0)
    local unit target = GetTimerUnit(ti, 1)
    local real damage = GetTimerReal(ti, "Damage")
    call UnitDamageTargetBJ( u, target, damage, ATTACK_TYPE_NORMAL, DAMAGE_TYPE_NORMAL )
    call CreateTextTagUnitBJ( I2S(R2I(damage)), target, 4, 10, 100, 0, 0, 0 )
    call SetTextTagVelocityBJ( bj_lastCreatedTextTag, 64, GetTimerReal(ti,"UnitFacing") )
    call SetTextTagPermanent( bj_lastCreatedTextTag, false )
    call SetTextTagLifespan( bj_lastCreatedTextTag, 5 )
    call SetTextTagFadepoint( bj_lastCreatedTextTag, 4 )
    call FlushStoredMission(GetGC(),I2S(H2I(ti)))
    call DestroyTimer(ti)
    set ti = null
    set u = null
    set target = null
endfunction

function Bolt_Spell takes unit u,integer ability_level,unit target returns nothing
    local real BoltFly_time = Bolt_GetFlyTime(u,ability_level,target)
    local real damage = Bolt_GetDamage(u,ability_level,target)
    local timer ti = CreateTimer( )
    call SetTimerUnit(ti,u,0)
    call SetTimerUnit(ti,target,1)
    call StoreReal(GetGC(),H2S(ti),"Damage",damage)
    call StoreReal(GetGC(),H2S(ti),"UnitFacing",GetUnitFacing(u))
    call TimerStart(ti,BoltFly_time,false,function Bolt_Timer_Action)
    set ti = null
endfunction[/codes]
其实,这也是有Bug的,因为这个锤子的伤害的延迟时间是建立在目标单位不会移动的情况下,实际情况必然不是这样的。
至于如何修改吗~~~~
大家自己好好想一想啦~~
(提示:用极端间隔的timer+不断存储坐标并按照速度靠近目标,直到目标与坐标相差不远模拟锤子的移动)

timer有三种计时方式,一是有循环,而是没有循环。但运用的方式基本却有三种:
第一种:
倒计时:非常简单,就是时间到了运行指定函数。一般用于技能中的简单等待。
(PS:说一句,GUI的等待,可能导致网络时卡机,而且效率和精度都比timer低,所以学了JASS,就别用等待了)
第二种:中长时间的循环运行,一般是隔X秒do something的技能用,比较经典的运用就是Dota/3C这种对抗地图的循环不间断刷兵了。
第三种:超短时间的循环运行,一般用来击退效果什么的用。
(PS:学了JASS,短距离的瞬移,最好就用SetUnitX+SetUnitY,效率很高。但是中长距离的瞬移,就只能用SetUnitPosition了)
还有特殊的一种,就是超短时间的倒计时,甚至于它的时间设置可能为0.0。这个是用来做一些特殊的事情的,一般不会碰到。
然后,再来向大家介绍一下,关于timer的一些函数:
[codes=jass]native CreateTimer          takes nothing returns timer
//创建timer
native CreateTimerDialog                takes timer t returns timerdialog
//创建计时器窗口(timerdialog)

native DestroyTimer        takes timer whichTimer returns nothing
//删除timer
native DestroyTimerDialog              takes timerdialog whichDialog returns nothing
//删除timerdialog

native GetExpiredTimer      takes nothing returns timer
//获得到时的timer

native IsTimerDialogDisplayed          takes timerdialog whichDialog returns boolean
//timerdialog是否可见
native TimerDialogDisplay              takes timerdialog whichDialog, boolean display returns nothing
//设置timerdialog是否可见
native TimerDialogSetRealTimeRemaining  takes timerdialog whichDialog, real timeRemaining returns nothing
//此函数玄妙无穷,下面特讲
native TimerDialogSetSpeed              takes timerdialog whichDialog, real speedMultFactor returns nothing
//设置timerdialog的显示速度( 若speed设为2,原先为1, 则显示的时间为原先显示的时间*现speed/原speed )
native TimerDialogSetTimeColor          takes timerdialog whichDialog, integer red, integer green, integer blue, integer alpha returns nothing
//timerdialog显示的时间的颜色
native TimerDialogSetTitle              takes timerdialog whichDialog, string title returns nothing
//timerdialog的标题
native TimerDialogSetTitleColor        takes timerdialog whichDialog, integer red, integer green, integer blue, integer alpha returns nothing
//timerdialog的标题的颜色

native PauseTimer          takes timer whichTimer returns nothing
//暂停timer
native ResumeTimer          takes timer whichTimer returns nothing
//恢复暂停的timer

native TimerGetElapsed      takes timer whichTimer returns real
//获得timer已记的时间( Elapsed = TimeOut - Remaining )
native TimerGetRemaining    takes timer whichTimer returns real
//获得timer剩下的时间( Remaining = TimeOut - Elapsed )
native TimerGetTimeout      takes timer whichTimer returns real
//获得timer设置的时间( TimeOut = Remaining + Elapsed )

native TimerStart          takes timer whichTimer, real timeout, boolean periodic, code handlerFunc returns nothing
//开启timer

native TriggerRegisterTimerEvent takes trigger whichTrigger, real timeout, boolean periodic returns event
//Trigger注册时间到的事件
native TriggerRegisterTimerExpireEvent takes trigger whichTrigger, timer t returns event
//Trigger注册timer到时的事件(不过这个直接用TimerStart里的code就行,这个现在没用了) [/codes]
注意!!
现在特别说一下:
[codes=jass]native TimerDialogSetRealTimeRemaining  takes timerdialog whichDialog, real timeRemaining returns nothing[/codes]
这个函数可以将一个timerdialog显示的时间设置为timeRemaining ,而不管它对应timer的时间。
而且它的倒计时就直接按自己的走,如果对应的timer暂停,这个timerdialog的显示时间也会按照它的speed走动。
也就是说,这个timerdialog完全是自己一个timer了!
╮(╯▽╰)╭扯远了~~~
还有,再说一句,timer暂停后,你用ResumeTimer,貌似是不能把它恢复的⊙﹏⊙b汗。
只能直接用TimerStart对这个timer重来……………………
还有timer删除不能直接删,最好先PauseTimer再DestroyTimer。
基本上,timer只能和GameCache+ReturnBug出现,所以,请不要忘了用FlushStoredMission()清除timer的数据哦~
一般timerdialog要在timer之前被删除。

循环的timer(尤其是技能用),一般都会循环N次就停下,很少有不停止循环的。一般来说,我们都是这样做的:
[codes=jass]function TimerAction takes nothing returns nothing
    local timer ti = GetExpiredTimer()
    local integer run = GetStoredInteger( <GameCache>, I2S(H2I( ti )), "TimeRun" )
    //你的timer的局部变量
    if run > [你指定的循环次数] then
        call FlushStoredMission( <GameCache>, I2S(H2I( ti )) )
        call PauseTimer( ti )
        call DestroyTimer( ti )
    else
        call StoreInteger( <GameCache>, I2S(H2I( ti )), "TimeRun", run + 1 )
        //你的timer的动作
    endif
    set ti = null
endfunction[/codes]
这样在每一次timer计时到时,就将计数器+1,如果这个计数器超过了你指定的循环的次数,那么就删除这个计时器。

嗯~~~~timer讲完了~~该讲什么了呢?……………………

哦对了,貌似还有个动态注册事件,个人感觉跟技能没太大用,在LZ贴里的JASS培训班——JASS培训教程——中级教程——动态注册详解里有。有兴趣的,可以去看看。
嗯,在那里头,还有个动态注册事件的演示,是个技能模板(?),还不错,大家可以看一看。

Timer一个极为经典的运用就是无数技能中的击退效果了。
[codes=jass]globals
    gamecache ba_gc = null
    constant real Map_MinX = -2048.0
    constant real Map_MinY = -2048.0
    constant real Map_MaxX = 2048.0
    constant real Map_MaxY = 2048.0
endglobals

function Init takes nothing returns nothing
    call FlushGameCache( InitGameCache( "ba.w3v" ) )
    set ba_gc = InitGameCache( "ba.w3v" )
endfunction
//先运行

function H2I takes handle h returns integer
    return h
    return 0
endfunction

function I2U takes integer i returns unit
    return i
    return null
endfunction

function SetCacheReal takes string MissionKey, string Key, real value returns nothing
    call StoreReal( ba_gc, MissionKey, Key, value )
endfunction

function GetCacheReal takes string MissionKey, string Key returns real
    return GetStoredReal( ba_gc, MissionKey, Key )
endfunction

function BackAbility_Action takes nothing returns nothing
    local timer ti = GetExpiredTimer()
    local string ti_h = I2S(H2I( ti ))
    local real time = GetCacheReal( ti_h, "Time" )
    local real speed = GetCacheReal( ti_h, "Speed" )
    local real speedadd = GetCacheReal( ti_h, "SpeedAdd" )
    local real timeout = GetCacheReal( ti_h, "TimeOut" )
    local real angle = GetCacheReal( ti_h, "Angle" )
    local unit target = I2U(GetStoredInteger( ba_gc, ti_h, "Target" ))
    local real elapsed = GetCacheReal( ti_h, "Elapsed" )
    local real x = GetUnitX( target ) + speed * Cos( angle * bj_DEGTORAD )
    local real y = GetUnitY( target ) + speed * Sin( angle * bj_DEGTORAD )
    local string effectpath = GetStoredString( ba_gc, ti_h, "Effect" )
    local real effecttime = GetCacheReal( ti_h, "EffectTime" )
    local effect e = null
    if elapsed > timeout or GetUnitState( target, UNIT_STATE_LIFE ) <= 0.0 then
        call FlushStoredMission( ba_gc, ti_h )
        call PauseTimer( ti )
        call DestroyTimer( ti )
        call PauseUnit( target, false )
        call SetUnitPathing( target, true )
    else
        call SetCacheReal( ti_h, "Elapsed", elapsed + time )
        if Map_MinX < x and x < Map_MaxX then
            call SetUnitX( target, x )
        endif
        if Map_MinY < y and y < Map_MaxY then
            call SetUnitY( target, y )
        endif
        call PauseUnit( target, true )
        if effectpath != null and effectpath != "" and ModuloReal( elapsed, effecttime ) == 0.0 then
            set e = AddSpecialEffect( effectpath, GetUnitX( target ), GetUnitY( target ) )
            call DestroyEffect( e )
        endif
        call SetCacheReal( ti_h, "Speed", speed + speedadd )
    endif
    set ti = null
    set target = null
    set e = null
endfunction

function BackAbility takes unit target, real time, real speed, real speedadd, real timeout, real angle, string effectpath, real effecttime returns nothing
    local timer ti = CreateTimer()
    local string ti_h = I2S(H2I( ti ))
    call SetCacheReal( ti_h, "Time", time )
    call SetCacheReal( ti_h, "Speed", speed * time )
    call SetCacheReal( ti_h, "SpeedAdd", speedadd * time )
    call SetCacheReal( ti_h, "TimeOut", timeout )
    call SetCacheReal( ti_h, "Angle", angle )
    call StoreString( ba_gc, ti_h, "Effect", effectpath )
    call SetCacheReal( ti_h, "EffectTime", effecttime )
    call StoreInteger( ba_gc, ti_h, "Target", H2I( target ) )
    call PauseUnit( target , true )
    call SetUnitPathing( target, false )
    call TimerStart( ti, time, true, function BackAbility_Action )
    set ti = null
endfunction[/codes]
好了,先说一下BackAbility的参数:
target = 被击退的那个单位
time = Timer运行的时间间隔(也就是多久单位位移一次)
speed = 单位位移的速度(按距离/秒来计算)
speedadd = 加速度(按距离/秒来计算)(也可以为负数)
timeout = 被击退单位多久才能恢复正常
angle = 击退的朝向
effectpath = 每次过一个effecttime时间时产生的特殊效果的路径
effecttime = 特殊效果添加的时间间隔(请注意一定是time的整数倍)

然后,我们再来重点看一下SetUnitX和SetUnitY的运用。
前面已经说了,SetUnitX+SetUnitY比SetUnitPosition效率要快得多。但是原因是SetUnitX和SetUnitY忽略了大量的检测,所以使用它会比使用SetUnitPosition要危险一些,但是做一些小小的规避即可避免灾难的发生。
1.SetUnitX+SetUnitY如果位移后的坐标超出了地图的总大小,游戏将崩溃。
2.SetUnitX+SetUnitY用在移动速度为0的单位上,虽然你用GetUnitX+GetUnitY发现它确实位移了,但是屏幕上显示的是这个单位还在原地傻站着。
3.SetUnitX+SetUnitY不能用在中长距离(横或纵坐标位移200距离以上)的瞬移上,可能会引发一些很恶心的BUG,这点我曾经在综合区发过贴,不过说得有点唠叨。
…………(可能还有其他的一些副作用,我们就先不深究了)
这些都非常好规避,相信大家应该能做出来吧~~

嗯…………说了这么多,就该说说【我认为的】JASS与GUI的最大区别了,那就是JASS的函数特性使得用JASS会比GUI多了无可比拟的灵活性。JASS可以将很多功能“拼插”在一起,让你可以在相对于使用GUI上节省大量的时间,来完成一个技能。

(╯▽╰)来举个例子,Dota中的裂魂人,会一个招,招名我忘了,但是它是一个攻击中有一定几率击退他攻击的单位并附加伤害的技能。
好了,来分析一下,这个招是怎么弄出来的……
首先是肯定有个击退的效果(当然了,可能还得有一个判断角度的东东)。然后是附加伤害和几率判断,都好弄。
还有一个就是判断它的攻击了。

由于【单位被攻击】这个event是有BUG的,我们就用【任意单位受到伤害】(这个魔兽默认不能实现,只能自己实现,在综合区有一大堆帖子写了)来实现单位被攻击到。当然,这个比较麻烦(而且还是有BUG),就直接写被攻击到之后的Condition吧(偷懒偷懒……):
[codes=jass]globals
    gamecache ba_gc = null
    constant real Map_MinX = -2048.0
    constant real Map_MinY = -2048.0
    constant real Map_MaxX = 2048.0
    constant real Map_MaxY = 2048.0
endglobals

function Init takes nothing returns nothing
    call FlushGameCache( InitGameCache( "ba.w3v" ) )
    set ba_gc = InitGameCache( "ba.w3v" )
endfunction
//先运行

function H2I takes handle h returns integer
    return h
    return 0
endfunction

function I2U takes integer i returns unit
    return i
    return null
endfunction

function SetCacheReal takes string MissionKey, string Key, real value returns nothing
    call StoreReal( ba_gc, MissionKey, Key, value )
endfunction

function GetCacheReal takes string MissionKey, string Key returns real
    return GetStoredReal( ba_gc, MissionKey, Key )
endfunction

function BackAbility_Action takes nothing returns nothing
    local timer ti = GetExpiredTimer()
    local string ti_h = I2S(H2I( ti ))
    local real time = GetCacheReal( ti_h, "Time" )
    local real speed = GetCacheReal( ti_h, "Speed" )
    local real speedadd = GetCacheReal( ti_h, "SpeedAdd" )
    local real timeout = GetCacheReal( ti_h, "TimeOut" )
    local real angle = GetCacheReal( ti_h, "Angle" )
    local unit target = I2U(GetStoredInteger( ba_gc, ti_h, "Target" ))
    local real elapsed = GetCacheReal( ti_h, "Elapsed" )
    local real x = GetUnitX( target ) + speed * Cos( angle * bj_DEGTORAD )
    local real y = GetUnitY( target ) + speed * Sin( angle * bj_DEGTORAD )
    local string effectpath = GetStoredString( ba_gc, ti_h, "Effect" )
    local real effecttime = GetCacheReal( ti_h, "EffectTime" )
    local effect e = null
    if elapsed > timeout or GetUnitState( target, UNIT_STATE_LIFE ) <= 0.0 then
        call FlushStoredMission( ba_gc, ti_h )
        call PauseTimer( ti )
        call DestroyTimer( ti )
        call PauseUnit( target, false )
        call SetUnitPathing( target, true )
    else
        call SetCacheReal( ti_h, "Elapsed", elapsed + time )
        if Map_MinX < x and x < Map_MaxX then
            call SetUnitX( target, x )
        endif
        if Map_MinY < y and y < Map_MaxY then
            call SetUnitY( target, y )
        endif
        call PauseUnit( target, true )
        if effectpath != null and effectpath != "" and ModuloReal( elapsed, effecttime ) == 0.0 then
            set e = AddSpecialEffect( effectpath, GetUnitX( target ), GetUnitY( target ) )
            call DestroyEffect( e )
        endif
        call SetCacheReal( ti_h, "Speed", speed + speedadd )
    endif
    set ti = null
    set target = null
    set e = null
endfunction

function BackAbility takes unit target, real time, real speed, real speedadd, real timeout, real angle, string effectpath, real effecttime returns nothing
    local timer ti = CreateTimer()
    local string ti_h = I2S(H2I( ti ))
    call SetCacheReal( ti_h, "Time", time )
    call SetCacheReal( ti_h, "Speed", speed * time )
    call SetCacheReal( ti_h, "SpeedAdd", speedadd * time )
    call SetCacheReal( ti_h, "TimeOut", timeout )
    call SetCacheReal( ti_h, "Angle", angle )
    call StoreString( ba_gc, ti_h, "Effect", effectpath )
    call SetCacheReal( ti_h, "EffectTime", effecttime )
    call StoreInteger( ba_gc, ti_h, "Target", H2I( target ) )
    call PauseUnit( target , true )
    call SetUnitPathing( target, false )
    call TimerStart( ti, time, true, function BackAbility_Action )
    set ti = null
endfunction

function AngleBetweenTwoLocs2Angle_First takes real x1, real y1, real x2, real y2 returns real
    local real angle = bj_RADTODEG * Atan2( y2 - y1, x2 - x1)
    if angle < 0.0 then
        set angle = 360.0 - angle
    endif
    return angle
endfunction

function AngleBetweenTwoUnits2Angle_First takes unit u1, unit u2 returns real
    return AngleBetweenTwoLocs2Angle_First( GetUnitX( u1 ), GetUnitY( u1 ), GetUnitX( u2 ), GetUnitY( u2 ) )
endfunction

function AngleBetweenTwoLocs2Angle_Second takes real x1, real y1, real x2, real y2 returns real
    return bj_RADTODEG * Atan2( y2 - y1, x2 - x1) + 180.0
endfunction

function AngleBetweenTwoUnits2Angle_Second takes unit u1, unit u2 returns real
    return AngleBetweenTwoLocs2Angle_Second( GetUnitX( u1 ), GetUnitY( u1 ), GetUnitX( u2 ), GetUnitY( u2 ) )
endfunction

function Trig_AnyUnitDamaged_Condition takes nothing returns boolean
    local unit target = GetTriggerUnit()
    local unit source = GetEventDamageSource()
    local real percent = 0.0
    local real angle = 0.0
    local string effectpath = null
    if GetUnitTypeId( source ) == <裂魂人的ID> then
        set percent = GetUnitAbilityLevel( source, <招的ID> ) * 15.0
        set angle = AngleBetweenTwoUnits2Angle_Second( target, source )
        set effectpath = "Abilities\\\\Weapons\\\\TreantMissile\\\\TreantMissile.mdl"
        if GetRandomReal( 0.0, 100.0 ) < percent then
            call BackAbility( target, 0.03, 300.0, -5.7, 1.5, angle, effectpath, 0.45 )
        endif
        call DisableTrigger( GetTriggeringTrigger() )
        call UnitDamageTarget( source, target, 25.0, true, true, ATTACK_TYPE_CHAOS, DAMAGE_TYPE_FORCE, WEAPON_TYPE_WHOKNOWS )
        call EnableTrigger( GetTriggeringTrigger() )
    endif
    set target = null
    set source = null
    return false
endfunction[/codes]

这里说一下,将【两点间方向】转换成360°的角度的方法(自己想会有点麻烦,最好记下来)
称【两点间方向】中第一个点(即第一个参数)为loc1,第二个点(即第二个参数)为loc2。
则,对于:
loc1,【两点间方向】的返回结果:
如果<0.0则角度为360.0 + 返回结果
其余情况角度=返回结果;
loc2,【两点间方向】的返回结果 + 180.0 = 角度
在上面的代码已经编好:
[codes=jass]function AngleBetweenTwoLocs2Angle_First takes real x1, real y1, real x2, real y2 returns real
    local real angle = bj_RADTODEG * Atan2( y2 - y1, x2 - x1)
    if angle < 0.0 then
        set angle = 360.0 - angle
    endif
    return angle
endfunction

function AngleBetweenTwoUnits2Angle_First takes unit u1, unit u2 returns real
    return AngleBetweenTwoLocs2Angle_First( GetUnitX( u1 ), GetUnitY( u1 ), GetUnitX( u2 ), GetUnitY( u2 ) )
endfunction

function AngleBetweenTwoLocs2Angle_Second takes real x1, real y1, real x2, real y2 returns real
    return bj_RADTODEG * Atan2( y2 - y1, x2 - x1) + 180.0
endfunction

function AngleBetweenTwoUnits2Angle_Second takes unit u1, unit u2 returns real
    return AngleBetweenTwoLocs2Angle_Second( GetUnitX( u1 ), GetUnitY( u1 ), GetUnitX( u2 ), GetUnitY( u2 ) )
endfunction[/codes]
First是指返回对于loc1的角度
Second是指返回对于loc2的角度
Units就是指算单位间的角度

嗯……你看,一个Dota中的技能几下就被我们给搞定了,是不是很有成就感呢?
那么,我们把等式写上:
击退+伤害+几率+单位被攻击判断=裂魂人的第三个招(⊙﹏⊙b汗,招名记不住啊)!

嗯,如果说,这个还需要一点点工夫的话,那么你直接用技能模板做出来的技能则是非常的方便!举个例子,Dota中的海巨人的大招叫“毁灭”,说白了就是放一圈穿刺…………
那么,我们要怎么做这个技能呢?

首先,我们有这个模板(其实就是我编的啦),功能就是在创造number个玩家p的u_id单位,对以x,y为中心的半径为radius的圆范围的360/number度的每一个点发布order命令,使其释放等级为ab_lv的ab_id技能,最后在lifetime秒后爆炸死亡。
[codes=jass]function SpellAroundAbility takes real x, real y, real radius, integer number, integer u_id, integer ab_id, integer ab_lv, player p, integer order, real lifetime returns nothing
    local integer i = 0
    local integer max = number - 1
    local real angle = 360.0 / number
    local unit u = null
    local real facing = 0.0
    local real targetx = 0.0
    local real targety = 0.0
    loop
        exitwhen i > max
        set facing = i * angle
        set u = CreateUnit( p, u_id, x, y, facing )
        call ShowUnit( u, false )
        call SetUnitPathing( u, false )
        call SetUnitInvulnerable( u, true )
        call SetUnitUseFood( u, false )
        call SetUnitX( u, x )
        call SetUnitY( u, y )
        call UnitAddAbility( u, ab_id )
        call SetUnitAbilityLevel( u, ab_id, ab_lv )
        set targetx = x + radius * Cos( facing * bj_DEGTORAD )
        set targety = y + radius * Sin( facing * bj_DEGTORAD )
        call IssuePointOrderById( u, order, targetx, targety )
        call UnitApplyTimedLife( u, 'BHwe', lifetime )
        call SetUnitExploded( u, true )
        set u = null
        set i = i + 1
    endloop
    set u = null
endfunction[/codes]
隐藏单位的东东全都弄好了,那怕你弄个默认的步兵上去(前提是你的隐藏技能不耗魔),一样可以完成。
那么使用这个模板,那就很好搞出“毁灭”了,根据Dota中的技能显示,“毁灭”是个无目标技能,也就是站在原地施放,穿刺以他为中心扩散。
那么模板中的x和y就是GetUnitX(海巨人)和GetUnitY(海巨人)。radius由于穿刺不管发布点的距离多少都会一路刺过去。所以只要小于穿刺的施法距离就可以。number的话,我觉得像是“毁灭”有18个穿刺……,然后u_id就用'hfoo',ab_id就是穿刺的id。ab_lv应该就是GetUnitAbilityLevel( GetTriggerUnit(), GetUnitSpellAbilityId() ),p就是GetOwningPlayer( GetTriggerUnit() ),order就是穿刺的id(852555),lifetime就设成1.0就可以了。
如果要写到触发里,可以先local unit u = GetTriggerUnit(),这样可以省去大量字符。
那么这个技能就是:
[codes=jass]local unit u = GetTriggerUnit()
call SpellAroundAbility( GetUnitX(u), GetUnitY(u), 10.0, 18, 'hfoo', <穿刺id>, GetUnitAbilityLevel( u, GetSpellAbilityId() ), GetOwningPlayer( u ), 852555, 1.0 )
set u = null[/codes]
触发是:
[trigger]Spell
    事件
        单位 - 任意单位 发动技能效果
    条件
        (施放技能) 等于 毁灭
    动作
        自定义代码:    local unit u = GetTriggerUnit()
        自定义代码:    call SpellAroundAbility( GetUnitX(u), GetUnitY(u), 10.0, 18, 'hfoo', <穿刺id>, GetUnitAbilityLevel( u, GetSpellAbilityId() ), GetOwningPlayer( u ), 852555, 1.0 )
        自定义代码:    set u = null
[/trigger]
是不是很简单呢?
模板能让使用者非常简单的完成一项任务,只需要在参数中设置,就可以达到使用者想要的效果。
这是模板的使用部分的解说,一般来说模板都是由JASS功力很高的人编写的,虽然不至于完美无缺,但是对于一般人而言,不出BUG的几率确实少了很多。而且不会有泄露的问题。当然了,你要是看都不看模板本身一眼就使用的话,出了BUG就不怪这模板了哟~~╮(╯_╰)╭
所以说,技能模板是好处多多的呀~~~~~
当然了,如果大家想自创模板,也是没问题的。
基本上就是把一个技能经过不断地抽象(就是将所有的可变可见效果全部提取成可变量),然后就是优化,将这个模板的开放性增强,或者优化其他的方面。嗯…………也可以说是将技能最基本的动作保留,剩下的都弄成参数让使用者设置。…………形象一点,把技能的肉都扒了,只剩下骨头,至于在使用者手中是塑造出妩媚动人的娇躯呢,还是凶神恶煞的肌肉男,那就不管我们的事啦(稍稍YD一下……)~~~(当然还有可能和别的模板拼在一起或者是被某些人把骨头都改装了~~啦~~)
不过你要看清下面的要求哟~~(别被吓哭了)
对于模板创造者来说,要求一定是严格再严格。
1.不许泄露(string泄露除外)
2.创造者自己能想到的全部BUG都要改(想不到的,也不能强求不是,毕竟我的模板还可能有BUG呢~~)。
3.效率优化,要保证自己的模板又快又好
4.开放性强(下面特别强调)
5.代码要求简明易懂,或者写注释,一定要让使用者明了这个模板运行中会干什么,它是怎样达到效果的(以防止使用者的一些东东和模板发生冲突)。

嗯……重点讲一下【开放性强】这一点吧。就是能够最大限度的满足各种要求,不能让使用者因为模板没有对口的功能而自己改动模板。就拿我开发SpellAroundAbility来说,我的灵感来自于Dota海巨人的“毁灭”,这个技能是以海巨人为中心发射穿刺,如果我拿来就写模板的话,那这个模板的开放性就比较小。我改动的有下列几点:
1.我将中心变为一个点,不是根据单位(海巨人)的坐标来确定中心。
这样的话,使用者就可以不废工夫创造一个以点为中心的技能,而不是创建一个单位再用模板或自己改模板
2.添加半径,穿刺是不需要释放半径的,哪怕就是原地施放穿刺,只要单位的facing对,就可以。我想到如果使用者想达到6烈焰风暴环的效果的话,那这肯定不行。
3.添加lifetime。可以由使用者设定单位的死亡时间,可以让使用者根据自己的情况看单位要不要再活一段时间(比如释放的隐藏技能如果是个娜迦女海巫的龙卷风那就需要单位活上一段时间了)。当然了,一些人还会让马甲单位“瞬间施法”(单位选项中【显示-动画-魔法释放点】和【显示-动画-魔法释放回复】全设为0.000),那么这时把lifetime设成0.0也没关系……

这个基本上是在不断的接触和观察很多技能之后,积累成经验而成的,新手只要不断地学习就可以啦~PS:要论做技能的经验,我肯定比不过疯人ゆ衰人(中间那字不知道是不是)大哥啦~~大家如果愿意学习可以骚扰他哦~~我只是个编系统的,要论辩技能还是找他好了~~~~(*^__^*) 嘻嘻……(害人ing)

既然知道如何自创模板了,那么又如何改良别人的模板呢?
首先我们看一下我自己手头的这个biackese兄的群体技能模板(在这里感谢biackese一下,没向你征求同意就用了你的模板……):
[codes=jass]function H2I takes handle h returns integer
    return h
    return 0
endfunction

function WAR3_Cache takes nothing returns gamecache
    if udg_war3GC==null then
        call FlushGameCache(InitGameCache("I'm cache.w3v"))
        set udg_war3GC=InitGameCache("I'm cache.w3v")
    endif
    return udg_war3GC
endfunction

function skill_act takes nothing returns nothing
    local unit t_u = GetSpellAbilityUnit()
    local location p = GetUnitLoc(t_u)
    local integer uid = GetStoredInteger(WAR3_Cache(),"I2S(H2I(t_u))","uid")
    local integer aid = GetStoredInteger(WAR3_Cache(),"I2S(H2I(t_u))","aid")
    local string order = GetStoredString(WAR3_Cache(),"I2S(H2I(t_u))","order")
    local integer spell = GetStoredInteger(WAR3_Cache(),"I2S(H2I(t_u))","spell")
    local unit last_u = CreateUnitAtLoc(GetOwningPlayer(t_u), uid, p, 0)
    call UnitAddAbility( last_u, aid )
    call SetUnitAbilityLevel( last_u, aid, GetUnitAbilityLevel(t_u, spell) )
    call ShowUnit( last_u,false )
    call UnitApplyTimedLife( last_u, 'BHwe',1.00  )
    call IssueTargetOrder( last_u, order, GetEnumUnit() )
    set last_u = null
    set t_u = null
    call RemoveLocation(p)
    set p = null
endfunction

function biackese_Cluster_skill takes integer i,integer uid,integer aid,string order returns nothing
    local unit t_u = GetSpellAbilityUnit()
    local location p = GetUnitLoc(GetSpellTargetUnit())
    local group g = GetUnitsInRangeOfLocAll(i, p)
    call StoreInteger(WAR3_Cache(),"I2S(H2I(t_u))","uid",uid)
    call StoreInteger(WAR3_Cache(),"I2S(H2I(t_u))","aid",aid)
    call StoreString(WAR3_Cache(),"I2S(H2I(t_u))","order",order)
    call StoreInteger(WAR3_Cache(),"I2S(H2I(t_u))","spell",GetSpellAbilityId())
    call ForGroup( g, function skill_act )
    call FlushStoredMission(udg_war3GC,"I2S(H2I(t_u))")
    set t_u = null
    call RemoveLocation(p)
    call DestroyGroup(g)
    set g = null
    set p = null
endfunction[/codes]

首先,对骨头的函数进行修改,有很多处需修改,我就不一一提了,大家自己对比一下我改的和原先的:
改后:
(说一下,这里biackese用GameCache没错的,只不过如果用全局变量既不要initgamecache也不需要写那么多代码,而且效率比用GameCache要好得多)
[codes=jass]function Cluster_skill_order takes unit u, unit target, integer order returns nothing
    if IssueTargetOrderById( u, order, target ) == false then
        call IssuePointOrderById( u, order, GetUnitX( target ), GetUnitY( target ) )
    endif
endfunction

function Cluster_skill_act takes unit target, real x, real y, integer u_id, player p, integer a_id, integer a_lv, integer order, real lifetime returns nothing
    local unit u = CreateUnit( p, u_id, x, y, 0.0 )
    call UnitAddAbility( u, a_id )
    call SetUnitAbilityLevel( u, a_id, a_lv )
    call ShowUnit( u, false )
    call SetUnitPathing( u, false )
    call SetUnitInvulnerable( u, true )
    call SetUnitUseFood( u, false )
    call SetUnitX( u, x )
    call SetUnitY( u, y )
    call UnitApplyTimedLife( u, 'BHwe', lifetime )
    call Cluster_skill_order( u, target, order )
    set u = null
endfunction

function XL_Cluster_skill takes real source_x, real source_y, real targetx, real targety, real radius, integer maxnumber, integer u_id, player p, integer a_id, integer ab_lv, integer order, real lifetime, boolexpr condition returns nothing
    local group g = CreateGroup()
    local integer i = maxnumber
    local unit target = null
    call GroupEnumUnitsInRange( g, source_x, source_y, radius, condition )
    if i < 0 then
        set i = CountUnitsInGroup( g )
    endif
    loop
        exitwhen i <= 0
        set target = GroupPickRandomUnit( g )
        call Cluster_skill_act( target, source_x, source_y, u_id, p, a_id, ab_lv, order, lifetime )
        call GroupRemoveUnit( g, target )
        set target = null
        set i = i - 1
    endloop
    call DestroyBoolExpr( condition )
    call DestroyGroup( g )
    set g = null
    set target = null
endfunction[/codes]
╮(╯▽╰)╭面目全非了…………………………
我改动主要有8个:
1.参数方面把GetSpellXXXXX全改成参数输入了。
2.将释放单位抽象成一个点(前面我也有说过这种类型)
3.增加一个参数:maxnumber用来控制最多有几个目标(这几个目标是随机选取的)可被打击(为负数时大家所有单位)。
4.增加lifetime(跟上面的一样)
5.增加condition,可由使用者指定目标的选取条件
6.增加一系列更好的隐藏单位的函数
7.增加ab_lv,由使用者指定隐藏技能的等级
8.隐藏技能的类型范围(原模板只支持目标技能)增加点技能。
自己体会一下哦~~我就不多说了…………。

关于模板的组合,这就需要你了解这要组合的N个模板和要怎样使用它们。这就是比较难的东东了,而且这也不是教程能够教你的…………
只举个例子:【群体技能】+【击退】=【群体击退】
首先我们想,群体击退是先选取单位,后击退,所以先自下而上看【击退】(为了方便只看BackAbility):
[codes=jass]function BackAbility takes unit target, real time, real speed, real speedadd, real timeout, real angle, string effectpath, real effecttime returns nothing
    local timer ti = CreateTimer()
    local string ti_h = I2S(H2I( ti ))
    call SetCacheReal( ti_h, "Time", time )
    call SetCacheReal( ti_h, "Speed", speed * time )
    call SetCacheReal( ti_h, "SpeedAdd", speedadd * time )
    call SetCacheReal( ti_h, "TimeOut", timeout )
    call SetCacheReal( ti_h, "Angle", angle )
    call StoreString( ba_gc, ti_h, "Effect", effectpath )
    call SetCacheReal( ti_h, "EffectTime", effecttime )
    call StoreInteger( ba_gc, ti_h, "Target", H2I( target ) )
    call PauseUnit( target , true )
    call SetUnitPathing( target, false )
    call TimerStart( ti, time, true, function BackAbility_Action )
    set ti = null
endfunction[/codes]
再加上角度运算:(只写Second的)
[codes=jass]function AngleBetweenTwoLocs2Angle_Second takes real x1, real y1, real x2, real y2 returns real
    return bj_RADTODEG * Atan2( y2 - y1, x2 - x1) + 180.0
endfunction[/codes]
还可以加上距离的变种,方便:
[codes=jass]function DistanceBetweenLocs takes real x1, real y1, real x2, real y2 returns real
    local real dx = x2 - x1
    local real dy = y2 - y1
    return SquareRoot( dx * dx + dy * dy )
endfunction[/codes]
【击退】就完了。
再看【群体技能】:……………………(忽略不写),这个如果和【击退】结合,那就可以把隐藏单位这一部分骨头拿掉(因为没有用),其他的可以留下。
[codes=jass]function Cluster_skill_BackAbility takes unit target, real x, real y, integer u_id, player p, integer a_id, integer a_lv, integer order, real lifetime returns nothing
    local unit u = CreateUnit( p, u_id, x, y, 0.0 )
    call UnitAddAbility( u, a_id )
    call SetUnitAbilityLevel( u, a_id, a_lv )
    call ShowUnit( u, false )
    call SetUnitPathing( u, false )
    call SetUnitInvulnerable( u, true )
    call SetUnitUseFood( u, false )
    call SetUnitX( u, x )
    call SetUnitY( u, y )
    call UnitApplyTimedLife( u, 'BHwe', lifetime )
    call Cluster_skill_order( u, target, order )
    set u = null
endfunction

function XL_Cluster_skill_BackAbility takes real x, real y, real radius, integer maxnumber, boolexpr condition returns nothing
    local group g = CreateGroup()
    local integer i = maxnumber
    local unit target = null
    call GroupEnumUnitsInRange( g, source_x, source_y, radius, condition )
    if i < 0 then
        set i = CountUnitsInGroup( g )
    endif
    loop
        exitwhen i <= 0
        set target = GroupPickRandomUnit( g )
        call Cluster_skill_BackAbility( target, source_x, source_y, u_id, p, a_id, ab_lv, order, lifetime )
        call GroupRemoveUnit( g, target )
        set target = null
        set i = i - 1
    endloop
    call DestroyBoolExpr( condition )
    call DestroyGroup( g )
    set g = null
    set target = null
endfunction[/codes]
我们主要改的就是Cluster_skill_BackAbility。需要把它变成对击退的接口函数。
由于对于被选取的任一个单位,对BackAbility有各自不同的参数(假设),但却无法从XL_Cluster_skill_BackAbility中得到,那么我们只能将这些参数进行函数运算了。
改完后的整个代码:
回复

使用道具 举报

 楼主| 发表于 2009-2-15 10:18:17 | 显示全部楼层

4

[codes=jass]globals
    gamecache ba_gc = null
    constant real Map_MinX = -2048.0
    constant real Map_MinY = -2048.0
    constant real Map_MaxX = 2048.0
    constant real Map_MaxY = 2048.0
endglobals

function Init takes nothing returns nothing
    call FlushGameCache( InitGameCache( "ba.w3v" ) )
    set ba_gc = InitGameCache( "ba.w3v" )
endfunction
//先运行

function H2I takes handle h returns integer
    return h
    return 0
endfunction

function I2U takes integer i returns unit
    return i
    return null
endfunction

function SetCacheReal takes string MissionKey, string Key, real value returns nothing
    call StoreReal( ba_gc, MissionKey, Key, value )
endfunction

function GetCacheReal takes string MissionKey, string Key returns real
    return GetStoredReal( ba_gc, MissionKey, Key )
endfunction

function BackAbility_Action takes nothing returns nothing
    local timer ti = GetExpiredTimer()
    local string ti_h = I2S(H2I( ti ))
    local real time = GetCacheReal( ti_h, "Time" )
    local real speed = GetCacheReal( ti_h, "Speed" )
    local real speedadd = GetCacheReal( ti_h, "SpeedAdd" )
    local real timeout = GetCacheReal( ti_h, "TimeOut" )
    local real angle = GetCacheReal( ti_h, "Angle" )
    local unit target = I2U(GetStoredInteger( ba_gc, ti_h, "Target" ))
    local real elapsed = GetCacheReal( ti_h, "Elapsed" )
    local real x = GetUnitX( target ) + speed * Cos( angle * bj_DEGTORAD )
    local real y = GetUnitY( target ) + speed * Sin( angle * bj_DEGTORAD )
    local string effectpath = GetStoredString( ba_gc, ti_h, "Effect" )
    local real effecttime = GetCacheReal( ti_h, "EffectTime" )
    local effect e = null
    if elapsed > timeout or GetUnitState( target, UNIT_STATE_LIFE ) <= 0.0 then
        call FlushStoredMission( ba_gc, ti_h )
        call PauseTimer( ti )
        call DestroyTimer( ti )
        call PauseUnit( target, false )
        call SetUnitPathing( target, true )
    else
        call SetCacheReal( ti_h, "Elapsed", elapsed + time )
        if Map_MinX < x and x < Map_MaxX then
            call SetUnitX( target, x )
        endif
        if Map_MinY < y and y < Map_MaxY then
            call SetUnitY( target, y )
        endif
        call PauseUnit( target, true )
        if effectpath != null and effectpath != "" and ModuloReal( elapsed, effecttime ) == 0.0 then
            set e = AddSpecialEffect( effectpath, GetUnitX( target ), GetUnitY( target ) )
            call DestroyEffect( e )
        endif
        call SetCacheReal( ti_h, "Speed", speed + speedadd )
    endif
    set ti = null
    set target = null
    set e = null
endfunction

function BackAbility takes unit target, real time, real speed, real speedadd, real timeout, real angle, string effectpath, real effecttime returns nothing
    local timer ti = CreateTimer()
    local string ti_h = I2S(H2I( ti ))
    call SetCacheReal( ti_h, "Time", time )
    call SetCacheReal( ti_h, "Speed", speed * time )
    call SetCacheReal( ti_h, "SpeedAdd", speedadd * time )
    call SetCacheReal( ti_h, "TimeOut", timeout )
    call SetCacheReal( ti_h, "Angle", angle )
    call StoreString( ba_gc, ti_h, "Effect", effectpath )
    call SetCacheReal( ti_h, "EffectTime", effecttime )
    call StoreInteger( ba_gc, ti_h, "Target", H2I( target ) )
    call PauseUnit( target , true )
    call SetUnitPathing( target, false )
    call TimerStart( ti, time, true, function BackAbility_Action )
    set ti = null
endfunction

function AngleBetweenTwoLocs2Angle_Second takes real x1, real y1, real x2, real y2 returns real
    return bj_RADTODEG * Atan2( y2 - y1, x2 - x1) + 180.0
endfunction

function DistanceBetweenLocs takes real x1, real y1, real x2, real y2 returns real
    local real dx = x2 - x1
    local real dy = y2 - y1
    return SquareRoot( dx * dx + dy * dy )
endfunction

function C_B_time takes unit target, real targetx, real targety, real x, real y, real distance, real angle returns real
    return 0.05
endfunction

function C_B_speed takes unit target, real targetx, real targety, real x, real y, real distance, real angle returns real
    return 1500.0 / distance + 30.0
endfunction

function C_B_timeout takes unit target, real targetx, real targety, real x, real y, real distance, real angle, real speed returns real
    return 3.0 + 100.0 / distance
endfunction

function C_B_speedadd takes unit target, real targetx, real targety, real x, real y, real distance, real angle, real speed, real timeout returns real
    return - ( speed / timeout )
endfunction

function Cluster_skill_BackAbility takes unit target, real x, real y, string effectpath, real effecttime returns nothing
    local real ux = GetUnitX( target )
    local real uy = GetUnitY( target )
    local real angle = AngleBetweenTwoLocs2Angle_Second( ux, uy, x, y )
    local real distance = DistanceBetweenLocs( ux, uy, x, y )
    local real time = C_B_time( target, ux, uy, x, y, distance, angle )
    local real speed = C_B_speed( target, ux, uy, x, y, distance, angle )
    local real timeout = C_B_timeout( target, ux, uy, x, y, distance, angle, speed )
    local real speedadd = C_B_speedadd( target, ux, uy, x, y, distance, angle, speed, timeout )
    call BackAbility( target, time, speed, speedadd, timeout, angle, effectpath, effecttime )
endfunction

function XL_Cluster_skill_BackAbility takes real x, real y, real radius, integer maxnumber, string effectpath, real effecttime, boolexpr condition returns nothing
    local group g = CreateGroup()
    local integer i = maxnumber
    local unit target = null
    call GroupEnumUnitsInRange( g, x, y, radius, condition )
    if i < 0 then
        set i = CountUnitsInGroup( g )
    endif
    loop
        exitwhen i <= 0
        set target = GroupPickRandomUnit( g )
        call Cluster_skill_BackAbility( target, x, y, effectpath, effecttime )
        call GroupRemoveUnit( g, target )
        set target = null
        set i = i - 1
    endloop
    call DestroyBoolExpr( condition )
    call DestroyGroup( g )
    set g = null
    set target = null
endfunction[/codes]
好了,大家自己看看吧~~我这个【JASS技能教程】的正篇就算结束了,最后再写点可能对大家有帮助的内容,希望能助大家在学习JASS与技能的路上一臂之力。


为大家提供这个扇面选取单位的函数(没有filter)。(主要是自己做的时候有点没头没脑的,花了好长时间才做出来…………所以就直接给大家了,省的大家再动脑筋~~(*^__^*) 嘻嘻……)
[codes=jass]globals
    group Fan_Group = null
    real Fan_x = 0.0
    real Fan_y = 0.0
    real Fan_anglemin = 0.0
    real Fan_anglemax = 0.0
endglobals

function AngleBetweenTwoLocs2Angle_Second takes real x1, real y1, real x2, real y2 returns real
    return bj_RADTODEG * Atan2( y2 - y1, x2 - x1) + 180.0
endfunction

function Fan_Check takes nothing returns boolean
    local unit u = GetFilterUnit()
    local real angle = AngleBetweenTwoLocs2Angle_Second( GetUnitX( u ), GetUnitY( u ), Fan_x, Fan_y )
    set u = null
    if Fan_anglemax < Fan_anglemin then
        return Fan_anglemin <= angle or angle <= Fan_anglemax
    endif
    return Fan_anglemin <= angle and angle <= Fan_anglemax
endfunction

function GetUnitsInFanAll takes real x, real y, real radius, real facing, real angle_add returns group
    local boolexpr be = null
    local real add = angle_add
    set Fan_Group = CreateGroup()
    set Fan_x = x
    set Fan_y = y
    if add < 0.0 then
        set add = -add
    elseif add > 180.0 then
        set add = ModuloReal( add, 180.0 )
    endif
    set Fan_anglemin = facing - angle_add
    if Fan_anglemin < 0.0 then
        set Fan_anglemin = Fan_anglemin + 360.0
    endif
    set Fan_anglemax = facing + angle_add
    if Fan_anglemax > 360.0 then
        set Fan_anglemax = Fan_anglemax - 360.0
    endif
    set be = Condition( function Fan_Check )
    call GroupEnumUnitsInRange( Fan_Group, x, y, radius, be )
    call DestroyBoolExpr( be )
    set be = null
    return Fan_Group
endfunction[/codes]
可以选取以x,y为中心的半径为radius的中线朝向为facing(facing采用0-360度制,0/360°线朝向在水平线右方),向两方各延展angle_add度的扇形范围内的所有单位。
可能这个没有filter的函数,不太和大家的胃口,所以我又改了改,增加了filter(其实我觉得GetUnitsInFanAll已经可以了):
[codes=jass]function Fan_Check_Matching takes nothing returns nothing
    local unit u = GetEnumUnit()
    local real angle = AngleBetweenTwoLocs2Angle_Second( GetUnitX( u ), GetUnitY( u ), Fan_x, Fan_y )
    if Fan_anglemax < Fan_anglemin then
        if not(Fan_anglemin <= angle or angle <= Fan_anglemax) then
            call GroupRemoveUnit( Fan_Group, u )
        endif
    elseif not(Fan_anglemin <= angle and angle <= Fan_anglemax) then
        call GroupRemoveUnit( Fan_Group, u )
    endif
    set u = null
endfunction

function GetUnitsInFanMatching takes real x, real y, real radius, real facing, real angle_add, boolexpr be returns group
    local real add = angle_add
    set Fan_Group = CreateGroup()
    set Fan_x = x
    set Fan_y = y
    if add < 0.0 then
        set add = -add
    elseif add > 180.0 then
        set add = ModuloReal( add, 180.0 )
    endif
    set Fan_anglemin = facing - angle_add
    if Fan_anglemin < 0.0 then
        set Fan_anglemin = Fan_anglemin + 360.0
    endif
    set Fan_anglemax = facing + angle_add
    if Fan_anglemax > 360.0 then
        set Fan_anglemax = Fan_anglemax - 360.0
    endif
    call GroupEnumUnitsInRange( Fan_Group, x, y, radius, be )
    call ForGroup( Fan_Group, function Fan_Check_Setting )
    call DestroyBoolExpr( be )
    return Fan_Group
endfunction[/codes]
这个有个问题,就是这个扇形判断是在filter之后运行的,也就是说,这个filter-【be】是判断一个在x,y为中心半径为radius的圆范围的group里的单位。所以说,要注意一些问题……比较难解决……就不说啦~~~~~~~~

最后,向大家推荐几个绝对经典,也是绝对范例(但不一定绝对正确)的技能模板演示(排名不分前后):
1.everguo的群体技能模板(去GA搜)
(可以实现诸如群体风暴之锤等等)(绝对推荐!!!)
2.everguo的环绕技能模板(在LZ贴里的JASS培训班——其他资源——大量JASS演示里)
(可以实现环绕技能的效果)
(后来爆料出了,被绕着的单位死亡后,环绕的那一群单位,还在原地转个不停的Bug)
(我将其Bug修正了之后,也发在了GA上,你搜一下,也可以搜到)
3.everguo的仿星际隐形技能中的OrderSystem(在LZ贴里的JASS培训班——JASS培训教程——中级教程——动态注册详解里)
(可以实现由玩家控制的open/close的自定义技能)
4.本人的光环技能模板,自己觉得还可以(也去GA搜)
(可以实现诸如增加周围单位攻击/防御等的光环技能)

(2009.2.18)
5.本人的弹射技能模板http://www.islga.org/bbs/read.php?tid=24718
(可以实现如:Dota:巫医-麻痹毒药、巫妖-连环霜冻、魔兽-闪电链等)
6.本人的牵引技能模板http://www.islga.org/bbs/read.php?tid=24737
(可以实现:黑洞、Dota:谜团-大招等)

(上面的模板都加入了自定义Code+自定义变量,使得开放性近乎提升到了无限的程度)

以下是一些用搜索找到的技能模板(而且是筛选过的)(排名不分前后):
1.仅仅是一个吸兵模板http://www.islga.org/bbs/read.php?tid=19492&keyword=%C4%A3%B0%E5
(与我的牵引模板有类似之处,但可惜开放性没我的好)

当然啦,上面的这些其实或多或少,都会有一些想不到的情况,发生BUG,比如我的光环技能模板,在光环单位隐身时,周围的单位仍能受到光环的影响。但是,不管这些BUG,只论这些模板的主体,那确实是非常好的对JASS技能新手的教材。

写上结束的日期:11:52 2009/2/14……
与文头的 9:40 2008/8/29 相距甚远(5个多月,将近半年,一个学期啊~~~~(>_<)~~~~ )啊
不过就是因为如此,我才能不断地从GA汲取知识并消化,把这个教程写得像现在这样。
所以,我要谢谢所有在GA帮助我/没帮助我的人,正是因为你们的存在,才让我有了动力和墨水终于写完了这个11000+中文字的教程。
还有观看我这个教程的人,不管你看的是转帖还是原帖,我都谢谢你们能支持我。


文头所说,大家要是发现我这个教程有什么不对或有意见的地方,就直接在GA上发短消息给我吧。

最后的最后(广告):
www.islga.org/bbs  GA地精研究院——中国最大的魔兽制作者门户网站
诚邀您的加入与参与!!
回复

使用道具 举报

 楼主| 发表于 2009-2-15 10:20:51 | 显示全部楼层

Last

最后感谢biackese和everguo,在你们不知道的情况下,引用了你们的演示,希望你们海涵。

两个有用的rar

JASS技能教程所有J代码.rar

6 KB, 下载次数: 128

JASS技能教程所有魔兽地图格式文件.rar

92 KB, 下载次数: 127

回复

使用道具 举报

发表于 2009-2-15 12:08:46 | 显示全部楼层
好东东 顶了 谢谢楼主
回复

使用道具 举报

发表于 2009-2-15 12:14:17 | 显示全部楼层
这个   是那个  淫guo  的东西么?
回复

使用道具 举报

 楼主| 发表于 2009-2-15 12:57:35 | 显示全部楼层
LS你把我也当成YD男了吗~~~~~~~~(>_<)~~~~ 人家不是YD男…………~~~~(>_<)~~~~ 呜呜
回复

使用道具 举报

 楼主| 发表于 2009-2-15 14:26:58 | 显示全部楼层
………………难道没有人为我加精华吗?………………~~~~(>_<)~~~~ 好伤心…………~~~~(>_<)~~~~
回复

使用道具 举报

发表于 2009-2-15 17:02:24 | 显示全部楼层
很难看啊.看下去好辛苦.
回复

使用道具 举报

发表于 2009-2-16 21:17:33 | 显示全部楼层
写的很辛苦,看着更辛苦
回复

使用道具 举报

发表于 2009-2-16 21:40:55 | 显示全部楼层
搞个电子书吧
这样看的不舒服~
额 每段的标题应该醒目点
回复

使用道具 举报

发表于 2009-2-17 17:54:01 | 显示全部楼层
LZ很强大 膜拜中
回复

使用道具 举报

 楼主| 发表于 2009-2-18 13:24:57 | 显示全部楼层
回前几位……
我不会搞电子书………………
如果是标题改的话,我可以改的……
回复

使用道具 举报

发表于 2009-2-23 18:12:51 | 显示全部楼层
手机上的什么都加不了~~
回复

使用道具 举报

发表于 2009-3-11 22:17:34 | 显示全部楼层
很強大~ 以後再學  先謝謝
回复

使用道具 举报

发表于 2009-3-21 13:06:06 | 显示全部楼层
好贴啊
支持~~~~
回复

使用道具 举报

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

本版积分规则

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

GMT+8, 2024-11-24 08:37 , Processed in 0.045561 second(s), 22 queries .

Powered by Discuz! X3.5

© 2001-2023 Discuz! Team.

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