找回密码
 点一下
查看: 8714|回复: 30

[翻译版]vJass for spell makers(完结)

[复制链接]
发表于 2008-6-14 13:54:59 | 显示全部楼层 |阅读模式
vJass是对Jass的扩展。其特殊地方在于,它可以自由的添加结构体和全局变量。 下面我会详细介绍它们的优点及如何使用.

这篇文章我将从操作不灵活的句柄变量到结构体,逐步讲解一个技能。也会具体说明为什么要停止使用句柄变量 (自定义值变量也一样)。
我们要移植到vjass的技能是星际争霸的官方技能——烟雾弹(optical flare), 这个技能能够降低单位视野. 虽然这个技能非常古老,不过却是新人学习的好东西。

其实这个技能实现起来非常简单的: 只需要改变单位的视野范围,周期性的移动一个马甲到单位的位置,当然这个马甲的视野几乎没有。
同时这也是个特殊的技能,需要用到一些全局变量。

[jass]
//**********************************************************************************************
//*
//* 技能名称:烟雾弹
//*
//* 要点:
//*          - “池”类函数
//*          - 句柄变量函数
//*          - 烟雾弹技能
//*          - 烟雾弹Buff
//*          - 烟雾弹视野破坏技能
//*          - 烟雾弹视野单位类型
//*          - 触发 (请确保指向正确的源代码)
//*注意:
//*          - 假如在你的地图中已经自定义了侦查技能,请把技能加到这个结构部分
//末尾的SetupDetectionPool函数中.
//*   
//**********************************************************************************************

//==============================================================================================
// 烟雾弹 结构
//
constant function OpticalFlare_SpellId takes nothing returns integer
    return 'A007' //// 地图中烟雾弹技能的源代码
endfunction

constant function OpticalFlare_BuffId takes nothing returns integer
    return 'B000' //// 地图中烟雾弹BUFF的源代码
endfunction

constant function OpticalFlare_SightDestructorSpellId takes nothing returns integer
    return 'A006' //// 地图中烟雾弹视野破坏技能的源代码
endfunction

constant function OpticalFlare_FakeSightUnit takes nothing returns integer
    return 'n000' //// 地图中烟雾弹显示单位类型的源代码
endfunction

constant function OpticalFlare_MissileSpeed takes nothing returns integer
    return 1500 //// 发射速度, 和使用技能的速度完全相同
endfunction

function SetupDetectionPool takes integer spells returns nothing

    call PoolAddItem(spells,'Agyv') //真实视野 (飞行机器)
    call PoolAddItem(spells,'Atru') //真实视野 (遮光物)
    call PoolAddItem(spells,'Adtg') //真实视野 (中立 1)
    call PoolAddItem(spells,'ANtr') //真实视野 (中立 2)
    call PoolAddItem(spells,'Adts') //魔法岗哨
    call PoolAddItem(spells,'Adt1') //侦查器 (岗哨守卫)
    call PoolAddItem(spells,'Abdt') //钻地探测 (飞行单位)

//假如你自定义能被动探测的技能,就必须在技能中加一句话
// 例子:

    call PoolAddItem(spells,'A008') //探测野怪技能

// 在你的地图里把上面一句删掉,否则这个技能会删除其他不支持的技能

endfunction

//===================================================================================================
function OpticalFlareDetectDetector takes unit u, integer p returns integer
local integer n=CountItemsInPool(p)
local integer i
    loop
        exitwhen n==0
        set i=PoolGetItem(p,n)
        if GetUnitAbilityLevel(u,i)>0 then
            return i
        endif
        set n=n-1
    endloop

return 0
endfunction

function OpticalFlare_GetUnit takes timer t, string s returns unit
    return GetHandleHandle(t,s)
endfunction

function OpticalFlare_Timer takes nothing returns nothing
local timer t=GetExpiredTimer()
local unit b=OpticalFlare_GetUnit(t,"b")
local unit sh=OpticalFlare_GetUnit(t,"sh")
local real x=GetHandleReal(t,"x")
local real y=GetHandleReal(t,"y")
local real nx=GetUnitX(b)
local real ny=GetUnitY(b)
local real a=ModuloReal( Atan2(ny-y,nx-x) , 2*bj_PI)
local real d= SquareRoot(Pow(x-nx,2) +Pow(y-ny,2))
    if ModuloReal(a+bj_PI/4,bj_PI*2)<=bj_PI then
        set d=d*40
    endif
    call SetUnitPosition(sh,nx+d*Cos(a),ny+d*Sin(a) )
    call SetHandleReal(t,"x",nx)
    call SetHandleReal(t,"y",ny)
    call SetUnitFacing(sh,GetUnitFacing(b))
    call SetUnitFlyHeight(sh, GetUnitFlyHeight(b)+120,0)
