找回密码
 点一下
查看: 2885|回复: 5

导弹模拟系统及其应用[VJ]

[复制链接]
发表于 2008-12-6 09:53:24 | 显示全部楼层 |阅读模式
用导弹模拟系统可以做出很多有趣的效果,诸如:任意时间反弹一个飞行中的导弹,或者随意改变飞行中导弹的伤害值,随意改变飞行中导弹的速度等等,最为重要的一点是,我们可以使用模拟使得导弹击中目标后触发我们想要的效果函数。先贴出VJ的导弹模拟系统代码:
[jass]library MissileSimulation

struct MissileData
unit caster
unit target
real damage
real speed
string func
boolean valid
location p
endstruct

globals
unit GetTriggerUnitEx
unit GetSpellTargetUnitEx
endglobals

private function Abs takes real i returns real
    if i < 0 then
        return 0 - i
    endif
    return i
endfunction

private function CacheValue takes nothing returns gamecache
    if bj_lastCreatedGameCache == null then
        call FlushGameCache( InitGameCache("ReturnBugSystem.w3v") )
        set bj_lastCreatedGameCache = InitGameCache("ReturnBugSystem.w3v")
    endif
    return bj_lastCreatedGameCache
endfunction


private function ConvertUnit takes integer value returns unit
    return value
    return null
endfunction

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

private function ConvertHandleInt takes handle valueHandle returns integer
    return valueHandle
    return 0
endfunction

private function ConvertHandle takes integer value returns handle
    return value
    return null
endfunction

private function I2H takes integer value returns handle
    return value
    return null
endfunction

private function H2S takes handle valueHandle returns string
    return I2S( ConvertHandleInt(valueHandle) )
endfunction

private function SetHandle takes string s1,string s2,handle u returns nothing
    call StoreInteger( CacheValue(), s1, s2, ConvertHandleInt(u) )
endfunction

private function GetUnit takes string s1,string s2 returns unit
    return ConvertUnit( GetStoredInteger(CacheValue(), s1, s2))
endfunction

private function SetInteger takes string s1,string s2,integer i returns nothing
    call StoreInteger( CacheValue(),s1,s2,i)
endfunction

private function GetInteger takes string s1,string s2 returns integer
    return GetStoredInteger( CacheValue(),s1,s2)
endfunction


private function CleanCV takes string s returns nothing
    call FlushStoredMission( CacheValue(), s )
endfunction

private function CDamage takes unit u1,unit u2,real DM returns nothing
    call UnitDamageTarget( u1, u2, DM, false, false, ATTACK_TYPE_NORMAL, DAMAGE_TYPE_FIRE, WEAPON_TYPE_WHOKNOWS )
    call CreateTextTagUnitBJ(I2S(R2I(DM)),u2,64,10,75,25,100,15)
    call SetTextTagVelocityBJ( GetLastCreatedTextTag(), 100, 90 )
    call SetTextTagPermanent( GetLastCreatedTextTag(), false )
    call SetTextTagFadepoint( GetLastCreatedTextTag(), 1 )
    call SetTextTagLifespan( GetLastCreatedTextTag(), 1.7 )
endfunction

private function Angle takes unit u,location p returns real
    return bj_RADTODEG * Atan2(GetLocationY(p) - GetUnitY(u),GetLocationX(p) - GetUnitX(u))
endfunction
private function AngleBetweenUnits takes unit u1,unit u2 returns real
    return bj_RADTODEG * Atan2(GetUnitY(u2) - GetUnitY(u1),GetUnitX(u2) - GetUnitX(u1))
endfunction

function MissileMove takes nothing returns nothing
    local timer lt = GetExpiredTimer()
    local unit mu = GetUnit(H2S(lt),"mu")
    local MissileData md = MissileData(GetUnitUserData(mu))
    local real x = GetUnitX(mu)
    local real y = GetUnitY(mu)
    local real angle = Angle(mu,md.p)
    local real distance
    local real highspeed
    local real heightdistance = GetUnitFlyHeight(md.target) - GetUnitFlyHeight(mu) + 60

   
    if md.valid == true then
        set md.p = GetUnitLoc(md.target)
        set distance = SquareRoot((x-GetLocationX(md.p))*(x-GetLocationX(md.p)) + (y-GetLocationY(md.p))*(y-GetLocationY(md.p)))
        if Abs(heightdistance) >= 50 then
            set highspeed = heightdistance/(distance / md.speed) * .03
            call SetUnitFlyHeight( mu, GetUnitFlyHeight(mu) + highspeed, 0.00 )
        endif
    else
        set distance = SquareRoot((x-GetLocationX(md.p))*(x-GetLocationX(md.p)) + (y-GetLocationY(md.p))*(y-GetLocationY(md.p)))
    endif
   
    if distance <= (md.speed * .03 / 2)  then
        call KillUnit(mu)
        if md.valid == true then
            call CDamage(md.caster,md.target,md.damage)
            if md.func != null then
                set GetTriggerUnitEx = md.caster
                set GetSpellTargetUnitEx = md.target
                call ExecuteFunc(md.func)
            endif
        endif
        call CleanCV(H2S(lt))
        call CleanCV(H2S(mu))
        call DestroyTimer(lt)
        call MissileData.destroy(md)
    else
        call SetUnitX(mu,Cos(angle*bj_DEGTORAD)*md.speed * .03 + x)
        call SetUnitY(mu,Sin(angle*bj_DEGTORAD)*md.speed * .03 + y)
        call SetUnitFacing(mu,angle)
    endif
   
    set lt = null
    set mu = null
