# Need Math formula for curve-like path

Level 31

#### WereElf

Level 13
wait... let me see if I understand you right:
You want it to move at increasing X-Y speed, and constant Z speed, but after some seconds - make Z constant? Or you want the Z to be decreasing until it reaches 0?

I guess you are going to use a sliding system for this? If so, then just increase the distance you move the unit to every time it runs.
While for the Z speed, to do it in way 1 I described earlier:
Keep Z speed constant, and after the movement has happened a set amount of times - set it to 0
To do it the 2-nd way:
Keep decreasing the Z speed until it reaches value below (or equal to) 0, then set it to 0

#### WereElf

Level 13
Well, formula for that shape is either x^2, either e^x (or any constant ^x), can't tell by looking only at this slice of it The way I described earlier would implement something that looks more like x^2.

#### Spellbound

Level 31
I'm using this right now for speed: `set FF_UnitTrail_Speed[id] = FF_UnitTrail_Speed[id] + (FF_UnitTrail_Distance[id] - Sin(FF_UnitTrail_Distance[id])) * .01`. However, when I apply the same for Height, it does moves in an arc instead of an exponential curve.

#### WereElf

Level 13
Wait.. why are you using Sin(distance), sin and cos should be used with angles, not lenghts (even if both are 'real's)

#### Spellbound

Level 31
That's because I'm abyssmal at math. I tried doing `set FF_UnitTrail_Speed[id] = FF_UnitTrail_Speed[id] + FF_UnitTrail_Distance[id] * .2` and while it works for speed, it wasn't working for the height incease. I wasn't getting a curve, it was a straight line.

#### BPower

Level 19
Bézier Curve would give you much freedom in creating curves.
The wiki article has nice gifs.

Interpolation functions

I planned already to write a more advanced library, but currently I don't have the time.

• #### WereElf

Level 13
Oh, Spellbound you need to increase the Z speed more than you increase the X-Y speed.
Try making the .2 to .4 (for the Z calculation).
If it doesn't work, try doing "Dist*Dist* .2" or * .4

#### Spellbound

Level 31
Bézier Curve would give you much freedom in creating curves.
The wiki article has nice gifs.

Interpolation functions

I planned already to write a more advanced library, but currently I don't have the time.

That... that like greek to me >.<
Would it be possible to just get a formula that increases the height exponentially and another that increases the distance covered exponentially?

Oh, Spellbound you need to increase the Z speed more than you increase the X-Y speed.
Try making the .2 to .4 (for the Z calculation).
If it doesn't work, try doing "Dist*Dist* .2" or * .4

I tried that already :\ It only creates a steeper line.
EDIT: I didn't see that last line. I'll try it and report back.

#### WereElf