set t=null
set b=null
set sh=null
endfunction

function OpticalFlare_Effect takes unit b, integer l returns nothing
local real ac=GetUnitAcquireRange(b)
local unit sh=CreateUnit( GetOwningPlayer(b), OpticalFlare_FakeSightUnit(), GetUnitX(b), GetUnitY(b), 0)
local integer abi=OpticalFlareDetectDetector(b, GetStoredInteger(InitGameCache("opticalflare"),"opt","pool") )
local timer t=CreateTimer()
    call SetUnitPathing(sh,false)
    call UnitRemoveAbility(b, abi )
    call UnitAddAbility(b, OpticalFlare_SightDestructorSpellId() )
    call UnitMakeAbilityPermanent(b,true,OpticalFlare_SightDestructorSpellId())
    call UnitMakeAbilityPermanent(b,true,OpticalFlare_BuffId())
    call SetHandleHandle(t,"b",b)
    call SetHandleHandle(t,"sh",sh)
    call TimerStart(t,0.01,true, function OpticalFlare_Timer )
    loop
        exitwhen IsUnitDeadBJ(b)
        call TriggerSleepAction(0)
        exitwhen not UnitHasBuffBJ(b, OpticalFlare_BuffId() )
    endloop
    call UnitRemoveAbility(b, OpticalFlare_SightDestructorSpellId() )
    call UnitAddAbility(b, abi )
call RemoveUnit(sh)
call FlushHandleLocals(t)
call DestroyTimer(t)
set t=null
set sh=null
endfunction

function Trig_Optical_Flare_Actions takes nothing returns nothing
local unit u=GetTriggerUnit()
local unit blind=GetSpellTargetUnit()
local integer l=GetUnitAbilityLevel(u,GetSpellAbilityId() )
    call PolledWait(  SquareRoot(Pow(GetUnitX(u)-GetUnitX(blind),2) + Pow(GetUnitY(u)-GetUnitY(blind),2)  ) / OpticalFlare_MissileSpeed() )
    call OpticalFlare_Effect(blind,l)
set u=null
set blind=null
endfunction

function Trig_Optical_Flare_Conditions takes nothing returns boolean
    return GetSpellAbilityId() == OpticalFlare_SpellId()
endfunction

function InitTrig_Optical_Flare takes nothing returns nothing
local integer i=CreatePool()
    call StoreInteger(InitGameCache("opticalflare"),"opt","pool",i)
    call SetupDetectionPool(i)
    set gg_trg_Optical_Flare = CreateTrigger(  )
    call TriggerRegisterAnyUnitEventBJ( gg_trg_Optical_Flare, EVENT_PLAYER_UNIT_SPELL_EFFECT )
    call TriggerAddCondition( gg_trg_Optical_Flare, Condition( function Trig_Optical_Flare_Conditions ) )
    call TriggerAddAction( gg_trg_Optical_Flare, function Trig_Optical_Flare_Actions )
endfunction
[/jass]         


下面我会详细解释这个技能的运作机理.
一些常量函数预处理JESP头文件

SpellId : 引起触发运行的技能ID

BuffId : 我们必须了解魔法效果ID,那么技能的触发效果能够被驱散。

SightDestructorSpellId:这是个能够改变单位视野范围的技能(添加负数视野范围)

FakeSightUnit:周期性移动到单位位置的马甲,拥有很小视野。

SetupDetectionPool: 因为侦查技能能使单位对于负数的额外视野范围免疫,所以我们需要移除它们。这是个构造函数,使用"池"来添加侦查技能...

下面是实际代码:

OpticalFlareDetectDetector: 这是个搜索和返回的函数(send and return function), 使用一个池来检查一个单位是否有侦查技能,假如有的话,返回这个技能. 一个单位如果有2个侦查技能就显得不合适了。