endfunction

function MissileStart takes unit caster,unit target,real damage,real speed,string func,integer modleunitid returns nothing
    local timer lt = CreateTimer()
    local MissileData md = MissileData.create()
    local unit mu
    set md.caster = caster
    set md.target = target
    set md.damage = damage
    set md.speed = speed
    set md.func = func
    set md.p = GetUnitLoc(target)
    set md.valid = true
    set mu = CreateUnit(GetOwningPlayer(caster),modleunitid,GetUnitX(caster),GetUnitY(caster),AngleBetweenUnits(caster,target))
    call UnitAddAbility(mu,'Aloc')
    call UnitAddAbility(mu,'Avul')
    call UnitAddAbility(mu,'Arav')
    call UnitRemoveAbility(mu,'Arav')
    call SetUnitFlyHeight(mu,60.,0)
    call SetUnitUserData(mu,md)
    call SetHandle(H2S(lt),"mu",mu)
    call TimerStart(lt,.03,true,function MissileMove)
    set mu = null
    set lt = null
endfunction

endlibrary[/jass]

使用规则call MissileStart(施法者,目标,伤害值,导弹速度,效果函数,模型单位id)。
你的效果函数可以不写在library MissileSimulation里面,在效果函数的顶端,你应该先把GetTriggerUnitEx和GetSpellTargetUnitEx传递给局部函数,如:[jass]
function Effect takes nothing returns nothing
    local unit triggerUnit = GetTriggerUnitEx
    local unit spelltargetunit = GetSpellTargetUnitEx
    ……[/jass]
这里的技能施放目标和施放技能的单位都是用全局变量传递的,如果你的效果函数里有诸如等待的语句,那么那个时候这两个全局变量的值可能已经被其他导弹的值覆盖掉了。

下面是效果的一些演示代码:

注意:3包括3以前的演示代码是需要注释掉call UnitAddAbility(mu,'Aloc')这条语句的,因为处于蝗虫状态的单位无法被枚举。删除这个技能依然无法被枚举,直到这个单位死亡为止。
具体的解决方法参看第四点。


1.blink技能施放后导弹丢失目标。[jass]
function blink_filter takes nothing returns nothing
    local unit u = GetFilterUnit()
    if GetUnitAbilityLevel(u,'导弹标识技能') > 0 then
        set s__MissileData_valid[GetUnitUserData(u)] = false
    endif
    set u = null
endfunction
        

function blink takes nothing returns nothing
    local group g = CreateGroup()
    local unit u = GetTriggerUnit()
    call GroupEnumUnitsInRange(g,GetUnitX(u),GetUnitY(u),1000.,Condition(function blink_filter))
    call DestroyGroup(g)
    set g = null
    set u = null
endfunction[/jass]

2.反弹全部靠近施法者1000范围内的所有导弹。[jass]

function blink_filter takes nothing returns nothing
    local unit u = GetFilterUnit()
    local unit temp
    if GetUnitAbilityLevel(u,'导弹标识技能') > 0 then
        set temp = s__MissileData_target[GetUnitUserData(u)] = false
        set s__MissileData_target[GetUnitUserData(u)] = s__MissileData_caster[GetUnitUserData(u)]
        set s__MissileData_caster[GetUnitUserData(u)] = temp
        set temp = null
    endif
    set u = null
endfunction
        

function blink takes nothing returns nothing
    local group g = CreateGroup()
    local unit u = GetTriggerUnit()
    call GroupEnumUnitsInRange(g,GetUnitX(u),GetUnitY(u),1000.,Condition(function blink_filter))
    call DestroyGroup(g)
    set g = null
    set u = null
endfunction[/jass]

使用一个技能创建一个结界,进入结界内的导弹速度将降为N的做法也大同小异,这里就不给出代码了,需要的可以自己做做看。

3.linken失效
[jass]function linken_filter takes nothing returns boolean
    local unit u = GetFilterUnit()
    if GetUnitAbilityLevel(u,'导弹标识技能') > 0 then
        set s__MissileData_valid[GetUnitUserData(u)] = false
    endif
    set u = null
    return false
endfunction

function linken_Conditions takes nothing returns boolean
    local group g
    local unit u = GetTriggerUnit()
    if  GetSpellAbilityId() == 'ANss'  then
        set g = CreateGroup()
        call GroupEnumUnitsInRange(g,GetUnitX(u),GetUnitY(u),1000.,Condition(function linken_filter))
        call DestroyGroup(g)
        set g = null
    endif
    set u = null
    return false