Level 13
if it's only making a steeper line, than Dist*Dist should do the same (since its constant).
What WILL work is:
"Speed = Speed + (Speed + Dist)* 0.2" (this isn't constant anymore)
If you want it to be growing faster, then do:
"Speed = Speed + (Speed*Speed + Dist)* 0.2"
or
"Speed = Speed + (Speed+Dist)*(Speed+Dist)* 0.2"

#### Spellbound

Level 31
It's not working. The only thing that does is if I have two acceleration values for speed and height, but even then the dummy doesnt hit the mark if the base speed is too high :\

#### WereElf

Level 13
umm, yes, you indeed need 2 different formulas for the X-Y acceleration and for the Z aceleration.
X-Y should be kept "Speed = Speed+Dist*c", while Z should be "Speed = Speed + ( Speed*Speed*Speed+Dist)*v" you can add more *Speed for greater effect

#### Spellbound

Level 31
I don't think it can work like that, because the dummy will end at different height if the base speed changes. Here's the deal, I have the base speed, the distance it has the cover, and the time in which it should cover this distance. The thing I need is the acceleration. Now, the situation is extra tricky because the z has to increase more than the y/x do.

The formula you gave me looks like it can work, but like I said, different speeds different results. That's why i tried trig at first. Sin(MyValue) seems to return a smaller number than MyValue, which is why I did MyValue - Sin(MyValue) to get the difference and add than instead, but that's stupid because I need the difference to increase every interval, not shrink.

There has to be a trigonometric fomula to generate specific kinds of curves. Sadly, the links BPower provided are completely lost on me.

So yeah, I'm bad at math #### WereElf

Level 13
Well, if you make the X-Y speed constant and the Z speed = "Speed + Dist*c" - you will get the arc you want, but the X-Y speed will be constant xP

By the way, I said that this curve looks like either x^2, either e^x (or c^x). Since it's hard to make it work with x^2, let's make it c^x.
The function will look like:
JASS:
``````local real r = 100.00 // or some other value
loop
exitwhen r >= Dist
if r = 100.00 then // the initial value of r
set Z = BaseZ * BaseZ
else
set Z = Z * BaseZ
endif
set r = r + 100.00
endloop
set r = (r - Dist)/100.00 // the 100 here changes too if you change any of the other 100's
set Z = Z * (BaseZ * r) // this row and the one above it may cause some bugs, need to do tests to find out if they work properly.
call SetUnitFlyHeight(unit, Z, real)``````

By the way, do have in mind that this function can sky rocket you very fast.

EDIT: Actually set the unit's "Z" by that formula. It will be WAY too fast if you use it for the speed.

Last edited:

#### Nichilus

Level 22
I second using Bézier curves.
The advantage of using beziér curves are that
- there are a lot of simulators for bezier curves, so you can make yourself a nice curve
- just needs 4 points per curve (first and last are start and end point while the middle two curve the line)
- the calculation is same no matter what 4 points you give it
- the calculation is quite simple (the wiki page goes through everything, but you don't need all of that and while the formula may seem intimidating, it's quite easy once you grasp the concept)
- the "movement speed" along the curve is always the same

What you should be interested in are cubic beziér curves, as those are for [x, y, z] coordinates.

Edit:
It's something like in this pseudocode I just wrote:
JASS:
``````INPUT:
Points A, B, C, D
Time t
Difference u = 1 - t

--------------------------------------
NOTATION:
A = start point, D = end point

The X coordinate of point P will be written as P[x], etc.

Time can be only a number in interval [0.00 , 1.00]
A time equal to 0.00 results in start point, the time 1.00 results in end point.

----------------------------------
CALCULATION

X = A[x] * (u*u*u) + B[x] * 3 * t * (u*u) + C[x] * 3 * (t * t) * u + D[x] * (t*t*t);
Y = A[y] * (u*u*u) + B[y] * 3 * t * (u*u) + C[y] * 3 * (t * t) * u + D[y] * (t*t*t);
Z = A[z] * (u*u*u) + B[z] * 3 * t * (u*u) + C[z] * 3 * (t * t) * u + D[z] * (t*t*t);

Your point = [X, Y, Z]``````

EXAMPLE:
JASS:
``````A [1.0, 2.0, 2.0]
B [3.0, 3.0, 2.0]
C [6.0, 3.0, 2.0]
D [4.0, 1.0, 3.0]

t = 0.25
u = 1 - t = 1 - 0.25 = 0.75

X = 1.0 * (0.75*0.75*0.75) + 3.0 * 3 * 0.25 * (0.75*0.75) + 6.0 * 3 * (0.25 * 0.25) * 0.75 + 4.0 * (0.25*0.25*0.25) = 0.422 + 1.266 + 0.844 + 0.063 = 2.595

Y = 2.0 * (0.75*0.75*0.75) + 3.0 * 3 * 0.25 * (0.75*0.75) + 3.0 * 3 * (0.25 * 0.25) * 0.75 + 1.0 * (0.25*0.25*0.25) = 0.844 + 1.266 + 0.422 + 0.016 = 2.548

Z = 2.0 * (0.75*0.75*0.75) + 2.0 * 3 * 0.25 * (0.75*0.75) + 2.0 * 3 * (0.25 * 0.25) * 0.75 + 3.0 * (0.25*0.25*0.25) = 0.844 + 0.844 + 0.281 + 0.047 = 2.016

Your point = [2.595, 2.548, 2.016]``````

This looks to be the case (although the numbers are slightly higher as I had to round the numbers and I ended up rounding numbers up most of the time) and checking it in the link I posted below gives good results as well

You can also try and check this: https://www.desmos.com/calculator/cahqdxeshd
It's only 2D, but it should give you an example of what bezier curve is.

Last edited:

#### Spellbound

Level 31
I would have written the formula myself if those formulae on the wiki made any sense to me. What's B supposed to be? t? How do I translate any of this into warcraft 3? Also I need the 'movement speed' to accelerate, so it can't be constant. I'm hoping that's possible?

#### WereElf

Level 13
Okay, I thought about it, and I think it's not so bad to use the `Pow()` func.
I mean... manually looping is quite heavy, so Pow is better.
JASS:
``````local real i = Dist/(MaxDist/3) // you can change the '3' to change the maximum height
set Z = Pow( BaseZ, i ) // you can change the BaseZ to change the max height too.``````

You can accelerate the X-Y speed this way.

#### Nichilus

Level 22
I would have written the formula myself if those formulae on the wiki made any sense to me. What's B supposed to be? t? How do I translate any of this into warcraft 3? Also I need the 'movement speed' to accelerate, so it can't be constant. I'm hoping that's possible?

I edited my last post with an example (also in the hidden tab). What I meant as constant is that if you increase the value of t by constant amount, the result is a constant distance travelled along the curve.
You can of course dynamically change the speed and it will increase/decrease the speed along the curve accordingly.

#### Spellbound

Level 31
@WereElf
I'm gonna try to figure out the Bezier thing. If I still don't get it I'll give your suggestion a go.

@Nichilus
I'm still rather confused how I make use of `Your point = [2.595, 2.548, 2.016]`. From what I gathered on Wikipedia, a Cubic Bezier curve needs 4 points - 2 for start/end and 2 for direction. How would I implement that in my existing code? I already have this to move my dummy unit:
JASS:
``````set FF_UnitTrail_Progress[id] = FF_UnitTrail_Progress[id] + FF_UnitTrail_Speed[id]

set x = FF_UnitTrail_X[id] + Cos(FF_UnitTrail_Angle[id]) * FF_UnitTrail_Progress[id]
set y = FF_UnitTrail_Y[id] + Sin(FF_UnitTrail_Angle[id]) * FF_UnitTrail_Progress[id]``````
Once the unit reaches a certain point, it needs to accelerate along the curve. How would I make a Bezier curve work with this? What I'm trying to do right now is just increase the speed by something like Speed * 1.05 so that ever tick it accelerates. It would work is speed was a fixed amount for all dummies.

PS: is `A [1.0, 2.0, 2.0]` acceptable syntax is WE? Also Difference u = 1 - t. Difference?

#### Nichilus

Level 22
I'm still rather confused how I make use of `Your point = [2.595, 2.548, 2.016]`. From what I gathered on Wikipedia, a Cubic Bezier curve needs 4 points - 2 for start/end and 2 for direction. How would I implement that in my existing code?
The cubic bézier takes 4 points as you wrote, however for time t it produces one point. That resulting point is basically an answer to the question: "Where would I be (in terms of location/coordinates) if I travelled for time t along the curve?"
I wrote about it two posts back and I write it now again. You move along the curve by changing the t component and the value of t can only range between 0.00 (= the calculation results in start point) to 1.00 (the calculation results in end point).
In the example I set t as 0.25. That means the point at 25% of the curve's length from the start.

Once the unit reaches a certain point, it needs to accelerate along the curve. How would I make a Bezier curve work with this? What I'm trying to do right now is just increase the speed by something like Speed * 1.05 so that ever tick it accelerates. It would work is speed was a fixed amount for all dummies.
Two options really:
a) After t = some specific value, you increase the speed. Since t can be only a number between 0.00 and 1.00 then you can approximate where you want to increase/decrease the speed. E.g. I want to increase the speed in the later half of the curve, so I increase the speed once I pass the t > 0.5 condition.

b) Use two (or more) bézier curves, for each curve you use different speed.

PS: is `A [1.0, 2.0, 2.0]` acceptable syntax is WE? Also Difference u = 1 - t. Difference?
Probably not acceptable syntax. I did write it's pseudo-code, not jass. I only put it in jass tags for better readability.

The original calculation uses everywhere (1-t). And then even raises it to power 2 or 3. So instead of writing:
`X = A[x] * ((1-t)*(1-t)*(1-t)) + B[x]...`
I simply pre-calculated the result of (1-t) and saved it into u. The result of that calculation is the difference of those two numbers.
(And really, is that what you are worried about?)

#### Anitarf

Level 7
There's no need to complicate this with bezier curves when simple polynomials can do the trick. As already noted, the x^2 function already matches the shape you are trying to get, and if not there are other simple functions you can try like x^3 or e^x. The problem you were having was that you calculated both distance traveled (d) and z increase as a function of time, so d=t^2 and z=t^2. Since they were both increasing at the same rate, you got a linear path, even though the rate was increasing. The solution is simple: just make z depend on distance rather than time, therefore: d=t^2 and z=d^2. Obviously, you also need to throw in some constants to ensure that end distance and end z are where you need them to be.

You would calculate these constants based on your initial constraints. If final distance D and duration are already determined when you start the spell, then that's simple, D=c*dur^2, therefore c=D/dur^2. Once the constant is calculated, you use it as you update the distance: d=c*t^2. Once t reaches dur, d will also reach D. Same for z.

Clarification: in the above examples, I am always calculating the position of the projectile based on its starting position, rather than its position on the previous update, so I'm not doing x=x+formula(update period), but x=startx+formula(elapsed time). Since we mostly use the former approach when doing periodic updates, this might take some getting used to, but I think it's the easier approach when trying to fit motion to function graphs.

#### WereElf