OpticalFlare_GetUnit: 这个技能使用了句柄变量,并没有GetHandleUnit之类的特殊函数 , 所以我们不得不周期性的进行类型转换。就是因为如此使我决定用自定义变量值代替句柄变量,自定义值变量有更完善的得到函数(get function).

OpticalFlare_Timer: 无论什么时候计时器到期,事件都会执行。这就是我要提醒大家的,仅仅移动马甲到单位的位置是不够的 。所以还要需要检测单位是否在移动,对单位的移动做预测。这个函数中的算法不错,但是不再这里多做介绍。注意 句柄变量的多方面应用。

OpticalFlare_Effect: 当技能释放时,这个函数被触发。设定具体的内容相当复杂,要判断魔法效果结束,然后删掉马甲。

Trig_Optical_Flare_Actions: 这是在等待一段时间后的calls _Effect 。由于投射物,再者由于许多东西可以升级它。

_Conditions: 我不想再解释一次。

InitTrig... : 在这里你必须要注意使用gamecache和池的方法,运用的十分特别.事实上,这个技能的设计上有相当的缺陷。因为添加起来十分混乱,所以我不想用全局变量,造成了现在每0.01面都有内存泄漏 ! 我们在后面会看到InitGameCache造成内存泄漏,而且只能用一次。

在分析了这个技能后,我们发现最重要的问题就是:修补内存泄漏.

最简单的办法是使用游戏缓存变量去预防InitGameCache,。但是这好像不是实际问题, 我们不要对整数(池ID)做些变动? 事实上,我们是不是真的需要用到池? 对于要呈现动态的东西,是不是运用关于池的物力论(the dynamism of pools)更好? 所以我推测用数组代替池更好些. 尽管我们还是需要用到一些整数变量.在这个烟雾弹例子里,所有的东西都是结构体, 所以用全局变量很方便.

但是要怎么添加全局变量呢? 以前唯一的方法是在GUI中的变量对话框中添加, 这样做不仅是花时间和有限制, 而且增加了JASS技能的移植,假如复制代码到其他地图里,无法复制需要使用的全局变量.

但是VJASS靠全局块(globals block)允许全局变量申明,就和 common.j/blizzard.j/war3map.j globals一样.

如下:
[jass]
globals
    type name
    type2 name2 = value
endglobals
[/jass]
类型申明和局部变量申明一样,其他的也一样。
现在我们可以很随意的添加全局数组来代替使用池和易泄漏的游戏缓存。

我们需要的全局变量:
[jass]
globals
    integer array DetectorSpells
    integer array DetectorSpellsN=0
endglobals
[/jass]
下面需要改变改变三个地方。第一步,玩家获得技能;第二步,假如发现侦查技能,返回技能ID;第三步,开关函数(init function)调用第一步。
[jass]

function SetupDetectionAbilities returns nothing

    set DetectorSpells[1]='Agyv' //真实视野 (飞行机器)
    set DetectorSpells[2]='Atru' //真实视野 (遮光物)
    set DetectorSpells[3]='Adtg' //真实视野 (中立 1)
    set DetectorSpells[4]='ANtr' //真实视野 (中立 2)
    set DetectorSpells[5]='Adts' //魔法岗哨
    set DetectorSpells[6]='Adt1' //侦查器 (岗哨守卫)
    set DetectorSpells[7]='Abdt' //钻地探测 (飞行单位)

    // set DetectorSpellsN=7
  //正常情况需要7个技能, 假如正好是7个,去掉上面一行代码的注释符

  //假如你自定义新技能允许被动探测, 就必须加些代码
  //例子:

    set DetectorSpells[8]='A008' //探测野怪技能
    set DetectorSpellsN=8 //添加8个技能到数组, 在这指定数量

// 在你的地图里把上面的例子删掉,否则这个技能会删除其他不支持的技能

endfunction
[/jass]

格式是固定的. 注意这里使用数组的方法, 可能看上去不是很直观,但是很有用,我在后面会详细介绍. 这个函数不再需要可变物(argument), 所以同样可以删除了.
[jass]

function OpticalFlareDetectDetector takes unit u returns integer
local integer i
local integer n=DetectorSpellsN
    loop
        exitwhen n==0
        set i=DetectorSpells[n]
        if GetUnitAbilityLevel(u,i)>0 then
            return i
        endif
        set n=n-1
    endloop
return 0
endfunction
[/jass]

我们也不需要那个P可变物. 只要改变OpticalFlare_Effect部分:
[jass] local integer abi=OpticalFlareDetectDetector(b ) [/jass]
现在来看开关函数简洁多了:
[jass]
function InitTrig_Optical_Flare takes nothing returns nothing
    call SetupDetectionAbilities()
    set gg_trg_Optical_Flare = CreateTrigger(  )
    call TriggerRegisterAnyUnitEventBJ( gg_trg_Optical_Flare, EVENT_PLAYER_UNIT_SPELL_EFFECT )
    call TriggerAddCondition( gg_trg_Optical_Flare, Condition( function Trig_Optical_Flare_Conditions ) )
    call TriggerAddAction( gg_trg_Optical_Flare, function Trig_Optical_Flare_Actions )
endfunction
[/jass]

评分

参与人数 1威望 +10 收起 理由
kook + 10 辛苦辛苦.

查看全部评分

 楼主| 发表于 2008-6-14 13:56:31 | 显示全部楼层
替代句柄变量
我们在上面明显的做了改变, 但是还有一个问题, 句柄变量的调用.

我们只要看看OpticalFlare_Timer和OpticalFlare_Effect,就能把现在这个技能中的句柄变量看透.

你可以会问,句柄变量有什么问题.

    句柄变量有太多的函数调用: H2I, I2S(), (Get)Store(d)(type), 可是JASS中函数调用的效率实在不敢恭维.

    句柄变量使用游戏缓存: 游戏缓存甚至在使用本地函数时都很慢. 句柄变量大多数情况下使用在存储/读取一些整数,实数变量, 使用游戏缓存本地函数根本不值得花费这么多时间. 不要被我的话误导, 在其他情况下游戏缓存还是非常有用的,但是在这里,明显不是最好的办法。

    句柄变量频繁出现bug。你适当的写代码就不会出现这样的问题,假如你过度信任为句柄储存参量(reference),还把这些引用转化为变量,就会出现很多问题. 包括被公认的游戏缓存bug (实际上是句柄堆栈bug)。

    对于有瑕疵的参量使用本地函数,就会出现问题 (例子:一个单位不再存在,而你只能从句柄变量得到)。假如在清除某个对象时,你不注意完全清除对象的句柄变量的参量,就会出现严重问题。

    句柄变量对于储存空间使用字符串为基础。

    句柄变量实在很让人郁闷, 你可能已经注意到了,在学习JASS技能时会卡一下。这是因为句柄变量和变量的复制会使代码读取繁杂.

为什么在这里选择使用结构体?
    结构体只需要2个函数调用: 一个是"created",另一个是"destroyed", 用结构体设置/获得值,不需要额外的函数调用。事实上,你仅仅是读取数组变量的索引!
    由于结构体使用数组来储存值和对象的参量, 所以这个对象是受保护的。由于结构体仍然有参量指向它,并且只有引擎知道, 所以更加安全. 而且由结构体获得被删除的单位比用句柄变量少了许多问题.
    代替了直接使用数组, 使复杂种类的代码能有更友好的体系(syntax).

使用结构体是最根本的解决方法? 答案是:NO
    结构体有实例限制——每个结构体类型最多有8190个实例,不过大多数情况下这不是问题(换句话说,在技能里根本不存在这个问题),但是某些情况下还是有问题的.
    结构体没有句柄变量灵活, 可以不使用字符串做标记,但是空间固定的名称必须要先申明, 正是如此,灵活性大大降低. 实际上,大部分情况下这是好事,除了个别应用.
    结构体与句柄之间并没有联系,这意味着结构体与句柄变量的运行方式不同,在设置变量和内容时,你不必让每个句柄都有一个结构体, 因此你只要一个工作区来获得句柄的结构.

在这个技能的例子里,使用结构体相当好,除非你想出办法把结构体和句柄联系起来. 除非你一个技能里有8190个实例,要不然这个限制毫无意义, 事实上一个技能能有100个实例就不容易了...

那么, 结构体是什么?
你知道什么是数组? 数组就是有相同名称,并且编入索引的一组变量. 现在我们来替换数组, 不使用数组的索引来分组变量, 而是根据名称来分类.

那么,你就有了许多种类的变量,形成了变量组或者次变量组,并且它们都有自己的名称.