endfunction[/jass]

4.解决蝗虫单位无法被枚举
因为本人能力有限,这里提供3种解决的思路。如果大家有其他的思路,欢迎提供。
⑴建立一个单位数组,创建一个导弹单位就记录在单位数组里面,死亡的时候从数组种移除,并把数组指针拨回这个单位的数组位置。当施放以上技能的时候,用loop判断单位的s__MissileData_target[GetUnitUserData(该单位)] 是否等于施放上述技能的单位,如果等于就更改有效值为false。因为你不可能在同一时间有成百上千个导弹单位,所以这样的耗时并不是很大。
⑵选中单位如果有'导弹标识技能'就将该单位的选择移除。当然这样的做法玩家会发现还是可以选择导弹单位的。
⑶使用动态注册,在模拟导弹函数当中,动态注册触发器事件,把导弹单位绑定在触发器上。当目标单位施放以上技能(为了简化你可以建立线性表)的时候,就把导弹单位的有效值设置为false

评分

参与人数 1威望 +63 收起 理由
kook + 63 原创内容

查看全部评分

发表于 2008-12-8 03:21:56 | 显示全部楼层
根据我以前做timerSYS的经验给楼主点建议,在这个函数MissileStart里面的参数是这样的
导弹单位,导弹初始x,导弹初始y
导弹目标单位,导弹目标x,导弹目标y
导弹命中半径
导弹移动速度
导弹最大存活时间
是否在目标瞬移超过x后仍然追随目标
是否在目标死亡后杀死/删除导弹
是否允许在命中目标后自动杀死/删除导弹
导弹携带参数
回复

使用道具 举报

 楼主| 发表于 2008-12-8 22:14:51 | 显示全部楼层
这个也太复杂了吧…可能要看你自己的图了,根据自己的需要加数据。不过我还是觉得很多没必要啊,比如导弹击中后是否删除,还有导弹存活时间…弱弱的问下什么是瞬移超过x?
回复

使用道具 举报

发表于 2008-12-8 22:37:32 | 显示全部楼层
嗯。添加了'Aloc'的单位还可以触发进入不规则区域的事件的,通过这个可以获取到创建的这些投射物。

当一个单位释放静止投射物的技能后,可以创建一个圆形的不规则区域,当进入以后,就可以改变它的相关参数了。不知道我说的对不对。

画圆的函数如下:
[codes=jass]//! define Region_C(n,r,x,y) Region_Circle(n,r,x,y,null)
//! define Region_Cunit(n,r,unit) Region_Circle(n,r,GetUnitX(unit),GetUnitY(unit),null)
//! define Region_Csimple(r,x,y) Region_Circle(20,r,x,y,null)
//! define Region_Csmu(r,unit) Region_Circle(20,r,GetUnitX(unit),GetUnitY(unit),null)
//! define Pi 3.1415926
function Region_Circle takes integer n,real r,real x,real y,region Re returns region
local real l_x
local real l_y
local integer i=1
local real rad
local rect rc=null
if(n<=0)then
    return null
endif

if(n-n/2*2==1)then
    set n=n+1
endif
set Re=CreateRegion()
set rc=Rect(-1,-1,1,1)
set rad=Pi/n/2
loop
    set l_x=r*Cos(rad*i)
    set l_y=r*Sin(rad*i)
    call SetRect(rc,x-l_x,y-l_y,x+l_x,y+l_y)
    call RegionAddRect(Re,rc)
    set i=i+1
    exitwhen i>=n
endloop
call RemoveRect(rc)
set rc=null
return Re
endfunction[/codes]

修改了下,循环里面的语句少了些,经测试n=20~30时,效果就不错了,1000半径是误差在-10~+30之间。

aaaaa.w3x

21 KB, 下载次数: 37

可以实验,查看下效果

回复

使用道具 举报

 楼主| 发表于 2008-12-8 23:01:03 | 显示全部楼层
因为没接触过不规则区域,所以创建的那段不是很明白……可不可以稍微解释一下啊…是用矩形拼凑的么?
回复

使用道具 举报

发表于 2008-12-9 08:24:18 | 显示全部楼层
嗯。就是用矩形拼起来的。[codes=jass]function Region_Circle takes integer n,real r,real x,real y,region Re returns region[/codes]
这里面的n的大小是矩形数量加1,r是组成圆的半径,xy是圆心,Re没啥用,防泄露的。
演示里面很直观的,估计你看一眼就明白了。

Vjass我看不懂,但是LZ做的这个可以实现SC2母船的时间炸弹的效果,所以献丑提供段代码,顺便继续关注。嘿嘿
回复

使用道具 举报

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

本版积分规则

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

GMT+8, 2024-5-3 18:08 , Processed in 0.182143 second(s), 22 queries .

Powered by Discuz! X3.5

© 2001-2023 Discuz! Team.

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