- Joined
- May 24, 2016
- Messages
- 357
Hello, im trying to make my projectile work properly as intended. To be honest, I got sick last days and probably my head is too dizzy to solve anything. I made a too complex projectile spell to make it work considering my current condition (and Im just kinda dumb anyway for something like that)
Phase 0:
Phase 0:
- Launch phase
- Projectile starts with strong vertical velocity (<span>vz</span>)
- Very small horizontal movement
- Gradually loses upward force because of simulated gravity
- Slowly drifts toward target direction
- Glide / stabilization phase
- Projectile begins gaining horizontal speed
- Vertical speed becomes near-zero or slightly negative
- Smooth turning toward target using limited turn rate
- Intended to look like a cruise missile leveling out in the air
- Dive / attack phase
- Projectile aggressively homes toward target
- Gravity increases strongly
- Horizontal speed accelerates
- Projectile dives toward the ground and explodes on ground contact
- Projectile stores custom x/y/z coordinates manually
- Uses angle interpolation instead of instant turning
- Uses separate vertical velocity (<span>vz</span>) and horizontal speed
- Projectile can optionally semi-home toward a moving target
- Explosion occurs when projectile reaches low enough z height during phase 2
- Goal is cinematic movement:
sharp launch ↗
climb ↗
glide →
dive ↘
acceleration ↘↘
impact
Issues:- After phase 0, projectile movement becomes jittery/shaky.
The missile appears to constantly micro-correct its direction, causing unnatural movement instead of smooth glide or dive behavior. - Height consistency is unstable.
Some projectiles enter phase 1 at a very high altitude, while others transition much lower, even under seemingly similar conditions/distances. - Rarely, the projectile physics completely break.
Possible failure cases include: - infinite diving
- spinning/circling in the air
- sliding/flying along the ground without exploding
- continuing movement after visually touching the ground
The projectile is supposed to explode once it reaches a low enough height during phase 2, but in some edge cases this condition fails or becomes unstable.
- After phase 0, projectile movement becomes jittery/shaky.
JASS:
function Watcher_L takes nothing returns nothing //launches missile
local timer t=GetExpiredTimer()
local integer idT=GetHandleId(t)
local unit wt=LoadUnitHandle(Hash,idT,0)
local unit c=LoadUnitHandle(Hash,idT,1)
//local unit F=LoadUnitHandle(Hash,idT,3)
local real r=LoadReal(Hash,idT,2)-1.
local real x
local real y
local real x2
local real y2
local real totalDist
local real maxHeight
local timer mt
local integer id
local unit missile
if not IsUnitDead(wt) then
set x=GetUnitX(wt)
set y=GetUnitY(wt)
//
// target
//
set I3 = GetPlayerId(GetOwningPlayer(c))
set F = GetClosestUnit2(x,y,Condition(function ChainLightning_F))
if F != null and DistanceXY(x,y,GetUnitX(F),GetUnitY(F)) <= 850 then
set x2=GetUnitX(F)
set y2=GetUnitY(F)
//
// distance
//
set totalDist=SquareRoot((x2-x)*(x2-x)+(y2-y)*(y2-y))
//
// adaptive height
//
set maxHeight=325.+totalDist*0.45
if maxHeight>620. then
set maxHeight=620.
endif
if maxHeight<300. then
set maxHeight=300.
endif
//
// missile dummy
//
set missile=CreateUnit(GetOwningPlayer(c),'dumi',x,y,0)
call UnitAddAbility(missile,'Amrf')
call UnitRemoveAbility(missile,'Amrf')
call SetUnitFlyHeight(missile,0.,0.)
//
// model
//
call AddSpecialEffectTarget("Abilities\\Weapons\\FireBallMissile\\FireBallMissile.mdl",missile,"origin")
call SetUnitScale(missile,0.40,0.40,0.40)
//
// missile timer
//
set mt=CreateTimer()
set id=GetHandleId(mt)
//
// save units
//
call SaveUnitHandle(Hash,id,StringHash("dummy"),missile)
call SaveUnitHandle(Hash,id,StringHash("caster"),c)
call SaveUnitHandle(Hash,id,StringHash("target"),F)
//
// save coords
//
call SaveReal(Hash,id,StringHash("x"),x)
call SaveReal(Hash,id,StringHash("y"),y)
call SaveReal(Hash,id,StringHash("z"),0.)
call SaveReal(Hash,id,StringHash("tx"),x2)
call SaveReal(Hash,id,StringHash("ty"),y2)
//
// physics
//
call SaveReal(Hash,id,StringHash("vz"),24.)
call SaveReal(Hash,id,StringHash("speed"),1.5)
call SaveReal(Hash,id,StringHash("angle"),Atan2(y2-y,x2-x))
//
// movement state
//
call SaveInteger(Hash,id,StringHash("phase"),0)
//
// misc
//
call SaveReal(Hash,id,StringHash("totalDist"),totalDist)
call SaveReal(Hash,id,StringHash("traveled"),0.)
call SaveReal(Hash,id,StringHash("maxHeight"),maxHeight)
call SaveReal(Hash,id,StringHash("scale"),0.40)
//
// debug
//
call MyUtils_echo("LAUNCH x="+R2S(x)+" y="+R2S(y)+" tx="+R2S(x2)+" ty="+R2S(y2)+" dist="+R2S(totalDist))
//
// start missile physics
//
call TimerStart(mt,0.03,true,function WatcherShot_OnPeriod)
endif
else
call FlushChildHashtable(Hash,idT)
call PauseTimer(t)
call DestroyTimer(t)
endif
if r<=0. then
call FlushChildHashtable(Hash,idT)
call PauseTimer(t)
call DestroyTimer(t)
else
call SaveReal(Hash,idT,2,r)
endif
set missile=null
set mt=null
set wt=null
set c=null
set F=null
set t=null
endfunction
// Period
function WatcherShot_OnPeriod takes nothing returns nothing
local timer t=GetExpiredTimer()
local integer id=GetHandleId(t)
local unit missile=LoadUnitHandle(Hash,id,StringHash("dummy"))
local unit caster=LoadUnitHandle(Hash,id,StringHash("caster"))
local unit target=LoadUnitHandle(Hash,id,StringHash("target"))
local real x=LoadReal(Hash,id,StringHash("x"))
local real y=LoadReal(Hash,id,StringHash("y"))
local real z=LoadReal(Hash,id,StringHash("z"))
local real tx=LoadReal(Hash,id,StringHash("tx"))
local real ty=LoadReal(Hash,id,StringHash("ty"))
local real vz=LoadReal(Hash,id,StringHash("vz"))
local real speed=LoadReal(Hash,id,StringHash("speed"))
local real angle=LoadReal(Hash,id,StringHash("angle"))
local integer phase=LoadInteger(Hash,id,StringHash("phase"))
local real totalDist=LoadReal(Hash,id,StringHash("totalDist"))
local real traveled=LoadReal(Hash,id,StringHash("traveled"))
local real maxHeight=LoadReal(Hash,id,StringHash("maxHeight"))
local real scale=LoadReal(Hash,id,StringHash("scale"))
local real targetAngle
local real angleDiff
local real turnRate
local real dist
local real moveX
local real moveY
//
// current target position
//
if target!=null and UnitAlive(target) then
set tx=GetUnitX(target)
set ty=GetUnitY(target)
endif
//
// distance
//
set dist=SquareRoot((tx-x)*(tx-x)+(ty-y)*(ty-y))
//
// target angle
//
set targetAngle=Atan2(ty-y,tx-x)
//
// angle difference
//
set angleDiff=targetAngle-angle
if angleDiff>bj_PI then
set angleDiff=angleDiff-2.*bj_PI
endif
if angleDiff<-bj_PI then
set angleDiff=angleDiff+2.*bj_PI
endif
//
// ==================================================
// PHASE 0
// LAUNCH
// ==================================================
//
if phase==0 then
set turnRate=0.003
if angleDiff>turnRate then
set angle=angle+turnRate
elseif angleDiff<-turnRate then
set angle=angle-turnRate
endif
//
// minimal drift
//
if speed<1.4 then
set speed=speed+0.03
endif
//
// strong upward impulse
//
set vz=vz-0.32
//
// prevent infinite climb
//
if vz<5. then
set vz=5.
endif
//
// movement
//
set moveX=speed*Cos(angle)
set moveY=speed*Sin(angle)
set x=x+moveX
set y=y+moveY
set z=z+vz
set traveled=traveled+speed
//
// transition
//
if z>=maxHeight or traveled>=totalDist*0.16 then
set phase=1
call MyUtils_echo("P0->P1 z="+R2S(z)+" dist="+R2S(dist))
endif
//
// blue
//
call SetUnitVertexColor(missile,80,140,255,255)
//
// ==================================================
// PHASE 1
// GLIDE
// ==================================================
//
elseif phase==1 then
set turnRate=0.010
if angleDiff>turnRate then
set angle=angle+turnRate
elseif angleDiff<-turnRate then
set angle=angle-turnRate
else
set angle=targetAngle
endif
//
// smooth horizontal acceleration
//
set speed=speed+0.05
if speed>8. then
set speed=8.
endif
//
// slight gravity
//
set vz=vz-0.09
if vz<-1.2 then
set vz=-1.2
endif
//
// movement
//
set moveX=speed*Cos(angle)
set moveY=speed*Sin(angle)
set x=x+moveX
set y=y+moveY
set z=z+vz
set traveled=traveled+speed
//
// phase 2 transition
//
if dist<=totalDist*0.45 then
set phase=2
set vz=-2.5
call MyUtils_echo("P1->P2 z="+R2S(z)+" dist="+R2S(dist))
endif
//
// scale growth
//
if scale<2.20 then
set scale=scale+0.11
if scale>2.20 then
set scale=2.20
endif
call SetUnitScale(missile,scale,scale,scale)
endif
//
// green
//
call SetUnitVertexColor(missile,100,255,120,255)
//
// ==================================================
// PHASE 2
// DIVE
// ==================================================
//
else
//
// aggressive steering
//
set turnRate=0.035
if angleDiff>turnRate then
set angle=angle+turnRate
elseif angleDiff<-turnRate then
set angle=angle-turnRate
else
set angle=targetAngle
endif
//
// acceleration
//
set speed=speed+0.22
if speed>26. then
set speed=26.
endif
//
// strong gravity
//
set vz=vz-1.10
//
// movement
//
set moveX=speed*Cos(angle)
set moveY=speed*Sin(angle)
set x=x+moveX
set y=y+moveY
set z=z+vz
set traveled=traveled+speed
//
// red
//
call SetUnitVertexColor(missile,255,90,90,255)
endif
//
// ground impact
//
if z<=15. and phase==2 then
call MyUtils_echo("HIT x="+R2S(x)+" y="+R2S(y)+" dist="+R2S(dist))
call DestroyEffect(AddSpecialEffect("war3mapImported\\Tidal Burst - Classic.mdx",x,y))
call GroupEnumUnitsInRange(SpellIndex.GLOBAL_GROUP,x,y,350.,null)
loop
set target=FirstOfGroup(SpellIndex.GLOBAL_GROUP)
exitwhen target==null
call GroupRemoveUnit(SpellIndex.GLOBAL_GROUP,target)
if UnitAlive(target) and IsUnitEnemy(target,GetOwningPlayer(caster)) then
call UnitDamageTarget(caster,target,300.,false,false,ATTACK_TYPE_NORMAL,DAMAGE_TYPE_MAGIC,null)
endif
endloop
call RemoveUnit(missile)
call FlushChildHashtable(Hash,id)
call PauseTimer(t)
call DestroyTimer(t)
set missile=null
set caster=null
set target=null
set t=null
return
endif
//
// apply movement
//
call SetUnitX(missile,x)
call SetUnitY(missile,y)
call SetUnitFlyHeight(missile,z,0.)
call SetUnitFacing(missile,angle*bj_RADTODEG)
//
// save
//
call SaveReal(Hash,id,StringHash("x"),x)
call SaveReal(Hash,id,StringHash("y"),y)
call SaveReal(Hash,id,StringHash("z"),z)
call SaveReal(Hash,id,StringHash("tx"),tx)
call SaveReal(Hash,id,StringHash("ty"),ty)
call SaveReal(Hash,id,StringHash("vz"),vz)
call SaveReal(Hash,id,StringHash("speed"),speed)
call SaveReal(Hash,id,StringHash("angle"),angle)
call SaveReal(Hash,id,StringHash("traveled"),traveled)
call SaveReal(Hash,id,StringHash("scale"),scale)
call SaveInteger(Hash,id,StringHash("phase"),phase)
set missile=null
set caster=null
set target=null
set t=null
endfunction
Last edited:


