• 🏆 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!
  • It's time for the first HD Modeling Contest of 2024. Join the theme discussion for Hive's HD Modeling Contest #6! Click here to post your idea!

[Snippet] Fly

Level 7
Joined
Apr 5, 2011
Messages
245
Libraries:
- Curves
- Fly

FAQ

Q: WTF?
A: Every object's movement in 3-dimensional space can be represented as a composition of XY (surface) and Z (height) movement.
Most custom maps XY movement is nothing more than simple linear trajectory with constant movespeed. Jump is an obvious example, where specific Z coordinates form parabola. Same maps don't have flying units realistically interact with each other and require those parabolas just as visual.
Meanwhile our sense of continuity is about 0.025 sec, we are less sensitive to rate of trajectory, so smooth trajectory and right amount of points, connected linearly, look almost same for us. Thus, speed can be updated every 0.1 sec, or 0.2 sec, or even bigger, which depends on situation (the faster is movement the less must be frequency).
Relatively small amount of points, required to satisfy our senses, makes wise to use constant set of points for geometrically similar curves.
Curves library is meant to put normalized curves, which can be easily scaled then.
Example:
Normalized = {0, 0.75, 1, 0.75, 0} (parabola)
Scaled (x400) = {0, 300, 400, 300, 0} (parabola)

Movement algorithm is made so it uses rate of curve, to minimize input data, some inner calculations and opens door for composite Z movement from different sources (by summing up all rates).
Example:
Curve = {0, 0.75, 1, 0.75, 0}
Rate of curve = {0.75, 0.25, -0.25, -0.75}

Q: How can I do my curve?
A: Very easy:
//-=-=-=- Settings -=-=-=-
//! textmacro CURVES
constant integer CURVE_TRAJECTORY_1 = 0 //Array address of curve data
constant integer CURVE_TRAJECTORY_2 = 5 //Array address of curve data
...
//! endtextmacro

//! textmacro CURVES_DATA
//CURVE_TRAJECTORY_1
set DATA[0] = 4 //Amount of points
set DATA[1] = .75
set DATA[2] = .25
set DATA[3] = -.25
set DATA[4] = -.75
//CURVE_TRAJECTORY_2
set DATA[5] = 6 //Amount of points
...
//! endtextmacro
//-=-=-=--=-=-=-=-=-=-=-=-


-=- Library Curves -=-

JASS:
//-=-=-=-=--=-=-=-=-=-=-=-
//-=-=-=-] Curves [-=-=-=-
//-=-=-=-=-=-=-=--=-=-=-=-

library Curves
//-=-=-=- Settings -=-=-=-
//! textmacro CURVES
    constant integer CURVE_PARABOLA = 0
    constant integer CURVE_PARABOLA5_RATE = 6
    constant integer CURVE_PARABOLA7_RATE = 11
//! endtextmacro

//! textmacro CURVES_DATA
    //CURVE_PARABOLA5
    set DATA[0] = 5
    set DATA[1] = 0
    set DATA[2] = .75
    set DATA[3] = 1
    set DATA[4] = .75
    set DATA[5] = 0
    //CURVE_PARABOLA5_RATE
    set DATA[6] = 4
    set DATA[7] = .75
    set DATA[8] = .25
    set DATA[9] = -.25
    set DATA[10] = -.75
    //CURVE_PARABOLA7_RATE
    set DATA[11] = 6
    set DATA[12] = .6
    set DATA[13] = .3
    set DATA[14] = .1
    set DATA[15] = -.1
    set DATA[16] = -.3
    set DATA[17] = -.6
//! endtextmacro
//-=-=-=--=-=-=-=-=-=-=-=-

globals
    //! runtextmacro CURVES()
endglobals
    struct CURVES extends array
        readonly static real array DATA
        private static method onInit takes nothing returns nothing
        //! runtextmacro CURVES_DATA()
        endmethod
    endstruct
endlibrary

-=- Library Fly -=-

JASS:
//-=-=-=-=-=-=-=-=-=-=-
//-=-=-=-] Fly [-=-=-=-
//-=-=-=-=-=-=-=-=-=-=-

library Fly requires Curves, Support
globals
    private integer I = 0
    private unit array Unit
    private timer array Timer
    private integer array Curve
    private real array Scale
    private real array Speed
    private integer array Node
    private real array Height
