• Listen to a special audio message from Bill Roper to the Hive Workshop community (Bill is a former Vice President of Blizzard Entertainment, Producer, Designer, Musician, Voice Actor) 🔗Click here to hear his message!
  • Read Evilhog's interview with Gregory Alper, the original composer of the music for WarCraft: Orcs & Humans 🔗Click here to read the full interview.

[vJASS] Cannonball script

Status
Not open for further replies.
Hi!

I have this cannonball struct i made for my map that moves the balls using vectors and corrects their height by saving the last terrain height and comparing it to the current height every cycle (and then substracting or adding the difference to the current height).

My problem is that the ball doesn't appear corrected at all, infact, it bounces up and down adn tend to go up and then crash dive when moving up steep slopes.

Please let me know if this is just a fault in the SetunitFlyHeight native or if it just me who fail at coding!

Here is the script:

JASS:
library Cannonball requires ListModule, GroupUtils

globals
    private location LOC
    private constant real INTERVAL = 0.038  //loop interval
    private constant real VEL = 2200*INTERVAL  //bullet velocity
    private constant real G = 9.81*INTERVAL  //the constant of gravity
endglobals


private function BallDoDamage takes nothing returns boolean
    local Knockback K
    local ball B = bj_forLoopAIndex  //i needed a global variable to pass over the ball struct, so i borrowed this one!
    
    if (IsUnitType(GetFilterUnit(), UNIT_TYPE_GIANT) == false ) then
      if (IsUnitType(GetFilterUnit(), UNIT_TYPE_ANCIENT) == false ) then
        if (GetUnitFlyHeight(B.u) < 200) then
          if (GetUnitState(GetFilterUnit(), UNIT_STATE_LIFE) > 0.5) then
               call KillUnit(GetFilterUnit())
               set K = Knockback.create(GetFilterUnit(), GetRandomReal(100, 150), GetRandomReal(0.5, 0.8), B.face)
               call SetUnitAnimation(GetFilterUnit(), "spell slam")
               call SetUnitFacing(GetFilterUnit(), ModuloReal(B.face-180, 360))
           return true
        endif
      endif
    endif
  endif
    
    return false
endfunction


struct ball 
    implement List
    real xv    //x, y, and z vectors
    real yv 
    real zv
    real last_z
    real ang 
    real face
    unit u
    
    private static timer ticker

  static method create takes real x, real y, real tar_x, real tar_y returns ball
    local thistype this = thistype.allocate()
    local real dx = tar_x - x
    local real dy = tar_y - y
    local real dist = SquareRoot(dx * dx + dy * dy)
    local real ang = 0.5*(Asin((G*dist)/(VEL*VEL))* bj_RADTODEG)
    local real f = bj_RADTODEG * Atan2(tar_y - y, tar_x - x)  //angle between start and target coordinates
    local real z
    local real z_diff
    local unit u = CreateUnit(Player(PLAYER_NEUTRAL_PASSIVE), 'h015', x, y, f)
    
    call .listAdd()
  
    if .count == 1 then
        call TimerStart(.ticker, INTERVAL, true, function thistype.moveballs)
    endif


  

    call MoveLocation(LOC, tar_x, tar_y)
    set z = GetLocationZ(LOC)
    call MoveLocation(LOC, x, y)
    set z_diff = GetLocationZ(LOC)-z

    set .face = f
    set .ang = (90-Atan((0.5*G*dist)/(VEL*VEL)) * bj_RADTODEG)-(Asin(z_diff/dist)*bj_RADTODEG)

    set .u       = u
    set .last_z   = GetLocationZ(LOC)
    set .xv      = VEL*Cos(.face*bj_DEGTORAD)*Sin(.ang*bj_DEGTORAD)
    set .yv      = VEL*Sin(.face*bj_DEGTORAD)*Sin(.ang*bj_DEGTORAD)
    set .zv      = VEL*Cos(.ang*bj_DEGTORAD)
    
  
    set u = null

    return this
  endmethod

  private static method moveballs takes nothing returns nothing
    local thistype this = .first
    local group damage = NewGroup()
    local real z_diff
    local real x
    local real y


    loop
        exitwhen this == 0
        
        set x = GetUnitX(.u)
        set y = GetUnitY(.u)
        call MoveLocation(LOC, x, y)
        
        
        
        set z_diff = .last_z-GetLocationZ(LOC)   //this is where the height correction comes in. I substract the current Z coordinate from the last..
        set .last_z = GetLocationZ(LOC)  //and then sets last coordinate to current, for the next loop.
        set .zv = .zv-G  //substracts gravity from Z vector.
  
        call SetUnitX(.u, x+.xv)
        call SetUnitY(.u, y+.yv)
        call SetUnitFlyHeight(.u, GetUnitFlyHeight(.u)+.zv+z_diff, 0)
      
        set bj_forLoopAIndex = this
        call GroupEnumUnitsInRange(damage, x, y, 65., Filter(function BallDoDamage))
        if (GetUnitFlyHeight(.u) < 1) then
          if (.zv < 0) then
            call KillUnit(.u)
            call .destroy()
          else
            call .bounce()
          endif
        endif
      
      
        if .count < 1 then
          call PauseTimer(.ticker)
        endif


            set this = .next
        endloop
    call ReleaseGroup(damage)
  endmethod
  
  method bounce takes nothing returns nothing
  //some code here that is being replaced, nothing important
  endmethod

  method onDestroy takes nothing returns nothing
    set this.u = null
    call .listRemove()
  endmethod

  
  private static method onInit takes nothing returns nothing
    set .ticker = CreateTimer()
  endmethod