实例会占用一个点。在2D平面里的点是一对值, x和 y. 假如是这种类型的变量,同样的会有2部分:x和y。

在理想情况下,我们可以使用JASS来做类型变量的点, 并且可以分别运算x和y的值.

如下:
[jass]
function DoSomething takes nothing returns point
local point p=point.create()
    set p.x=4.5
    set p.y=12.3
return p
endfunction
[/jass]

而且可以很容易的写个函数来返回在点"变量"中X和Y的值,那么现在的这个"超级"变量就是个结构体. 当然,在Jass中不允许申明这些新类型,但是VJASS可以...

如何申明呢?申明的方法是什么呢?
你只需要告诉编译器2件事:新类型的名称和其持有的"次变量".

格式如下:
[jass]struct point
    real x
    real y
endstruct
[/jass]
在JassHelper里的readme文档里你可以已经看到了这个.

所以这些结构体类型可以包含任意"次变量"的组合。现在我们就可以调用空间里的这些"次变量"成员了.

在例子里最后要注意,p变量并不是一个点,而是一个点的实例.

在这个例子里, 我们需要做些什么?
对 _Effect和_Timer的检查可以得出结论: 用到的句柄变量有4个:单位b(有BUFF), 单位sh (马甲), 和实数x,y (单位的当前位置).
当我们把句柄变量转换为结构体的时候,功能必须相同, 所以我们要把结构体联系上计时器, 这个结构体应该有4个值.
[jass]
struct flaredata
    unit b  //有BUFF的单位
    unit sh //马甲单位
    real x  //单位当前位置
    real y  //单位当前位置
endstruct[/jass]
创建一个结构体
创建一个结构体类型的对象是非常容易的, 只需要使用 (objectname).create(), 就像这个烟雾弹对象,我们只用了flaredata.create()

我们如何把结构体和句柄联系上?
这很棘手么? 当然不是.结构体的参量与整数是可以互换的
我们在这里会使用最简单的方法,仅适用GetHandleInt / SetHandleInt , 在仅适用一次的情况下,句柄变量根本没有性能问题,当存储本地函数类型时,根本不是同类型的参量, 所以我们有很多办法把单个整数和句柄联系起来,并且有一些专门指向句柄类型的办法.
我们使用单个储存函数来储存结构体参量,只取得一个整数就可以了,没有必要使用结构体。
下面我们对结构体赋值一个整数,然后加载它. 我建议使用structname() ,因为这样更有效率。
在这里用语言很难描述, 所以我写出代码。
[jass]
struct flaredata
    unit b  //有BUFF的单位
    unit sh //马甲单位
    real x  //单位的当前位置
    real y  //单位的当前位置
endstruct
function OpticalFlare_Timer takes nothing returns nothing
local timer t=GetExpiredTimer()
local flaredata dat= flaredata( GetHandleInt(t,"dat") ) //注意这个类型的操作记号
                                                         //在这个例子里,并不是一定需要
                                                         //只是让代码看起来更清晰
local real nx=GetUnitX(dat.b)
local real ny=GetUnitY(dat.b)
local real a=ModuloReal( Atan2(ny-dat.y , nx-dat.x ) , 2*bj_PI) //不再需要局部变量
local real d= SquareRoot(Pow(dat.x-nx,2) +Pow(dat.y-ny,2))
    if ModuloReal(a+bj_PI/4,bj_PI*2)<=bj_PI then
        set d=d*40
    endif
    call SetUnitPosition(dat.sh,nx+d*Cos(a),ny+d*Sin(a) )
    set dat.x=nx //注意这里设置的语法
    set dat.y=ny
    call SetUnitFacing(    dat.sh,GetUnitFacing( dat.b ))
    call SetUnitFlyHeight( dat.sh, GetUnitFlyHeight( dat.b )+120,0)
