- Joined
- Feb 4, 2009
- Messages
- 1,314
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)
("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
- 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)
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)
-
Loop - Actions
- 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)
-
Loop - Actions
- -------- 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
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.
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
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)
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
So we are going to make a function with a nice and smooth curve like this:
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":
So our new formula looks like:
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
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):
The second line will go through (0|z0) and (d|0):
Now we merge these two lines:
In our last step we will add the new line to our parabola function and get this:
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
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)
- 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:
- Download this http://www.hiveworkshop.com/forums/tools-560/war3-model-editor-62876/
- Open it (2.1. Make it work, good luck)
- File - New
- Window - Node Manager
- Right click - Create Bone (or Helper)
- Right click - Edit Node - Rotation
- Select "Interpolation Type - Linear" and paste one part of the code below this list
- Right click the bone or helper and click "Create Attachment"
- Right click - Edit Node - Rename it to "Origin Ref" or any other attachment point name (as long as you add " Ref" to it)
- Window - Sequence Manager - Right click - Create New - Name it "Stand" - From 0 to (duration of animation in milliseconds)
- File - Save
- Import it to your map and change buffs and attachment points to origin or whatever you named it
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 }
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 }
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
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.png6.5 KB · Views: 7,716
-
circle.jpg277.3 KB · Views: 8,081
-
final_parabola.png1.9 KB · Views: 7,584
-
line1.png949 bytes · Views: 7,524
-
line2.png875 bytes · Views: 7,583
-
simplify_parabola.png11.6 KB · Views: 8,406
-
merge_lines.png3.9 KB · Views: 7,607
-
parabola_anim.gif134.6 KB · Views: 7,879
-
good.png2.5 KB · Views: 9,861
-
bad.png4.1 KB · Views: 7,726
-
Movement.w3x22.8 KB · Views: 4,022
-
FireBlast.mdx8 KB · Views: 3,762
Last edited by a moderator: