HomeUser Control Panel (unavailable in archive)ForumsTutorialsArt GalleryResourcesMaps

Setting unit's Z over uneven terrain acting strangely

01-21-2009, 08:36 PM#1
the-thingy
For some reason I can't explain (whether it's just something obvious I'm not seeing, or the math involved), my dummy projectile starts going crazy over uneven terrain.

Firstly, the dummy is supposed to travel in a parabolic path, but it keeps adjusting itself to terrain height (even though my function that sets the dummy's Z is meant to cater for that...).

Secondly, I'm using dummy.mdx and trying to get the projectile pointing in the right direction in flight, but that's going crazy and ends up facing up, down, up, down (and so on).

I'll cut out the configurables, the only one which is actually relevant to the parabola is .CURVE, which has a value of 2.5

Collapse JASS:
struct PB

//Required globals    
    private static location tempLoc = Location (0,0)
    private static group genGroup = CreateGroup ()
    private static timer spellTimer = CreateTimer ()
    private static PB array data
    private static PB gdata = 0
    private static integer N = 0
    private static real groupX = 0
    private static real groupY = 0
    private static real doublePi = bj_PI * 2
    private static real offsetX
    private static real offsetY
    private static real MinX = GetRectMinX (bj_mapInitialPlayableArea)
    private static real MaxX = GetRectMaxX (bj_mapInitialPlayableArea)
    private static real MinY = GetRectMinY (bj_mapInitialPlayableArea)
    private static real MaxY = GetRectMaxY (bj_mapInitialPlayableArea)
    
    
//Required struct members
    private unit caster
    private unit proj
    private real speed
    private real pcos
    private real psin
    private real curdist
    private real maxdist
    private effect pfx
    
    
    
//Required helper functions
    private static method RingEffect takes string fxString, real ringCount, real effectCount, real offset, real x, real y returns nothing
        local integer i1 = 1
        local integer i2 = 1
        local real angle = GetRandomReal (0, .doublePi)
        local real x2
        local real y2
        loop
        exitwhen i1 > ringCount
            loop
            exitwhen i2 > effectCount
                set angle = angle + .doublePi/effectCount
                set x2 = x + Cos (angle) * (i1 * offset)
                set y2 = y + Sin (angle) * (i1 * offset)
                call DestroyEffect(AddSpecialEffect (fxString, x2, y2))
                set i2 = i2 + 1
            endloop
            set i1 = i1 + 1
        endloop
    endmethod


    private static constant method JumpParabola takes real dist, real maxdist,real curve returns real
        local real t = (dist*2)/maxdist-1
        return (-t*t+1)*(maxdist/curve)
    endmethod
    
    
    private static method SetUnitXY takes unit u,real x,real y returns nothing
        if x < .MaxX and x > .MinX and y < .MaxY and y > .MinY then
            call SetUnitX(u,x)
            call SetUnitY(u,y)
        endif
    endmethod
    
    
    private static method GetCoordZ takes real x, real y returns real
        call MoveLocation (.tempLoc, x, y)
        return GetLocationZ (.tempLoc)
    endmethod

    
    private static method SetUnitZ takes unit u, real h returns boolean
        local real z = h - .GetCoordZ (GetUnitX (u), GetUnitY (u))
        if z > 0 then
            call SetUnitFlyHeight (u, z, 0)
            return false
        else
            return true
        endif
    endmethod
    
    private static method GetUnitZ takes unit u returns real
        return .GetCoordZ (GetUnitX (u), GetUnitY (u)) + GetUnitFlyHeight (u)
    endmethod
    
    
    private static method GetRandOffset takes real x, real y, real dist returns nothing
        local real discrepdist = ((dist - .ACC_RANGE)/.ACC_DISCREP_MULTI)*.ACC_DISCREP
        local real angle = GetRandomReal (0, .doublePi)
        set .offsetX = x + Cos (angle) * discrepdist
        set .offsetY = y + Sin (angle) * discrepdist
    endmethod
    
    

    
    
//Spell functions
    private static method GroupFunc takes nothing returns boolean
        local PB a = .gdata
        local unit u = GetFilterUnit ()
        local real ux = GetUnitX (u)
        local real uy = GetUnitY (u)
        local real x = .groupX - ux
        local real y = .groupY - uy
        local real dist = SquareRoot (x*x + y*y)
        local real percent = (.DamageRadius (a.caster) - dist)/.DamageRadius (a.caster)
        local real dmg
        if percent < .MIN_DMG_PERCENT then
            set percent = .MIN_DMG_PERCENT
        endif
        set dmg = .Damage (a.caster) * percent
        if IsUnitEnemy (u, GetOwningPlayer (a.caster)) then
            call UnitDamageTarget (a.caster, u, dmg, false, true, .ATYPE, .DTYPE, .WTYPE)
        endif
        set u = null
        return false
    endmethod

    
    private static method Move takes nothing returns nothing
        local integer i = .N
        local real fxAngle = 0
        local real x = 0
        local real y = 0
        local real x2 = 0
        local real y2 = 0
        local real z = 0
        local real uz = 0
        local real ex = 0
        local real ey = 0
        local boolean b = false
        local real hDist = 0
        local real zDif = 0
        local integer animindex = 0
        local PB a
        loop
        exitwhen i == 0
            set a = .data[i]
            set x = GetUnitX (a.proj)
            set y = GetUnitY (a.proj)
            set x2 = x + a.pcos*a.speed
            set y2 = y + a.psin*a.speed
            set z = .JumpParabola (a.curdist, a.maxdist, .CURVE)
            set uz = .GetUnitZ (a.proj)
            set hDist = SquareRoot ((x2-x)*(x2-x) + (y2-y)*(y2-y))
            set zDif = z - uz
            set animindex = R2I (Atan2 (zDif, hDist) * bj_RADTODEG + 90)
            
            if animindex >= 0 and animindex <= 180 then
                call SetUnitAnimationByIndex (a.proj, animindex)
            endif
            
            call .SetUnitXY (a.proj, x2, y2)
            set b = .SetUnitZ (a.proj, z)
            
            if b and a.curdist > 0 then
                if .RESET_DUMMY then
                    call SetUnitAnimationByIndex (a.proj, 0)
                endif
                call DestroyEffect (a.pfx)
                set a.curdist = 0
                call KillUnit (a.proj)
                set .gdata = a
                call GroupEnumUnitsInRange (.genGroup, x, y, .DamageRadius (a.caster), Condition (function PB.GroupFunc))
                call .RingEffect (.END_FX, .RING_COUNT, .FX_COUNT, .RING_OFFSET, x, y)
                call a.destroy ()
                set .data[i] = .data[.N]
                set .N = .N - 1
                
                if .N == 0 then
                    call PauseTimer (.spellTimer)
                endif
            endif
            
            set a.curdist = a.curdist + a.speed
            set i = i - 1
        endloop
    endmethod

    
    private static method SpellCond takes nothing returns boolean
        return GetSpellAbilityId () == .SID
    endmethod
    
    
    private static method SpellActions takes nothing returns nothing
        local PB a
        local integer i = 0
        local unit u
        local unit c = GetTriggerUnit ()
        local real cx = GetUnitX (c)
        local real cy = GetUnitY (c)
        local real sx
        local real sy
        local location l = GetSpellTargetLoc ()
        local real tx = GetLocationX (l)
        local real ty = GetLocationY (l)
        local real x = tx - cx
        local real y = ty - cy
        local real rx2
        local real ry2
        local real angle = Atan2 (y, x)
        local real dist = SquareRoot (x*x + y*y)
        local real angle2
        local real dist2
//ix, iy and idist are testing variables
        local real ix
        local real iy
        local real idist
        //set rx = cx + Cos (angle) * dist
        //set ry = cy + Sin (angle) * dist
        call RemoveLocation (l)
        set l = null
        
        if .N == 0 then
            call TimerStart (.spellTimer, .TIMER_INT, true, function PB.Move)
        endif
        
        loop
        exitwhen i == .PROJCOUNT
           if dist < .MIN_RANGE then
                set angle = GetRandomReal (0, .doublePi)
           endif
           set a = PB.create ()
           set a.caster = c
           set sx = cx + Cos (GetRandomReal (0, .doublePi)) * GetRandomReal (0, .SPAWN_OFFSET)
           set sy = cy + Sin (GetRandomReal (0, .doublePi)) * GetRandomReal (0, .SPAWN_OFFSET)
           set u = CreateUnit (GetOwningPlayer (a.caster), .LID, sx, sy, angle * bj_RADTODEG)
           call SetUnitVertexColor (u, .LR, .LG, .LB, .LA)
           call SetUnitScale (u, .LS, .LS, .LS)
           call SetUnitAnimation (u, .ANIM)
           call QueueUnitAnimation (u, "stand")
           call UnitApplyTimedLife (u, 'BTLF', .LIFESPAN)
           if dist <= .ACC_RANGE then
                set angle2 = GetRandomReal (0, .doublePi)
                set dist2 = GetRandomReal (0, .TARGET_OFFSET)
                set rx2 = tx + Cos (angle) * dist2
                set ry2 = ty + Sin(angle) * dist2
           else
                call .GetRandOffset (tx, ty, dist)
                set rx2 = .offsetX
                set ry2 = .offsetY
           endif
           set x = rx2-cx
           set y = ry2-cy
           set ix = rx2 - tx
           set iy = ry2 - ty
           set idist = SquareRoot (ix*ix + iy*iy)
           set angle = Atan2 (y, x)
           set a.pcos = Cos (angle)
           set a.psin = Sin (angle)
           set a.maxdist = SquareRoot (x*x + y*y)
           if a.maxdist < .MIN_RANGE then
                set angle = GetUnitFacing (u) * bj_DEGTORAD
                set a.maxdist = a.maxdist + .MIN_RANGE
                set a.pcos = Cos (angle)
                set a.psin = Sin (angle)
           endif
           set a.proj = CreateUnit (GetOwningPlayer (a.caster), .PID, sx, sy, angle * bj_RADTODEG)
           call SetUnitVertexColor (a.proj, .PR, .PG, .PB, .PA)
           call UnitAddAbility (a.proj, 'Arav')
           call UnitRemoveAbility (a.proj, 'Arav')
           set a.curdist = 0
           set a.speed = GetRandomReal (.SPEED_LOW, .SPEED_HIGH) * .TIMER_INT
           set a.pfx = AddSpecialEffectTarget (.PROJ_FX, a.proj, .ATTACH_PT)
           set i = i + 1
           set .N = .N + 1
           set .data[.N] = a
        endloop
        
        set c = null
        set u = null
    endmethod
    
    
    private static method onInit takes nothing returns nothing
        local trigger t = CreateTrigger ()
        call TriggerRegisterAnyUnitEventBJ (t, EVENT_PLAYER_UNIT_SPELL_EFFECT)
        call TriggerAddCondition (t, Condition (function PB.SpellCond))
        call TriggerAddAction (t, function PB.SpellActions)
        if .ALLOW_PRELOAD then
            call KillUnit (CreateUnit (Player (0), .LID, 0, 0, bj_UNIT_FACING))
            call KillUnit (CreateUnit (Player (0), .PID, 0, 0, bj_UNIT_FACING))
            call DestroyEffect (AddSpecialEffect (.PROJ_FX, 0, 0))
            call DestroyEffect (AddSpecialEffect (.END_FX, 0, 0))
        endif
        set .MinX = GetRectMinX (bj_mapInitialPlayableArea)
        set .MaxX = GetRectMaxX (bj_mapInitialPlayableArea)
        set .MinY = GetRectMinY (bj_mapInitialPlayableArea)
        set .MaxY = GetRectMaxY (bj_mapInitialPlayableArea)
    endmethod
endstruct

Also, I've tested on gentle terrain height changes (Terrain -> Height -> Raise) and cliffs (Terrain -> Grass Cliff) and the problem is occuring for both (although far more extreme on the cliffs)

Also, who is the author of dummy.mdx? ???? isn't much of a credit to the author
01-21-2009, 10:06 PM#2
Anitarf
Is your projectile unit a flying unit by any chance?
01-21-2009, 11:11 PM#3
Ammorth
I believe vexorian was the author of dummy.mdx, but I could be wrong.

As Anitarf said, make your unit movement type hover and then add and remove the crow-form ability.
01-22-2009, 12:33 AM#4
Bobo_The_Kodo
Quote:
As Anitarf said, make your unit movement type hover and then add and remove the crow-form ability.
Hover means you don't have to add / remove crow form
01-22-2009, 12:50 AM#5
Joker
Quote:
Originally Posted by Bobo_The_Kodo
Hover means you don't have to add / remove crow form
no.
01-22-2009, 01:01 AM#6
chobibo
I think GetLocationZ returns the terrain z value without the unit's flyheight.
Collapse JASS:
call SetUnitFlyHeight (u, z+GetUnitFlyHeight(unit), 0)
01-22-2009, 01:21 AM#7
Here-b-Trollz
The issue, as Anitarf is saying in a sly way, is that flying units are hardcoded to try to smooth out their heights over terrain, so they don't move up and down as you would want them too, since you're also moving them left and right.
01-22-2009, 04:38 PM#8
the-thingy
Quote:
Is your projectile unit a flying unit by any chance?
Quote:
I believe vexorian was the author of dummy.mdx, but I could be wrong.

As Anitarf said, make your unit movement type hover and then add and remove the crow-form ability.
It was - just changed to Hover, but the units are still bobbing up and down with the terrain height.

And thanks for letting me know who made dummy.mdx :)

Quote:
Hover means you don't have to add / remove crow form
Tried that, and the projectiles hugged the ground.

Quote:
I think GetLocationZ returns the terrain z value without the unit's flyheight.
Well, I don't think I need to factor in the unit's fly height. The intended height should be controlled by set z = .JumpParabola (a.curdist, a.maxdist, .CURVE)
If I factored the unit's flyheight into that, it would (approximately) achieve double the height that it should (since the dummy's height would be 20-40 below the value of z above) at normal fly height
01-22-2009, 04:55 PM#9
Zerzax
Collapse JASS:
    set z = .JumpParabola (a.curdist, a.maxdist, .CURVE) + GetLocationZ(loc)