Level 13
I made a sample map with my suggestion.
JASS:
``````function Jump takes nothing returns nothing
local unit u = gg_unit_hpea_0000
local timer t = GetExpiredTimer()
local real MaxDist = 500.00
local real x = GetUnitX(u)
local real y = GetUnitY(u)
local real Dist = SquareRoot((x-udg_X)*(x-udg_X)+(y-udg_Y)*(y-udg_Y))
local real i
local real Z
local real nx = x + udg_Speed*CosBJ(udg_A)
local real ny = y + udg_Speed*SinBJ(udg_A)
if Dist < MaxDist then
set Dist = SquareRoot((nx-udg_X)*(nx-udg_X)+(ny-udg_Y)*(ny-udg_Y))
set i = (Dist+200)/MaxDist
set Z = Pow(100, i)
call SetUnitX(u, nx)
call SetUnitY(u, ny)
call SetUnitFlyHeight( u , Z , 1000000000.00 )
set udg_Speed = udg_Speed + 2
else
call DestroyTimer(t)
endif
set u = null
set t = null
endfunction

function Trig_asd_Actions takes nothing returns nothing
local unit u = gg_unit_hpea_0000
local timer t = CreateTimer()
call UnitAddAbility( u , 'Arav' )
call UnitRemoveAbility( u , 'Arav')
set udg_X = GetUnitX(u)
set udg_Y = GetUnitY(u)
set udg_A = GetUnitFacing(u)
set udg_YT = udg_Y + 500*SinBJ(udg_A)
set udg_XT = udg_X + 500*CosBJ(udg_A)
set udg_Speed = 10.00
call TimerStart( t, 0.05, true, function Jump)
set u = null
endfunction

//===========================================================================
function InitTrig_asd takes nothing returns nothing
set gg_trg_asd = CreateTrigger(  )
call TriggerRegisterPlayerEventEndCinematic( gg_trg_asd, Player(0) )
call TriggerAddAction( gg_trg_asd, function Trig_asd_Actions )
endfunction``````
Just press Escape and look at the lonely villager.
You can set the Speed to Speed + 1 for smoother movement, and i to (Dist+190)/MaxDist for not so high aptitude.
Kind a didn't make a way for him to go back to the ground, but pressing escape again restarts his height.
And it turned out I didn't need XT and YT after all #### Attachments

• jump test.w3m
17.2 KB · Views: 53
• Spellbound

#### Spellbound

Level 31
There's no need to complicate this with bezier curves when simple polynomials can do the trick. As already noted, the x^2 function already matches the shape you are trying to get, and if not there are other simple functions you can try like x^3 or e^x. The problem you were having was that you calculated both distance traveled (d) and z increase as a function of time, so d=t^2 and z=t^2. Since they were both increasing at the same rate, you got a linear path, even though the rate was increasing. The solution is simple: just make z depend on distance rather than time, therefore: d=t^2 and z=d^2. Obviously, you also need to throw in some constants to ensure that end distance and end z are where you need them to be.

You would calculate these constants based on your initial constraints. If final distance D and duration are already determined when you start the spell, then that's simple, D=c*dur^2, therefore c=D/dur^2. Once the constant is calculated, you use it as you update the distance: d=c*t^2. Once t reaches dur, d will also reach D. Same for z.

Clarification: in the above examples, I am always calculating the position of the projectile based on its starting position, rather than its position on the previous update, so I'm not doing x=x+formula(update period), but x=startx+formula(elapsed time). Since we mostly use the former approach when doing periodic updates, this might take some getting used to, but I think it's the easier approach when trying to fit motion to function graphs.

Right, problem. The time it takes for the unit to go from start to end is 2 seconds. I'm using a 0.03125 seconds interval as my periodic, so 2 seconds would be 64 intervals.

That's how I'm calculating my constant:
`set FF_UnitTrail_Acceleration[id] = FF_UnitTrail_Distance[id] / (64 * 64)`
I do that only once at the start of the curve.

This is how I calculate by distance:
`set FF_UnitTrail_Progress[id] = FF_UnitTrail_Acceleration[id] * (64 *64)`
Progress is the ground covered. I compare that with FF_UnitTrail_Distance[id] to see is the full distance has been covered. Acceleration is my constant, and 64 intervals represent 2 seconds.

This is how I calculate my height increase:
`set FF_UnitTrail_Height[id] = FF_UnitTrail_Acceleration[id] * (FF_UnitTrail_Progress[id] * FF_UnitTrail_Progress[id])`

This is how I'm moving my dummy:
`set x = FF_UnitTrail_X[id] + Cos(FF_UnitTrail_Angle[id]) * FF_UnitTrail_Progress[id]`
`set y = FF_UnitTrail_Y[id] + Sin(FF_UnitTrail_Angle[id]) * FF_UnitTrail_Progress[id]`
`call SetUnitFlyHeight(u, FF_UnitTrail_Height[id], 0.00)`

And, it's not working lol. I tried to follow your instructions, but, yeah. Help @_@

