• 🏆 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!

About Movement

Level 19
Joined
Feb 4, 2009
Messages
1,313
About Movement

We will start with simple movement on flat terrain. First, NEVER use the GUI function for moving units because it is like 10 times slower. These awesome JASS-functions which can also be used in GUI as "Custom script" are much faster and additionally they don't interrupt orders. Don't worry about locations. Coordinates are faster anyway.

  • Custom script: call SetUnitX(udg_unit_variable, udg_x)
  • Custom script: call SetUnitY(udg_unit_variable, udg_y)
Unfortunately, the JASS alternative for moving units does not include any kind of pathing checks, so we will have to do this ourself. It can be done with an item because items snap to the next pathable position instantly. So we move our item to the desired position and check if it still is at the same position. If it is the point must be pathable and we will move the unit to the position of the item.

("x" and "y" are reals, "pitem" and "pitem2" are our pathcheckitems and "u" is the unit we will move to the checked position in case it is pathable)

  • Custom script: call SetItemPosition(udg_pitem, udg_x, udg_y)
  • Custom script: if GetWidgetX(udg_pitem) == udg_x and GetWidgetY(udg_pitem) == udg_y then
  • Custom script: call SetUnitX(udg_u, udg_x)
  • Custom script: call SetUnitY(udg_u, udg_y)
  • Custom script: endif
The automatic item snapping won't work correctly in a few cases, e.g. in deep water or on cliffs which are full of trees. The items will just stay there. Usually one can't get there without passing an impassable area but if you want to make sure that things work correctly all the time you can use a second item because it will get stuck at the same point. So if a point is pathable the first item will stay at that point and the second item will snap next to it. In all other cases the terrain is unpathable.

  • Custom script: call SetItemPosition(udg_pitem, udg_x, udg_y)
  • Custom script: call SetItemPosition(udg_pitem2, udg_x, udg_y)
  • Custom script: if GetWidgetX(udg_pitem) == udg_x and GetWidgetY(udg_pitem) == udg_y and GetWidgetX(udg_pitem2) != udg_x and GetWidgetY(udg_pitem2) != udg_y then
  • Custom script: call SetUnitX(udg_u, udg_x)
  • Custom script: call SetUnitY(udg_u, udg_y)
  • Custom script: endif
  • -------- If we move our items they will be unhiden so we hide them again --------
  • Custom script: call SetItemVisible(udg_pitem, false)
  • Custom script: call SetItemVisible(udg_pitem2, false)
Now you might think what will happen if there is an item at the point where we want to place our unit?
The answer is: It will block our pathchecking items and everything will act as if the point would be unpathable.
The solution:
1. We hide all items in the region around the desired position (pregion) and count them (pmax)
2. We apply our pathing check
3. We unhide all items and reset our counter
I am using an array to save these items (parray) but if you are sure that there are no other hidden items than the ones you are using for path checking you are free to hide/unhide all items laying around.

  • Custom script: call MoveRectTo(udg_pregion, udg_x, udg_y)
  • Item - Pick every item in pregion and do (Actions)
    • Loop - Actions
      • Set pmax = (pmax + 1)
      • Set parray[pmax] = (Picked item)
      • Custom script: call SetItemVisible(GetEnumItem(), false)
  • Custom script: call SetItemPosition(udg_pitem, udg_x, udg_y)
  • Custom script: call SetItemPosition(udg_pitem2, udg_x, udg_y)
  • Custom script: if GetWidgetX(udg_pitem) == udg_x and GetWidgetY(udg_pitem) == udg_y and GetWidgetX(udg_pitem2) != udg_x and GetWidgetY(udg_pitem2) != udg_y then
  • Custom script: call SetUnitX(udg_u, udg_x)
  • Custom script: call SetUnitY(udg_u, udg_y)
  • Custom script: endif
  • For each (Integer i) from 1 to pmax, do (Actions)
    • Loop - Actions
      • Custom script: call SetItemVisible(udg_parray[udg_pmax], true)
  • -------- If we move an item it will be unhiden so we hide our items again --------
  • Custom script: call SetItemVisible(udg_pitem, false)
  • Custom script: call SetItemVisible(udg_pitem2, false)
  • Set pmax = 0
Now our path checking system is finished and it is much faster than the one used by Blizzard for the GUI move-unit function. However, there still is a little bug. Since items have a very small collision radius we can only recognise free points of that size. If you got a unit with a size of 32 it might fit through a space which is smaller than that using this way of pathchecking. Feel free to post any ideas on how to fix this. Of course it would be possible to check a bigger area by doing that many times but that would kill our performance advantage.