set t=null
endfunction
function OpticalFlare_Effect takes unit b, integer l returns nothing
local real ac=GetUnitAcquireRange(b)
local unit sh=CreateUnit( GetOwningPlayer(b), OpticalFlare_FakeSightUnit(), GetUnitX(b), GetUnitY(b), 0)
local integer abi=OpticalFlareDetectDetector(b )
local timer t=CreateTimer()
local flaredata dat = flaredata.create()
    call SetUnitPathing(sh,false)
    call UnitRemoveAbility(b, abi )
    call UnitAddAbility(b, OpticalFlare_SightDestructorSpellId() )
    call UnitMakeAbilityPermanent(b,true,OpticalFlare_SightDestructorSpellId())
    call UnitMakeAbilityPermanent(b,true,OpticalFlare_BuffId())
    set dat.x=0. //结构体空间不像句柄变量,自动初始化为0
    set dat.y=0. //也可以在结构体块中初始化使用缺省值
    set dat.b=b
    set dat.sh=sh
    call SetHandleInt(t,"dat",dat) //在这里把结构体对象和句柄联系起来
    call TimerStart(t,0.01,true, function OpticalFlare_Timer )
    loop
        exitwhen IsUnitDeadBJ(b)
        call TriggerSleepAction(0)
        exitwhen not UnitHasBuffBJ(b, OpticalFlare_BuffId() )
    endloop
    call UnitRemoveAbility(b, OpticalFlare_SightDestructorSpellId() )
    call UnitAddAbility(b, abi )
call RemoveUnit(sh)
call FlushHandleLocals(t)
call DestroyTimer(t)
call flaredata.destroy(dat) //一旦不需要用到结构体对象,需要销毁它们
set t=null
set sh=null
endfunction
[/jass]
是的,我们需要销毁结构体, 就像清除句柄变量. 不要担心结构体会有溢出, 它们只使用预先分配给它们的内存, 销毁只是为了ID能够重复利用. 假如只是不停的创建结构体类型,而不销毁,我们很容易就会遇到8190个实例的限制,因为没有用到的实例并没有被释放.
我们必须让代码看起来更有效率,当然不要有GetHandleHandle之类的, 假如我们每0.01秒减少GetHandle**调用,每次烟雾弹单位都会减少1(从4开始往下).
有没有更好的方法不去使用所有的游戏缓存?我们有方法来替代储存对于句柄的结构体的整数ID。但是在这个技能里我们用另外一种方法 ,不需要联系到计时器,我们只要再次用到全局变量...
回复

使用道具 举报

 楼主| 发表于 2008-6-14 13:56:40 | 显示全部楼层
单独计时器
我们现在要做的事,给每个技能里的实例创建一个计时器, 写上信息,然后让计时器每0.01秒到期, 当BUFF失去后销毁计时器和信息。

这意味着技能里的有多少实例就要有多少计时器. 这样有必要么?

在这个技能里,计时器到期不是重点,重点是每0.01秒都让BUFF主动运行。

我们不需要对每个运行的实例都创建计时器,那样根本不需要,我们需要的只是一个单独的计时器,使它强制对所有实例运行。

听起来难做起来简单, 我们只要给每个运行的实例添加一个全局数组。当这个唯一的计时器到期,通过整个数组,运行每个其中的索引。

给数组添加内容并不难,只要有一些单元号码的变量,在选定上一个位置后添加到新的单元。
看一下计时器到期事件,仅仅是运行数据,包括从到期的计时器获取数据 。
[jass]
globals
flaredata array OpticalFlare_Ar
integer OpticalFlare_total = 0
timer OpticalFlare_timer=CreateTimer()
endglobals
[/jass]
现在我们有了需要的一切, 烟雾弹数据对象的数组,一个整数能告诉我们一切 ,一个计时器在需要的时候能到期。

现在,我们在计时器处理上使用些小技巧,当没有运行的实例时计时器应该终止. 并且要每0.01秒周期性的到期,无论什么时候都至少要有一次。

需要的时候开启计时器也十分容易,只要检查把单元加入到数组的那个时刻 ,假如单元的号码是0,计时器失效.

在没有运行的BUFF时,如何终止计时器有些复杂. 首先,在销毁烟雾弹数据对象时我们需要把它在数组中清除. 否则数组会被无效的对象塞满。

我们可以在flaredata.destroy()调用的时候, 把单元从数组中清除. 但是要想找到刚被销毁的对象的索引无法解决.

我们用其他的方法来停止从函数中销毁烟雾弹数据对象, 用计时器到期事件函数。

OpticalFlare_Effect需要正确的烟雾弹数据对象的销毁请求(或者移除)。