I made a sample map with my suggestion.
JASS:
``````function Jump takes nothing returns nothing
local unit u = gg_unit_hpea_0000
local timer t = GetExpiredTimer()
local real MaxDist = 500.00
local real x = GetUnitX(u)
local real y = GetUnitY(u)
local real Dist = SquareRoot((x-udg_X)*(x-udg_X)+(y-udg_Y)*(y-udg_Y))
local real i
local real Z
local real nx = x + udg_Speed*CosBJ(udg_A)
local real ny = y + udg_Speed*SinBJ(udg_A)
if Dist < MaxDist then
set Dist = SquareRoot((nx-udg_X)*(nx-udg_X)+(ny-udg_Y)*(ny-udg_Y))
set i = (Dist+200)/MaxDist
set Z = Pow(100, i)
call SetUnitX(u, nx)
call SetUnitY(u, ny)
call SetUnitFlyHeight( u , Z , 1000000000.00 )
set udg_Speed = udg_Speed + 2
else
call DestroyTimer(t)
endif
set u = null
set t = null
endfunction

function Trig_asd_Actions takes nothing returns nothing
local unit u = gg_unit_hpea_0000
local timer t = CreateTimer()
call UnitAddAbility( u , 'Arav' )
call UnitRemoveAbility( u , 'Arav')
set udg_X = GetUnitX(u)
set udg_Y = GetUnitY(u)
set udg_A = GetUnitFacing(u)
set udg_YT = udg_Y + 500*SinBJ(udg_A)
set udg_XT = udg_X + 500*CosBJ(udg_A)
set udg_Speed = 10.00
call TimerStart( t, 0.05, true, function Jump)
set u = null
endfunction

//===========================================================================
function InitTrig_asd takes nothing returns nothing
set gg_trg_asd = CreateTrigger(  )
call TriggerRegisterPlayerEventEndCinematic( gg_trg_asd, Player(0) )
call TriggerAddAction( gg_trg_asd, function Trig_asd_Actions )
endfunction``````
Just press Escape and look at the lonely villager.
You can set the Speed to Speed + 1 for smoother movement, and i to (Dist+190)/MaxDist for not so high aptitude.
Kind a didn't make a way for him to go back to the ground, but pressing escape again restarts his height.
And it turned out I didn't need XT and YT after all Nice! I do believe there should be a simpler way of doing this though - what's more, I'd rather avoid having a timer and actually understand what I'm making lol. +rep #### Anitarf

Level 7
This is how I calculate by distance:
`set FF_UnitTrail_Progress[id] = FF_UnitTrail_Acceleration[id] * (64 *64)`
That will just give you the final value again. What you need to do is use the number of updates that have occurred so far, rather than the full number of updates (64).

#### Spellbound

Level 31
Okay, I did this:
`set FF_UnitTrail_Progress[id] = FF_UnitTrail_Acceleration[id] * (FF_UnitTrail_Time[id] * FF_UnitTrail_Time[id])`

then I update Time
`set FF_UnitTrail_Time[id] = FF_UnitTrail_Time[id] + 1`

This time around, the dummy doesn't move at all :\

#### Anitarf

Level 7
Hmm, can you post the entire update function? I can't find the error in just the few lines you've posted.

#### Spellbound

Level 31
Okay, wow, sorry, I'm an idiot. I was gonna looking at my code and then noticed that I didn't add the dummy back into the unit group for it to loop back.

It seemed to work, but the curve is a bit too steep. Any way I could make the angle of ascent a bit shallower?

There is one other thing though - the dummy unit was already moving at a certain speed, and was at a certain height before it begins to accelerate and ascend. Is there a way to make it accelerate/ascend from those values?

In case it can help, here's the code:
JASS:
``````function Trig_UnitTrail takes nothing returns boolean

local unit u = null
local unit TrainedUnit = null
local unit Building = null

local integer id = 0

local real x = 0.00
local real y = 0.00
local real z = 0.00

if FirstOfGroup(FF_UnitTrail_Group) != null then

loop
set u = FirstOfGroup(FF_UnitTrail_Group)
exitwhen u == null

set id = GetUnitUserData(u)
call GroupRemoveUnit(FF_UnitTrail_Group, u)

if UnitAlive(u) then

if FF_UnitTrail_Progress[id] < FF_UnitTrail_Distance[id] then

if not FF_UnitTrail_IsTrainingFinished[id] then

//Steadily move the dummy towards the destination
set FF_UnitTrail_Progress[id] = FF_UnitTrail_Progress[id] + FF_UnitTrail_Speed[id]

else

//dummy has reached destination. Begin acceleration and ascent.
set FF_UnitTrail_Progress[id] = FF_UnitTrail_Acceleration[id] * (FF_UnitTrail_Time[id] * FF_UnitTrail_Time[id])
set FF_UnitTrail_Height[id] = FF_UnitTrail_Acceleration[id] * (FF_UnitTrail_Progress[id] * FF_UnitTrail_Progress[id])
set FF_UnitTrail_Time[id] = FF_UnitTrail_Time[id] + 1

call SetUnitFlyHeight(u, FF_UnitTrail_Height[id], 0.00)

endif

set x = FF_UnitTrail_X[id] + Cos(FF_UnitTrail_Angle[id]) * FF_UnitTrail_Progress[id]
set y = FF_UnitTrail_Y[id] + Sin(FF_UnitTrail_Angle[id]) * FF_UnitTrail_Progress[id]

call SetUnitX(u, x)
call SetUnitY(u, y)

call SetUnitFacing(u, (FF_UnitTrail_Angle[id] * bj_RADTODEG) + 180.00)

if not UnitAlive(FF_UnitTrail_Source[id]) then
call UnitApplyTimedLife(u, 'BTLF', 0.01)
endif

else

//dummy has reached destination. Set training to true and begin ascent and acceleration.
if not FF_UnitTrail_IsTrainingFinished[id] then
//call SetUnitColor(u, GetPlayerColor(GetOwningPlayer(u)))
set FF_UnitTrail_IsTrainingFinished[id] = true
set FF_UnitTrail_Progress[id] = 0.00
set FF_UnitTrail_Distance[id] = FF_UnitTrail_FinalDistance[id] - GetRandomReal(10, 100)
//set FF_UnitTrail_Speed[id] = FF_UnitTrail_Speed[id] + FF_TRAIN_SPEED_BUMP
//set FF_UnitTrail_Height[id] = FF_UnitTrail_Height[id] + FF_UnitTrail_Speed[id]
set FF_UnitTrail_Acceleration[id] = FF_UnitTrail_Distance[id] / (64 * 64)
set FF_UnitTrail_Time[id] = 1
set FF_UnitTrail_X[id] = GetUnitX(u)
set FF_UnitTrail_Y[id] = GetUnitY(u)

//dummy has died (either was killed or training canceled)
else
call DestroyEffect(bj_lastCreatedEffect)
set TrainedUnit = ReplaceUnitBJ(u, FF_UnitTrail_UnitType[id], bj_UNIT_STATE_METHOD_RELATIVE)
set Building = FF_UnitTrail_Source[id]
//call SetUnitFacing(TrainedUnit, (FF_UnitTrail_Angle[id] * bj_RADTODEG) + 180.00)
call IssuePointOrder(TrainedUnit, "move", GetLocationX(GetUnitRallyPoint(Building)), GetLocationY(GetUnitRallyPoint(Building)))

set FF_UnitTrail_IsTrainingFinished[id] = false
set FF_UnitTrail_UnitType[id] = 0
set FF_UnitTrail_Progress[id] = 0.00
set FF_UnitTrail_Distance[id] = 0.00
set FF_UnitTrail_FinalDistance[id] = 0.00
set FF_UnitTrail_Acceleration[id] = 0.00
set FF_UnitTrail_Height[id] = 0.00
set FF_UnitTrail_Source[id] = null
set FF_UnitTrail_Speed[id] = 0.00
set FF_UnitTrail_Angle[id] = 0.00
set FF_UnitTrail_X[id] = 0.00
set FF_UnitTrail_Y[id] = 0.00

set TrainedUnit = null
set Building = null
endif

endif

else //if the dummy is dead, refund part of the cost

endif

endloop

loop
set u = FirstOfGroup(FF_UnitTrail_GroupTemp)
exitwhen u == null
call GroupRemoveUnit(FF_UnitTrail_GroupTemp, u)
endloop
else
call DisableTrigger(gg_trg_Unit_Trail)
endif

return false
endfunction

//===========================================================================
function InitTrig_Unit_Trail takes nothing returns nothing
set gg_trg_Unit_Trail = CreateTrigger()
call DisableTrigger(gg_trg_Unit_Trail)
call TriggerRegisterTimerEvent( gg_trg_Unit_Trail, 0.03125, true )
call TriggerAddCondition( gg_trg_Unit_Trail, function Trig_UnitTrail )
endfunction``````

#### Anitarf

Level 7
For the unit going too high up, the problem is you're using the same constant value in the z equation as in the distance equation. You need to calculate a different constant for the z equation: `set FF_UnitTrail_AccelerationZ[id] = final_height / (FF_UnitTrail_Distance[id] * FF_UnitTrail_Distance[id])` Note that I used `FF_UnitTrail_Distance[id]` instead of `64`, since the z formula is based on distance rather than on time.

If you want the missile to have a starting speed, the formula needs some tweaking. Right now, it was based on a simple y=a*x^2 form, the first derivative of which (thus, its "speed") is y'=a*2*x. So, at the start, when x=0, speed is also zero. If we add another term to the equation, we can fix this: y=a*x^2+b*x, thus y'=a*2*x+b, thus when x=0 speed will be b. If we want a fixed starting speed, then b is already known, we just need to take it into account when calculating a, in your case: `FF_UnitTrail_Acceleration[id] = (FF_UnitTrail_Distance[id] - startspeed*64)/(64*64)`

