|
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] |
评分
-
查看全部评分
|