销毁请求只是烟雾弹数据结构体中的一个空间,我们可以添加一个布尔值的销毁请求:
[jass]
struct flaredata
unit b //有BUFF的单位
unit sh //马甲单位
real x //单位当前的位置
real y //单位当前的位置

boolean destroyplease = false
endstruct
[/jass]
注意这里的= false, 它确定了在使用create()时,自动给新的对象赋予缺省值。
OpticalFlare_Effect函数在对象销毁的时候应该设置=ture,而不是调用函数来销毁.

计时器到期时间可以根据运行的BUFF进行循环, 当其中任意destroyplease=true 了,就会销毁对象并且从数组中清除。

假如在处理数组中的单元后,单元的数量为0, 又会再次终止计时器。

现在处理每个烟雾弹数据对象的函数是OpticalFlare_Timer,我们应该修正它,让它能在循环中运行并且能执行销毁操作。
[jass]
function OpticalFlare_Timer takes nothing returns nothing
local integer i=0
local flaredata dat
local real nx
local real ny
local real a
local real d

loop
exitwhen i==OpticalFlare_total
set dat= OpticalFlare_Ar
if (dat.destroyplease) then
//这里很复杂
//我们已经参照数据文件(dat)得到烟雾弹数据
//所以我们先要在数组中清除它
//我们可以简单的只清除这个位置的最后烟雾弹数据
//然后减少全部

set OpticalFlare_Ar= OpticalFlare_Ar[ OpticalFlare_total - 1]
set OpticalFlare_total=OpticalFlare_total-1

//现在我们可以自由清除数据文件
call dat.destroy() //销毁结构体对象

//假如我们不减去i, 他会跳过我们移动到其位置的新对象
// 后面会增加i
set i=i-1

else //注意这里和原本计时器到期函数很相似
set nx=GetUnitX(dat.b)
set ny=GetUnitY(dat.b)
set a=ModuloReal( Atan2(ny-dat.y , nx-dat.x ) , 2*bj_PI)
set d= SquareRoot(Pow(dat.x-nx,2) +Pow(dat.y-ny,2))

if ModuloReal(a+bj_PI/4,bj_PI*2)<=bj_PI then
set d=d*40
endif
call SetUnitPosition(dat.sh,nx+d*Cos(a),ny+d*Sin(a) )
set dat.x=nx
set dat.y=ny
call SetUnitFacing( dat.sh,GetUnitFacing( dat.b ))
call SetUnitFlyHeight( dat.sh, GetUnitFlyHeight( dat.b )+120,0)
endif
set i=i+1
endloop

if (OpticalFlare_total==0) then
//终止计时器, 不再需要它了
call PauseTimer(OpticalFlare_timer)
endif

endfunction
[/jass]
我们现在要修正 _Effect函数,因为这个函数不仅创建一个计时器,还对其配属 . 我们只要它能把心得烟雾弹数据对象添加到数组中,在必要的时候开启计时器,设置destroyplease=true来代替销毁.
[jass]
function OpticalFlare_Effect takes unit b, integer l returns nothing
local real ac=GetUnitAcquireRange(b)
local unit sh=CreateUnit( GetOwningPlayer(b), OpticalFlare_FakeSightUnit(), GetUnitX(b), GetUnitY(b), 0)
local integer abi=OpticalFlareDetectDetector(b )
local flaredata dat = flaredata.create()
call SetUnitPathing(sh,false)
call UnitRemoveAbility(b, abi )
call UnitAddAbility(b, OpticalFlare_SightDestructorSpellId() )
call UnitMakeAbilityPermanent(b,true,OpticalFlare_SightDestructorSpellId())
call UnitMakeAbilityPermanent(b,true,OpticalFlare_BuffId())

set dat.x=0. //不想句柄变量,结构体内存不自动初始化为0
set dat.y=0. //也可以在结构体块中用缺省值初始化
set dat.b=b
set dat.sh=sh

if(OpticalFlare_total==0) then //在数组中没有单元,所以计时器不活动
call TimerStart(OpticalFlare_timer,0.01,true,function OpticalFlare_Timer)
//重启计时器
endif

set OpticalFlare_total=OpticalFlare_total+1 //增加单元号码
set OpticalFlare_Ar[ OpticalFlare_total-1 ] = dat //把烟雾弹数据添加到数组
//注意数组中的索引以[0]开始
loop
exitwhen IsUnitDeadBJ(b)
call TriggerSleepAction(0)
exitwhen not UnitHasBuffBJ(b, OpticalFlare_BuffId() )
endloop
call UnitRemoveAbility(b, OpticalFlare_SightDestructorSpellId() )
call UnitAddAbility(b, abi )