Note that since you measure time in updates rather than seconds, you need to keep this in mind when choosing the value of startspeed - if you want your starting speed to be 500/second, startspeed needs to be 500/32. Note also that if you choose a starting speed that is too high, the equations might yield a negative constant - this means that your projectile will actually need to deccelerate in order to reach the final distance in the given time interval and not sooner.

#### Spellbound

Level 31
Well now the unit moves backwards lol. As for the height I tried to follow the logic you explained and calculated it's constant in the same way, using Distance instead of time, but the unit stays grounded. I supposed that's because it's returning a negative value and flying height cannot go below zero with triggers?

EDIT: yep, called a DebugMsg and both Acceleration and AccelerationZ have negative value. Gonna try multiplying em by -1 and see what happens.

EDIT2: well the dummy rises alright but it's the same deal as before and it all starts from a zero (or near zero maybe?) value.

JASS:
``````function Trig_UnitTrail takes nothing returns boolean

local unit u = null
local unit TrainedUnit = null
local unit Building = null

local integer id = 0

local real x = 0.00
local real y = 0.00
local real z = 0.00

if FirstOfGroup(FF_UnitTrail_Group) != null then

loop
set u = FirstOfGroup(FF_UnitTrail_Group)
exitwhen u == null

set id = GetUnitUserData(u)
call GroupRemoveUnit(FF_UnitTrail_Group, u)

if UnitAlive(u) then

if FF_UnitTrail_Progress[id] < FF_UnitTrail_Distance[id] then

if not FF_UnitTrail_IsTrainingFinished[id] then

//Steadily move the dummy towards the destination
set FF_UnitTrail_Progress[id] = FF_UnitTrail_Progress[id] + FF_UnitTrail_Speed[id]

else

//dummy has reached destination. Begin acceleration and ascent.
set FF_UnitTrail_Progress[id] = FF_UnitTrail_Acceleration[id] * (FF_UnitTrail_Time[id] * FF_UnitTrail_Time[id])
set FF_UnitTrail_Height[id] = FF_UnitTrail_AccelerationZ[id] * (FF_UnitTrail_Progress[id] * FF_UnitTrail_Progress[id])
set FF_UnitTrail_Time[id] = FF_UnitTrail_Time[id] + 1

call SetUnitFlyHeight(u, FF_UnitTrail_Height[id], 0.00)

endif

set x = FF_UnitTrail_X[id] + Cos(FF_UnitTrail_Angle[id]) * FF_UnitTrail_Progress[id]
set y = FF_UnitTrail_Y[id] + Sin(FF_UnitTrail_Angle[id]) * FF_UnitTrail_Progress[id]

call SetUnitX(u, x)
call SetUnitY(u, y)

call SetUnitFacing(u, (FF_UnitTrail_Angle[id] * bj_RADTODEG) + 180.00)

if not UnitAlive(FF_UnitTrail_Source[id]) then
call UnitApplyTimedLife(u, 'BTLF', 0.01)
endif

else

//dummy has reached destination. Set training to true and begin ascent and acceleration.
if not FF_UnitTrail_IsTrainingFinished[id] then
//call SetUnitColor(u, GetPlayerColor(GetOwningPlayer(u)))
set FF_UnitTrail_IsTrainingFinished[id] = true
set FF_UnitTrail_Progress[id] = 0.00
set FF_UnitTrail_Distance[id] = FF_UnitTrail_FinalDistance[id] - GetRandomReal(10, 100)
//set FF_UnitTrail_Speed[id] = FF_UnitTrail_Speed[id] + FF_TRAIN_SPEED_BUMP
//set FF_UnitTrail_Height[id] = FF_UnitTrail_Height[id] + FF_UnitTrail_Speed[id]
//set FF_UnitTrail_Acceleration[id] = FF_UnitTrail_Distance[id] / (FF_CURVE_TIME * FF_CURVE_TIME)
set FF_UnitTrail_Acceleration[id] = (FF_UnitTrail_Distance[id] - FF_UnitTrail_Speed[id] * FF_CURVE_TIME) / (FF_CURVE_TIME * FF_CURVE_TIME)
set FF_UnitTrail_AccelerationZ[id] = (GetRandomReal(600, 1000) - GetUnitFlyHeight(u) * FF_UnitTrail_Distance[id]) / (FF_UnitTrail_Distance[id] * FF_UnitTrail_Distance[id])
set FF_UnitTrail_Time[id] = 1
set FF_UnitTrail_X[id] = GetUnitX(u)
set FF_UnitTrail_Y[id] = GetUnitY(u)