endstruct

endlibrary


Also, The_Reborn_Devil was so nice as to help me with the formula for calculating required launch angle for the ball to reach target x/y, but sadly, it does not compensate for target height (that is why i added the "-(Asin(z_diff/dist)*bj_RADTODEG)" ).

If someone can come up with ideas for making a viable formula for this, please tell me! It would be great to have some nice accurate cannonballs going!
 
usually I do it like this:
ini - save the starting height (including terrain height), lets call it z0
loop - subtract current height from z0 and set it to flying height

if you have any weird glitches at cliff edges use a walking dummy with storm crow form instead of a flying dummy (is caused by pitch angle or something)

Also, The_Reborn_Devil was so nice as to help me with the formula for calculating required launch angle for the ball to reach target x/y, but sadly, it does not compensate for target height (that is why i added the "-(Asin(z_diff/dist)*bj_RADTODEG)" ).

If someone can come up with ideas for making a viable formula for this, please tell me! It would be great to have some nice accurate cannonballs going!

I tried that once but the formula was quite long
I'll waste the next few hours to calculate it again

edit:
this might be the solution (not 100% sure since there should be 2 real solutions but there are 2 with i instead so I am a little confused)
http://www.wolframalpha.com/input/?i=solve+ad^2%2F%282s%29%2B%28v^2-x^2%29x%2Fs%2Bx^2%3D0+for+x

x is the x-velocity
xvel^2 + yvel^2 = velocity
a is acceleration (gravity, -9.8)
s is the height difference between start and end point
d is the distance between start and end point

approach was to use the formula
0 = 0.5*a*t^2 + y * t + s
which represents a ball thrown with speed y straight into the air and the correlations
t = d/x which represents the time it takes to cross the distance d with speed x
where x^2 + y^2 = v^2

edit2:
going for the angle directly (by plugging in sin(a)*v=vx and so on) yields even longer formulas so you really should try to approximate the solution

edit3:
and as always there is a wikipedia article about it
http://en.wikipedia.org/wiki/Trajectory
why should I even bother thinking about something
someone else did it already
I hate the internet o_O
 
Last edited:
Actually, after having tried it extensively, with maybe one or two tweaks, i would actually say that my current formula produces perfect shots every time, not sure if it is the same one as i posted in here, but here's how it looks:

JASS:
(90-Atan((0.5*G*dist)/(VEL*VEL)) * bj_RADTODEG)-(Asin(z_diff/dist)*bj_RADTODEG)

It is actually so accurate that i had to add a random inaccuracy modifier to make it believable! :ogre_hurrhurr:

So i'm really sorry for having you wasting time calculating that.


On the other hand, i tried using the crow crow glitch - and it works sooo much better!!
Apparently i hadn't set the pitch and roll of the ball to 0 either, which is another obvious reason as to why it was moving so funky. :)

+ rep and many thanks for your help!!
 
Status
Not open for further replies.
Top