call RemoveUnit(sh)
set dat.destroyplease = true //会发送信号到计时器函数,所以它销毁了这个对象
set sh=null
endfunction
[/jass]
我们使用了一些数组操作来代替了很多麻烦的事,比如去除创建计时器, 销毁计时器 , 清除句柄变量。

就是这么简单! 这个技能是简洁的结构体代替了句柄变量,在0.01秒的计时器的速度上有了很大的提高.曾经在有11个有烟雾弹单位出现的时候,我的电脑卡机卡到死. 现在你再也不用担心这个问题了,就算有再多的单位有BUFF也没问题。

最后提醒一件事: 把header改为指向JassHelp, 而不是指向池类和句柄变量。

============================华丽的分割线===================================
全文结束!本人进入休眠状态。。。。。。。。。。。。。
回复

使用道具 举报

发表于 2008-6-14 15:54:09 | 显示全部楼层
那么我沙发下..
英文看不懂的说.
等lz放出知道做什么
再研究
回复

使用道具 举报

发表于 2008-6-14 16:37:37 | 显示全部楼层
‘’因为侦查技能能使单位对于负数的额外视野范围免疫,所以我们需要移除它们。‘’



不错的讲解
一开始不明白的地方  现在也有点懂了

期待第四次更新
回复

使用道具 举报

发表于 2008-6-14 16:51:43 | 显示全部楼层
占楼待售。
回复

使用道具 举报

发表于 2008-6-14 18:36:32 | 显示全部楼层
你们…………
好吧 支持一下
回复

使用道具 举报

发表于 2008-6-14 21:47:10 | 显示全部楼层
居然是预告...
回复

使用道具 举报

发表于 2008-6-14 23:35:17 | 显示全部楼层
GA汉化小王子诞生了 于是期待
回复

使用道具 举报

发表于 2008-6-14 23:37:58 | 显示全部楼层
引用第8楼hyp于2008-06-14 23:35发表的  :
GA汉化小王子诞生了 于是期待
其实都是我剥削出来的
回复

使用道具 举报

发表于 2008-6-22 15:28:47 | 显示全部楼层
挑几个神仙请回家。
回复

使用道具 举报

发表于 2008-6-22 17:15:59 | 显示全部楼层
那么看不懂
飘过
回复

使用道具 举报

发表于 2008-6-24 01:53:33 | 显示全部楼层
= =|||就算是英文也请先留个东西吧 总有人看得懂的....
另...连3贴不是要扣分么
回复

使用道具 举报

发表于 2008-6-25 19:42:55 | 显示全部楼层

Re:[翻译版]vJass for spell makers(第一次更新)

话说终于有东西了
回复

使用道具 举报

发表于 2008-6-26 20:35:32 | 显示全部楼层
第四次还没出啊?
回复

使用道具 举报

 楼主| 发表于 2008-6-26 21:05:40 | 显示全部楼层
今天感冒了。。。。。头疼的很。。。。。。

下午睡了一觉     累的很    没心情翻译
回复

使用道具 举报

发表于 2008-6-26 21:07:25 | 显示全部楼层
翻译是累活,慢慢来
回复

使用道具 举报

发表于 2008-6-26 21:58:09 | 显示全部楼层
感冒了就多休息啊
小厷厷要注意身体啊
回复

使用道具 举报

发表于 2008-6-27 11:39:35 | 显示全部楼层
17楼……………………注意影响(同XX退散!!)
文字copy 了会仔细研究的!
回复

使用道具 举报

发表于 2008-6-27 12:22:33 | 显示全部楼层
引用第18楼血戮魔动冰于2008-06-27 11:39发表的  :
17楼……………………注意影响(同XX退散!!)
文字copy 了会仔细研究的!
鄙视
我关心小厷厷关你啥事
你屠版我还懒得说你呢
回复

使用道具 举报

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

本版积分规则

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

GMT+8, 2024-11-23 18:17 , Processed in 0.219441 second(s), 22 queries .

Powered by Discuz! X3.5

© 2001-2023 Discuz! Team.

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