//dummy has died (either was killed or training canceled)
else
call DestroyEffect(bj_lastCreatedEffect)
set TrainedUnit = ReplaceUnitBJ(u, FF_UnitTrail_UnitType[id], bj_UNIT_STATE_METHOD_RELATIVE)
set Building = FF_UnitTrail_Source[id]
//call SetUnitFacing(TrainedUnit, (FF_UnitTrail_Angle[id] * bj_RADTODEG) + 180.00)
call IssuePointOrder(TrainedUnit, "move", GetLocationX(GetUnitRallyPoint(Building)), GetLocationY(GetUnitRallyPoint(Building)))

set FF_UnitTrail_IsTrainingFinished[id] = false
set FF_UnitTrail_UnitType[id] = 0
set FF_UnitTrail_Progress[id] = 0.00
set FF_UnitTrail_Distance[id] = 0.00
set FF_UnitTrail_FinalDistance[id] = 0.00
set FF_UnitTrail_Acceleration[id] = 0.00
set FF_UnitTrail_AccelerationZ[id] = 0.00
set FF_UnitTrail_Height[id] = 0.00
set FF_UnitTrail_Source[id] = null
set FF_UnitTrail_Speed[id] = 0.00
set FF_UnitTrail_Angle[id] = 0.00
set FF_UnitTrail_X[id] = 0.00
set FF_UnitTrail_Y[id] = 0.00

set TrainedUnit = null
set Building = null
endif

endif

else //if the dummy is dead, refund part of the cost

endif

endloop

loop
set u = FirstOfGroup(FF_UnitTrail_GroupTemp)
exitwhen u == null
call GroupRemoveUnit(FF_UnitTrail_GroupTemp, u)
endloop
else
call DisableTrigger(gg_trg_Unit_Trail)
endif

return false
endfunction

//===========================================================================
function InitTrig_Unit_Trail takes nothing returns nothing
set gg_trg_Unit_Trail = CreateTrigger()
call DisableTrigger(gg_trg_Unit_Trail)
call TriggerRegisterTimerEvent( gg_trg_Unit_Trail, 0.03125, true )
call TriggerAddCondition( gg_trg_Unit_Trail, function Trig_UnitTrail )
endfunction``````

#### Anitarf

Level 7
Just using the new formula when calculating the acceleration is not enough, you also need to use the new formula when calculating progress: `set FF_UnitTrail_Progress[id] = FF_UnitTrail_Acceleration[id] * (FF_UnitTrail_Time[id] * FF_UnitTrail_Time[id]) + FF_UnitTrail_Speed[id] * FF_UnitTrail_Time[id]`

As for the height I tried to follow the logic you explained and calculated it's constant in the same way
You're not doing it the same way, though. When calculating acceleration for distance, you use the end value and the starting speed. For z, you use the end value (I assume that's what the random real is) and the start value, not speed. For that, you need a different equation of the form y=a*x^2+c instead of y=a*x^2+b*x. The value of c is your starting value and if you input final height for y and final distance for x you can calculate the constant a needed to achieve them. Then, like with distance, you need to use a matching equation in the update function: `set FF_UnitTrail_Height[id] = FF_UnitTrail_AccelerationZ[id] * (FF_UnitTrail_Progress[id] * FF_UnitTrail_Progress[id]) + FFUnitTrail_StartHeight[id]`

#### Spellbound

Level 31
the whole y=a*x^2+b*x is really confusing me.

To get the constant for the Z increase, I need to do:
AccelerationZ = EndHeight * EndDistance^2 + Acceleration ?

EDIT: Wait no, is y=a*x^2+c is the last equation you gave me?

Ugh, my troglodyte brain dies when math is involved.

c, aka Acceleration, is the starting value? I thought the current flying height was the starting Z value?
y = EndHeight
x = FinalDistance
a = AccelerationZ

therefore... a = y / (x^2 + c) ???

#### Anitarf

Level 7
the whole y=a*x^2+b*x is really confusing me.
I'm using standard notation for quadratic functions. I'm sorry, I just assumed you'd be familiar with this stuff. Basically, a quadratic function has the kind of shape that you want - rising more slowly at the start, then faster towards the end. There are other functions with this property, but the quadratic function is the simplest so that's the one I chose to use.

Now, the graph of a quadratic function always has the shape of a parabola, but this shape can be moved and stretched by changing the constants of the function. So, what I did was I calculated the needed constants to ensure that the shape matches your requirements.

To get the constant for the Z increase, I need to do:
AccelerationZ = EndHeight * EndDistance^2 + Acceleration ?
No, it's AccelerationZ = (EndHeight - StartHeight) / EndDistance^2

• Spellbound

#### Spellbound

Level 31
I just assumed you'd be familiar with this stuff.
Lol, nope. I barely remember any secondary school level math, and you can forget calculus. I've systematically failed those classes. Never could find an interest in it :\

No, it's AccelerationZ = (EndHeight - StartHeight) / EndDistance^2

Funnily enough, this a = y / (x^2 + c) worked lol.