endglobals

    private function Refresh takes nothing returns nothing
        set Support.workTimer = GetExpiredTimer()
        set Support.workInteger = I
        loop
            if Support.workTimer == Timer[Support.workInteger] then
                set Node[Support.workInteger] = Node[Support.workInteger] + 1
                set Support.workReal = CURVES.DATA[Curve[Support.workInteger] + Node[Support.workInteger]]
                set Height[Support.workInteger] = Height[Support.workInteger] + Scale[Support.workInteger] * Support.workReal
                if Support.workReal < 0 then
                    set Support.workReal = -Speed[Support.workInteger] * Support.workReal
                else
                    set Support.workReal =  Speed[Support.workInteger] * Support.workReal
                endif
                call SetUnitFlyHeight(Unit[Support.workInteger], GetUnitDefaultFlyHeight(Unit[Support.workInteger]) + Height[Support.workInteger], Support.workReal)
                if Node[Support.workInteger] == CURVES.DATA[Curve[Support.workInteger]] then
                    if Support.workInteger == I then
                        set Unit[Support.workInteger] = null
                    else
                        set Unit[Support.workInteger] = Unit[I]
                        set Timer[Support.workInteger] = Timer[I]
                        set Curve[Support.workInteger] = Curve[I]
                        set Scale[Support.workInteger] = Scale[I]
                        set Speed[Support.workInteger] = Speed[I]
                        set Node[Support.workInteger] = Node[I]
                        set Height[Support.workInteger] = Height[I]
                        set Unit[I] = null
                        set Timer[I] = Support.workTimer
                    endif
                    call PauseTimer(Support.workTimer)
                    set I = I - 1
                endif
                return
            endif
            set Support.workInteger = Support.workInteger - 1
        endloop
    endfunction    

    function UnitFly takes unit u, integer curve, real height, real time returns nothing
        set I = I + 1
        set Unit[I] = u
        if Timer[I] == null then
            set Timer[I] = CreateTimer()
        endif
        call TimerStart(Timer[I], time / CURVES.DATA[curve], true, function Refresh)
        set Curve[I] = curve
        set Scale[I] = height
        set Speed[I] = height * CURVES.DATA[curve] / time
        set Node[I] = 1
        set Height[I] = height * CURVES.DATA[curve + 1]
        call SetUnitFlyHeight(u, GetUnitDefaultFlyHeight(u) + Height[I], Speed[I] * CURVES.DATA[curve + 1])
    endfunction
endlibrary

Example of usage:
JASS:
call UnitFly(GetTriggerUnit(), CURVE_PARABOLA7_RATE, 300, 2)
It makes unit fly for 2 seconds with 300 height at maximum
XY movement is done separately

Added:
Btw, if you see some memory leak, point me please
 
Last edited:

Kazeon

Hosted Project: EC
Level 33
Joined
Oct 12, 2011
Messages
3,449
JASS:
        if Timer[I] == null then
            set Timer[I] = CreateTimer()
        endif
        call TimerStart(Timer[I], time / CURVES.DATA[curve], true, function Refresh)
derp.. derp.. >.>

first, I really don't know how to use this, this is something new, unique, but inefficient way to generate the trajectory, I believe you can do it by using one timer and using normal dynamic indexing. Also I see you like textmacro so much, there is no point in using it too often..

btw
JASS:
        private static method onInit takes nothing returns nothing
        //! runtextmacro CURVES_DATA()
        endmethod
you may use module to make it more elegant.

JASS:
//! textmacro CURVES
    constant integer CURVE_PARABOLA = 0
    constant integer CURVE_PARABOLA5_RATE = 6
    constant integer CURVE_PARABOLA7_RATE = 11
//! endtextmacro
Y U NO replace textmacro & endtextmacro to globals & endglobals?

JASS:
    set DATA[0] = 5
    set DATA[1] = 0
    set DATA[2] = .75
    set DATA[3] = 1
    set DATA[4] = .75
    set DATA[5] = 0
and I think everything must be calculated, not defined by user. Also that means we must declare new variable like constant integer CURVE_PARABOLA5_RATE = 6 to make a new trajectory? that's horrible.
and library Curves is actually doing nothing except declaring variables, however, imo this is unaccepatable.. but I like the idea where user may set the estimated time function Fly takes unit u, integer curve, real height, real time
 
Level 7
Joined
Apr 5, 2011
Messages
245
Also I see you like textmacro so much
I just that color, man :D

Well, those constants is nothing compared to triggers. :D
Declaring comfortable names with constants is usual thing
I am not sure if I can use module, because set data is put inside function
I disagree that user must do himself nothing to profit from library. This is still very user-friendly.

Added:
Some "documentation" appeard
 
It means that instead of using 1 timer per instance, you only use 1 timer that simply loops thru all instances

and the code looks too confusing with all those textmacros when you don't even need them... and as Maggy said, please document what those values on CURVES does...

Also instead of hardcoded curves, I would prefer that you simply take an "arc" value and dynamically create the actual parabola movement using a formula...
 
Level 7
Joined
Apr 5, 2011
Messages
245
It means that instead of using 1 timer per instance, you only use 1 timer that simply loops thru all instances
Every timer must be supplied with its own period, otherwise it would make no sense.
The thing is, each curve is set by constant number of points, therefore period is different for different fly time. If one jump is longer than other, it will take more time and use bigger period, that's why timers can't be merged. Also, each of them has own start point.
But I can merge close timers though.
Well, I will try something.
and the code looks too confusing with all those textmacros when you don't even need them... and as Maggy said, please document what those values on CURVES does...
See FAQ
Also instead of hardcoded curves, I would prefer that you simply take an "arc" value and dynamically create the actual parabola movement using a formula...
These curves aren't hardcoded. Calculating actual trajectory is relatively hardcoded.
Imagine if you have zigzag trajectory. You need to write specific algorithm, which works slower, while going through coords is always same.
 