Note: Instead of comparing the coordinates directly you can also check the distance between the item and the desired x and y values using the Pythagorean theorem: 32 * 32 (most unit's size) => (x-x2) * (x-x2) + (y-y2) * (y-y2)

Circular movement

Translating a unit towards an angle requires trigonometric functions. The following picture explains how it works.

85074d1278608697-about-movement-circle.jpg


So we can use this code to calculate the translation of the x- and y-coordinates of a point by an angle "a" and a radius "r":

  • Set x = ((Cos(a)) x r)
  • Set y = ((Sin(a)) x r)
How to make units fly

To change a unit's flying height one can use this function:

  • Animation - Change (Picked unit) flying height to 300.00 at 0.00
Note: at a rate of "0.00" is instant

Too bad it does not work on ground units. But...wait! Why are Druids of the Talon and Obsidian Statues able to fly? Hmmmm...probably because of their abilities to make them fly! And because Blizzard was lazy the effect still works if the ability got removed! So we can do this:

  • Unit - Add Storm Crow Form to (Picked unit)
  • Unit - Remove Storm Crow Form from (Picked unit)
And after that we will be able to change our picked unit's flying height as if it was flying. Keeping that in mind we can move units around in 3 dimensions. E.g. we can make units jump or make them fly through the air after they got hit by a bomb or something.

Parabolas

Why you need parabolas:
Spells with missiles, leaping/jumping units and knockbacks will look much more natural if you are using a parabolic function. For example there are many people who make things fly like this:

  • Animation - Change (Triggering unit) flying height to 300.00 at 150.00
  • Wait 2.00 game-time seconds
  • Animation - Change (Triggering unit) flying height to 0.00 at 150.00
This is bad because it will look unnatural like this:

85086d1278611558-about-movement-bad.png


So we are going to make a function with a nice and smooth curve like this:

85085d1278611558-about-movement-good.png


There is an easy and a complicated way to do this. Let's start with the easy one which is...

Parabola for flat terrain

Variables:

* d: distance between starting and end point
* x: distance between starting point and the missile
* h: maximal height

The following equitations shall be true:

f(0) = 0
The unit shall stand on ground when starting to jump (orly?)

f(d) = 0
...and of course it shall stand on ground when landing.

f(d/2) = h
In the middle of the starting and end point the height must be maximal.


We will start with the general parabola function which looks like this:

f(x) = ax² + bx + c

There is no need for the "+ bx" part because both sides of the parabola should be mirrored so we have:

f(x) = ax² + c

Let's plug in the height h for c!

f(x) = ax² + h

To make it look more like our desired parabola we shift the axis by d/2:

f(x) = a (x - d/2)² + h

We still don't know anything about "a" so we take "f(d) = 0":

85073d1278608697-about-movement-calc_a.png


So our new formula looks like:

a%3E


EDIT by Purge: Fixed the formula:
JASS:
function GetParabolaZ takes real x, real d, real h returns real
    return 4 * h * x * (d - x) / (d * d)
endfunction
Note: Better don't use the Pow(base, exponent) here because it is able to solve stuff such as Pow(-42, -13.37) and therefore quite complicated and slow.

Parabola for different starting and end height

We already got a formula for same terrain height so the only thing we have to do is to fix the differences. This can be done by overlaying the parabola with two lines.

Our first line will go through (0|0) and (d|zd):

85077d1278608697-about-movement-line2.png


The second line will go through (0|z0) and (d|0):

85076d1278608697-about-movement-line1.png


Now we merge these two lines:

85078d1278608716-about-movement-merge_lines.png


In our last step we will add the new line to our parabola function and get this:

85075d1278608697-about-movement-final_parabola.png


JASS:
function GetParabolaZEx takes real x, real d, real h, real z0, real zd returns real
    return 4 * h * x * (d -x) / (d * d) + x * (zd-z0) / d + z0
endfunction
Finally a nice animation to make things more picturesque:

85084d1278611164-about-movement-parabola_anim.gif


This function won't work if you are using it just as it is. You will have to subtract the current height at the point you want to move your unit to because our parabola calculates the absolute height. How to do that? Read the next topic.

In case you prefer other parabolas these formulas might come in handy for you:

Trajectory with starting speed v0, angle a and gravitation g:
h(x) = tan(a) x - (gx²) / (2 v0² cos(a)²)

Distance:
d(x) = (v0² sin(2a))/(g)

Height:
h(x) = (v0² sin(a)²)/(2g)

Time:
t(x) = (v0 sin(a))/g

Getting the terrain height

There is no function such as "GetTerrainHeight(x,y)" but there is GetLocationZ(location). I recommend to create a location at map-ini so you can use it any time to check pathing. Of course you could create a new one every time you need one but "call MoveLocation(location, x, y)" is better.

  • Custom script: set udg_r = GetLocationZ(udg_zLoc)
So if we want to use our function it would look like this:

  • Animation - Change u flying height to ((((((4.00 x Height) x Distance) x (MaxDistance - Distance)) / (MaxDistance x MaxDistance)) + ((Distance x (EndHeight - StartingHeight)) / MaxDistance)) + (StartingHeight - r)) at 0.00
Moving units around using dummy models

It is possible to attach special effects to certain attachment points of units. Since we are too lazy to write down every model path for every unit to use them for triggers we will use the "Cyclone" ability. It will attach the target to a given model which is specified in the buff "Cyclone (Extra)". On default this is "Abilities\Spells\NightElf\Cyclone\CycloneTarget.mdl" (Press Ctrl + D to see raw data) and the attachment point "sprite, first". Any other model with any other attachment point of that model will work as well, for example a blademaster with attachment point "weapon". Further advantages: You don't have to trigger pathing/invulnerability/pausing and movement and therefore it will be much faster.
But why not make a custom dummy model which moves our unit? Spinning a unit very fast usually is impossible because the unit turn rate is limited but with a dummy and cyclone it will work.

How to make such a dummy? - Follow these steps:

  1. Download this http://www.hiveworkshop.com/forums/tools-560/war3-model-editor-62876/
  2. Open it (2.1. Make it work, good luck)
  3. File - New
  4. Window - Node Manager
  5. Right click - Create Bone (or Helper)
  6. Right click - Edit Node - Rotation
  7. Select "Interpolation Type - Linear" and paste one part of the code below this list
  8. Right click the bone or helper and click "Create Attachment"
  9. Right click - Edit Node - Rename it to "Origin Ref" or any other attachment point name (as long as you add " Ref" to it)
  10. Window - Sequence Manager - Right click - Create New - Name it "Stand" - From 0 to (duration of animation in milliseconds)
  11. File - Save
  12. Import it to your map and change buffs and attachment points to origin or whatever you named it
Rotate around y-axis
Code:
0: { 0, 0, 0, 1 }
1000: { 0, 0.7, 0, 0.7 }
2000: { 0, 1, 0, 0 }
3000: { 0, -0.7, 0, 0.7 }
4000: { 0, 0, 0, 1 }
Rotate around z-axis
Code:
0: { 0, 0, 0, 1 }
1000: { 0, 0, 0.7, 0.7 }
2000: { 0, 0, 1, 0 }
3000: { 0, 0, -0.7, 0.7 }
4000: { 0, 0, 0, 1 }
Change 0-4000 to your desired duration in milliseconds.


Well-known facts about movement:

  • usually units can't move faster than 522 but this limit can be exceeded with triggers (for example this way: http://www.thehelper.net/forums/showthread.php?t=133708)
  • you can change the minimum movementspeed to 0: Advanced - Gameplay Constants - [Tick "Custom Gameplay Constants]

Not-so-well-known-facts:

  • invisible units can be blocked with items
  • if you build a building and fill the last point of pathable terrain on a cliff with it you will get glitched to the next pathable point with same cliff height in most cases
  • by giving a building the root ability of the nightelf buildings they become able to rotate while attacking
  • trigger-tags eat line breaks
This http://www.thehelper.net/forums/showthread.php?t=106212 inspired me to write this tutorial. I didn't copy anything but the idea. Credits to Ace though, good job :)

The attached test map features examples for the parabola functions, pathchecking with 2 items and spinning units with cyclone.
The other map contains an example spell and a dummy both using a parabola for their movement.
 

Attachments

  • calc_a.png
    calc_a.png
    6.5 KB · Views: 7,707
  • circle.jpg
    circle.jpg
    277.3 KB · Views: 8,070
  • final_parabola.png
    final_parabola.png
    1.9 KB · Views: 7,575
  • line1.png
    line1.png
    949 bytes · Views: 7,518
  • line2.png
    line2.png
    875 bytes · Views: 7,577
  • merge_lines.png
    merge_lines.png
    3.9 KB · Views: 7,601
  • simplify_parabola.png
    simplify_parabola.png
    11.6 KB · Views: 8,400
  • parabola_anim.gif
    parabola_anim.gif
    134.6 KB · Views: 7,873
  • good.png
    good.png
    2.5 KB · Views: 9,857
  • bad.png
    bad.png
    4.1 KB · Views: 7,715
  • Movement.w3x
    22.8 KB · Views: 4,012
  • FireBlast.mdx
    8 KB · Views: 3,756
Last edited by a moderator:
First, NEVER use the GUI function for moving units because it is slow.

The GUI function interrupts orders, while SetUnitX/SetUnitY doesn't, which is most of the reason why it is preferred. ;D [Although, yes, SetUnitPosition is definitely slow as well]

Otherwise, nice job. I'll probably approve this after updating some of the grammar. ( whenever I get to it ;P )
 
Level 2
Joined
Apr 10, 2010
Messages
22
nice tutorial, helped me a lot... :thumbs_up:
but I've got a little question (I suck at math)
is it possible do make unit moving like in this image...?
if yes you could add it...

and yes I like these "...":grin:
 

Attachments

  • curve.jpg
    curve.jpg
    9.6 KB · Views: 3,814
Level 17
Joined
Jun 17, 2007
Messages
1,433
This would be the function (the original made negative), but I'm not sure what you would do with it unless your unit is air or otherwise has a fly height > 0.
JASS:
function GetParabolaZ takes real x, real d, real h, returns real
    return -4 * h * x (d -x) / (d * d)
endfunction
 
Last edited:
Level 2
Joined
Apr 10, 2010
Messages
22
first thank you!
second, the answer to your question:
what about a dragon coming down from the sky, slaming the ground, and then rising again?
 
Last edited:
Top