• 🏆 Texturing Contest #33 is OPEN! Contestants must re-texture a SD unit model found in-game (Warcraft 3 Classic), recreating the unit into a peaceful NPC version. 🔗Click here to enter!
  • 🏆 Hive's 6th HD Modeling Contest: Mechanical is now open! Design and model a mechanical creature, mechanized animal, a futuristic robotic being, or anything else your imagination can tinker with! 📅 Submissions close on June 30, 2024. Don't miss this opportunity to let your creativity shine! Enter now and show us your mechanical masterpiece! 🔗 Click here to enter!

[vJASS] Cannonball script

Not open for further replies.

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:

library Cannonball requires ListModule, GroupUtils

    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

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
    return false

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)


    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

  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

        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()
            call .bounce()
        if .count < 1 then
          call PauseTimer(.ticker)

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

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

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


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!
Level 19
Feb 4, 2009
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

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)

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

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

and as always there is a wikipedia article about it
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:

(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!!
Not open for further replies.