Kazeon

Hosted Project: EC
Level 33
Joined
Oct 12, 2011
Messages
3,449
that's why timers can't be merged
they can't but you can use real to replace the "duration". but still you need to use one timer..
Well, those constants is nothing compared to triggers. :D
what trigger? you dont need any trigger, if so, then still it's better to have 1 trigger than 100 constants perhaps? this is about possibility..
using a formula...
that is the easiest way to generate a good parabola trajectory: using a formula which I dont remember clearly. and currently this library doesnt event support horizontal parabola movement which most missile systems around here has supported that one..

I am not sure if I can use module, because set data is put inside function
you can, but with different way

I disagree that user must do himself nothing to profit from library. This is still very user-friendly.
this is very not-user-friendly. since they must declare and define ~7 variables first to create ONE trajectory? How if I want to launch 100 missiles and each of them has different arc? so I need to declare/define (7*100 + 100 = 800) variables first? you must be joking

Q: How can I do my curve?
A: Very easy:
seriously, that's more like a joke
 
Level 7
Joined
Apr 5, 2011
Messages
245
this is very not-user-friendly. since they must declare and define ~7 variables first to create ONE trajectory? How if I want to launch 100 missiles and each of them has different arc? so I need to declare/define (7*100 + 100 = 800) variables first? you must be joking
:ogre_rage:

You need to do 100 different algorithms then, which, of course, means more code than 800 variables
I dont remember clearly.
What a shame. :D

All you need is to read my FAQ. You will see an explanation why the algorithm is good.
 

Kazeon

Hosted Project: EC
Level 33
Joined
Oct 12, 2011
Messages
3,449
You need to do 100 different algorithms then, which, of course, means more code than 800 variables
if you are using the formula that I'm talking about, you just need one or two function then you can launch more than 100 missiles at once without declaring any new variable.. :)
What a shame. :D
nope

just a reference for you, look at how he calculate the trajectory, max height, etc. so that everything is done automatically, not manually like this
JASS:
    set DATA[0] = 5
    set DATA[1] = 0
    set DATA[2] = .75
    set DATA[3] = 1
    set DATA[4] = .75
    set DATA[5] = 0


EDIT:
here is the easiest formula I have found so far:
Code:
((4.00 x fly_height_max) / distance_max) x (distance_max - distance_current) x (distance_current / distance_max)
but still, there is no point in making a system like this anymore, there are just too many great similliar systems which doing their job multiple times better than yours, I'm sorry but I'm just trying tbh, so that you don't need to update this uselessly, eventually imo this one will never get approved except if you can make it better than the other existing one, that means you must re-write the whole code bcs this one is obviously inefficient. I'm sorry I can't say this inefficient, but this is just too unfriendly for user, and not dynamic..

this is very not-user-friendly. since they must declare and define ~7 variables first to create ONE trajectory? How if I want to launch 100 missiles and each of them has different arc? so I need to declare/define (7*100 + 100 = 800) variables first? you must be joking
that is the big CON of this system
 
Last edited:
Let the user pass in an eccentricity. Generate the curves.
Don't use this Curves library crap.

It's code-smell and it has terrible disadvantages like the fact that changing one of the arrays forces you to change every other one. What if I wanted to add an extra value or change a curve completely in the middle of all the declarations? This makes everything much more prone to human error because I have to correctly change the required count and all the required indices in the array settings and the global constant declarations.
 
Level 7
Joined
Apr 5, 2011
Messages
245
wish you the best luck
Ty :)
Let the user pass in an eccentricity. Generate the curves.
Don't use this Curves library crap.

It's code-smell and it has terrible disadvantages like the fact that changing one of the arrays forces you to change every other one. What if I wanted to add an extra value or change a curve completely in the middle of all the declarations? This makes everything much more prone to human error because I have to correctly change the required count and all the required indices in the array settings and the global constant declarations.
I can make functions like SetCurve and CurveSetPoint.
For example:
JASS:
call SetCurve(ROAD_TO_SCHOOL)
call CurveSetPoint(0)
call CurveSetPoint(0)
call CurveSetPoint(0)
call CurveSetPoint(0)
call CurveSetPoint(0)
call CurveSetPoint(0) //first lesson
call CurveSetPoint(0)
call CurveSetPoint(0)
call CurveSetPoint(1)
Is it acceptable?

Added:
you are completely retarted, fag >.> his algorithm is the best, you can't doubt it no matter what
My algorithm isn't the best, because I use timer array. There is a lot of improvement I can bring into it.
 
Why not put the FAQ into the header? Do you expect your users to remember those or to always check this thread if they forgot what those numbers are???

You cannot use a single timer because you need different periods? Ever heard of counters? You can effectively use a single timer for this that uses a .03xxxx period and a counter for each instance... Though if you look at most movement/projectile scripts out there, they don't even use counters, they simply move everything every tick...

and yeah, I agree with the Dreadlord, we don't need another one of these
 
Top