Triggered Unit Movement
From TDGwiki
Written by CHUNK
Contents |
Introduction
This is a simple tutorial on moving units through triggers that is meant for people with at least a modest background in triggering. If you are unsure what value these type of triggers have, they are a common element in triggered abilities – for example, units that go flying when something near them explodes or they get knocked backwards when hit, but it is also useful for less obvious unit movement, for example disguising a unit as a missile that will explode when it collides with something it is traveling into.
For those of you who are already familiar with triggered unit movement that might be interested in a more efficient method of moving units around, I will eventually link in a tutorial on using custom GUI coordinate functions here.
Triggered movement of units is actually a fairly simple process; it is essentially breaking the path of a unit into a series of short moves that are repeatedly executed by a periodic event (or a timer). In doing this (with a bit of math), you can actually make units do some fairly complex things without much extra work, but for now, let’s just start with the basics.
Moving Units and Memory Leaks
This section will cover how to move units instantly over small increments and how to do so effectively, without creating memory leaks.
Moving Units Instantly
To move units around with triggers and make them look to be moving smoothly, there are two fundamental actions you will need. The first, which should be obvious, is
| Unit - Move (unit) instantly to (point) |
For this tutorial, we will only be using point-based movement, and if you are making a trigger that repeatedly moves a unit, it is essential that you pay very close attention to memory leak management. If you aren’t familiar with how to do this, we will cover the basics of point leaks here.
The reason that managing point leaks is important, is that you will be moving units around very quickly (a series of very small hops) and each hop will leave behind a reference to the point where the unit has been moved. Points are made up of two component values (x, y) which are two real (decimal) numbers that define the unit’s position in coordinate space. If you’ve had basic math, x and y coordinates should be somewhat familiar to you; if not, don’t worry about it – it’s not that important to understand how it works, as long as you manage the problem.
When you click on the Move Unit Instantly action, you will be forced to enter two bits of information, the unit to be moved and the point. The point will default to (Center of (playable map area)) – which is defined as 0,0 in x,y space. Whenever you enter anything at all into the point part of this action, you are creating a point reference (if it helps, think of it as creating a pixel on the terrain as an object that you could see). Every time you see (position of (triggering unit)), for example, that is a point reference that you are creating. These point references hang around on any computer that is playing the game when they are created, and they do not leave the computer’s memory until you either destroy them or stop playing Warcraft.
Now, for most points, there isn’t much reason to force your computer to keep remembering these points, because there’s isn’t anything even there any more – but Warcraft won’t destroy them without being told to. So, why not just wait until the unit is done moving and tell Warcraft to remove all of the point references? Well, that’s the heart of the problem with memory management – you’ve created this point, but once your unit moves on to the next point, there is no way for the computer to go back and delete points that it previously created, because it doesn’t have any way to refer to that point. These points which are being remembered by the computer, but can’t be referenced by the computer are called leaks – because the available memory is slowly leaking away (I guess) as it remembers more and more points but can’t destroy them because it can’t find them any longer.
How do we resolve this problem with points ‘leaking’? Simple – we give the point a permanent reference and then we can destroy it whenever we want (usually immediately). I am going into a lot of detail about destroying point leaks here because you are going to generate thousands of points (or perhaps billions over the course of a game) and if you don’t destroy them, the computer that your players are using will eventually get bogged down during the game and when they leave the game there will be a long pause before they can return to the Warcraft interface.
In this tutorial, we will use a series of variables. For the sake of consistency, the unit being moved will always be the unit variable YourUnit, and the (position of YourUnit) will always be tempPt. Here is the proper way to deal with moving a unit instantly without creating memory leaks:
Actions
|
Set the variable = the point you want to move the unit to. Move the unit to the variable point. Destroy the point with a little custom script.
You don’t absolutely need to destroy points immediately after you use them, but if you assign some other point to the variable before you destroy it, you are defeating the purpose – you are still creating a point leak. In this example, we didn’t actually move the unit at all (or rather we moved it to its current position) but that was just done for consistency with the following sections.
Using Point with Polar Offset
The second fundamental function of any triggered movement is the point with polar offset function. You get to this function by attempting to fill in a value for a point. If you are moving a unit instantly it would be an option that would go in this slot:
| Unit - Move YourUnit instantly to (point with polar offset) |
Point with polar offset is composed of 3 parts, a point (duh) an offset (distance from that point) and a polar direction (angle). In essence, you are defining a new point by using one point and describing the new point in terms of that point, some direction, and some distance. The angle is the angle from the old point to the new one.
Part of the reason that I spent so much time discussing memory leaks is that when you use the function Point with Polar Offset, you are generating a new point. So each time you use it, you create a point and then another point defined a reference to the first one. If you move a unit 50 little steps and don’t control the memory leaks, you will create 100 point leaks. Run that trigger ten times and you are looking at 1000 point leaks.
There is a simple way to deal with point leaks generated by Point with Polar Offset – set the point to a variable before you use it, use the variable, and then destroy the point after you finish – just as we did with the original point. Here’s an example, using tempPt2 as the offset point – all I’ve done is change a few things from our previous code:
Actions
|
In this example, I left the default values (offset 256.00, 0.00 degrees) in as the values and I changed the destination of our move unit instantly point to be the offset point instead of the original point from our first example. This little bit of code is the fundamental part of any triggered unit movement. All we need to do is shorten the movement distance (256 is a bit too big of a step to move a unit) and change the value for the angle to whatever we want – and then put this inside of a trigger that repeats steps.
In all of the following examples, I will be using a real variable to hold the value of the offset distance [Increment] and another to hold the value of the direction we are moving the unit [YourAngle]. You can go ahead and make these all of these variables now so you are ready for the next section, where we will create the loop that will move the unit around.
Looped Movement
Every triggered unit movement uses one of two possible options to move the unit: timers or periodics. I’m not really sure that one is always better than the other, although I generally use periodic events out of habit. Feel free to replace the periodics with timers if you prefer, it is unlikely to have a major impact on anything – they both are used the same way, as a means for quick repeating actions.
When you move units around with triggers, you will probably need some sort of destination in mind. Sometimes you want to move a unit in a series of constant steps: like you always knock back units 5000 pixels in exactly 100 steps, so you can set the value of Increment = to some constant value (50 in this case). Sometimes you may want to move a unit for a specific duration, sometimes you may want to have the total distance be variable. You need to figure these things out before you move on to your periodic. For our example, I will show two examples, one in which you have a constant increment size and one in which your final distance determines the size of the increments.
Before we start with the loops, there are two things I will recommend from experience:
- Keep your periodics or timer increments above 0.03 but probably somewhere below 0.10 – this is mainly because many computers on bnet cannot easily handle loop rates faster than 0.03 (0.04 is probably a little better, actually).
- Keep your movement increment values somewhere between 30 and 60. Much smaller and the unit will not appear to be moving, much larger and the movement will appear too jerky.
Moving units preset distances with triggers
So, let’s put together a basic incremental movement loop:
Incremental Movement Loop
|
In this example, the increment is always 35.00, so if you’d prefer, you can set the increment variable to a default value or you can set the increment value somewhere else. I’ve just stuck it here for now so that you can see the distance increment and how we will be using it.
Now, if you are using periodics, you will want to start with the periodic trigger turned off and you will need to do something to turn it on and off. My usual way of dealing with this is to turn the periodic on with whatever event (a unit starts the effect of an ability, say) and then have the periodic turn itself off. This is very easy to do with a simple if/then (multiple) action and a new variable that tracks the total distance that the unit has been moved.
For now, let’s just use a chat event to set our basic variable values such as YourUnit, YourAngle, Distance and Increment. Then turn on our loop, which will start defaulted off. Then we will add an if/then fork to our looping trigger and each loop it will subtract the increment moved from the total distance and if the value of distance is still greater than 0.00, we will move the unit, if not, turn off our loop so it returns to an off state for the next time we run it.
Starting Event
|
Incremental Movement Loop
|
As you can see, each loop the value of distance drops by 35.00, if the value of distance is still greater than 800, it will move the unit, if not, it will turn the looping trigger back off. The nice part about doing your loop this way is that if your unit gets stuck on some terrain boundary and stops being able to be moved, the trigger will still shut itself off. You can also easily add extra conditions to keep the unit from being moved into certain regions or over artificial terrain boundaries.
For a trigger like the one above, only one instance of the trigger can be running at a time, because I’ve only used global variables and if two units were to try and use this loop at the same time, the values of the second user would interfere with the first. In our next example, I will provide an easy solution to this issue, by showing off a way to have multiple units moving at different rates and distances all using the same loop trigger.
Moving multiple units different distances and directions with the same loop
If you want to move multiple units different distances at different rates and directions, it is fairly simple to do, but you will need to create a unit group variable (here moveGroup) and you will need to store the information about the increment, total distance, and the angle you are moving the unit on the unit itself. My preferred way to do this is to store these elements as handle variables directly on the unit, but you can get by without them by using a little trickery and storing these things as other elements on the unit – custom value, acquisition range, and unit movement speed are all elements that aren’t being used by a unit when they are being moved by triggers, so we can just go ahead and store our information on the unit in those fields.
Let’s do that now instead of using those variables in the start trigger above. And lets make the event something that any unit can do – like say any ability at all. Doesn’t matter which ability for now, just for this example. For this example, we will store the angle the unit is to travel on the unit as the movement speed (you will need to set the minimum speed to 0 for this if you use this method – Game Constants). For player units, this value does have some impact, so you should remember to reset it to something reasonable when the unit reaches its destination, but for this example, we don’t care what angle we are moving the unit, so we will just set it to a random angle. We will use the acquisition range to hold the value that distance previously used and the increment will be stored as the custom value (converted to an integer). Because I want to show off how this works for all angles, distances, and increments, we will make the distance a random value and the increment will be set for this trigger to be equal some percent of the total distance.
Starting Event
|
As you can see, we’ve utilized the unit group, adding the unit when they cast the ability to the group. We have no idea what the value of the final distance, increment, or angle is, but we want them to all work for all units who may be casting the ability at any time. If the loop is already on, turning it on won’t hurt anything, so we can leave this part of the trigger as it is.
Now all we need to do is modify our loop a bit to adjust for reading the values from the unit and change the periodic around a little so that it picks all units in the moveGroup and moves each one. We will need to make one adjustment, because acquisition ranges will not be set below 50.00, so we will introduce a temporary real variable to hold our current acquisition – 50.00. In any real triggers, we could also compensate for this extra 50 when we set the distances:
Incremental Movement Loop
|
Now you can have 100 different units all using one trigger and all moving in random directions at random rates and it will still turn itself off when all of the units are removed from moveGroup (when they all reach their destinations).
This was just an example to show how this sort of thing can be done, leak free. For a cleaner set of code, use coordinates (hopefully a tutorial on this soon) and store the values as handle variables. For now, feel free to experiment with moving units around with triggers.
I should also note that this tutorial really only explains how to move units around laterally, but once you have the lateral movement set up, it is fairly easy to do things like turn off the unit's collision, check for doodads or terrain height changes within range of the point the unit is being moved to (tempPt2), or add the ability crowform (temporarily) to the moved unit and modify the unit's flying height while it is being moved around by these triggers.
Fun with Unit Movement Triggers
One of the things you can easily do with unit movement triggers is make fancy special effects on your abilities. Believe it or not, most of what you need to know how to do, you are already familiar with! I will provide a few examples below to get you thinking about how you can make some pretty fancy stuff with a few repeating actions. Let's start with spinning units around another unit.
Example: Spinning Units
There isn't much difference between spinning units around a point or another unit and moving units in a line - just a slight change in the geometry of our repeated motions. Instead of always moving the unit in a consistent direction, we are just going to move the unit the same distance around a point by constantly increasing the angle slightly.
To do this, we start with our basic movement code from the previous triggers and our basic periodic with a unit group so we can move multiple units at once and we will build a fancy-looking effect from the ability Frost Nova, spinning units around the target to create an effect. Although in this example, we will be using a single timer, you could easily make the timers into an array and make this code work for multiple instances in the map.
First you need to create a dummy unit with flight for movement and give the unit the ability Locust and change the settings so that it can fly as high or low as you want and change the model to something you want for the effect. For this simple example, I changed my units so that they have the Frost Wyrm breath missile, because I am just adding a fancy faked stun effect to Frost Nova. Give it no attacks and set fields like shadow and food used to nothing so that it appears to be an effect and not some strange unit to people who are looking for odd things when spells are cast.
You will also need some variables: tempPt, tempPt2, a real [SpinReal] and a unit group variable [SpinnerGroup]. Eventually you will also need a timer (if you want to do things exactly as I have), and a unit variable [YourUnit]. These last two come into play in other triggers, but you may as well make all the preparation stuff at the start.
The Basic Spinner Loop
Our loop, this time called Spinner, but with the same base code as before. Here instead of variables, we are using the custom value of the units being spun around to track the unit's current angle. Because angles modulate (every 360 degrees is another cycle) there is no reason to worry about the angle values, 0, 360, 720, etc. are all the same position.
For our example, I've created a real variable - you could actually skip this step and just modify the custom value directly, but I wanted to make it easy for you to see how each loop the current angle of the unit is tracked by the custom value of the unit and each time the unit is moved slightly farther around the circle (in this case 22.00 degrees farther) and then that information is then stored back into the unit's custom value, so that it always stores it's current angle as the custom value. With each new loop, the value is read, the unit moved, then the new angle value stored:
Spinner
|
All very basic stuff for you by this point, I hope. Now that we have the units spinning around our unit, we just need to give them a little elevation with each loop. For our example, I am just going to change the current flying height by adding a slight increment to it. Once the unit gains a certain height, we will use an if/then fork to explode the unit. The result will be a units that spin around the target unit (YourUnit), each step around, they will get slightly higher, creating an upward spiraling coil, then the effect units will explode when they get to the top (and remove them from our unit group).
As with our other group of units moving all at once example, we want to shut off the spinner when we finish with it, so we can just check to see if the SpinnerGroup is empty each loop and if it is, turn off the Spinner.
All of this can be done by adding the following code continuing from the code directly above:
Animation - Change (Picked unit) flying height to ((Current flying height of (Picked unit)) + 4.00) at 1000.00
|
Simple. This is the whole spinner trigger. Now we just need to add in something to start the spinner going and place the units. For this example, we will just use a timer to periodically release new units and use custom value to track how many have been released. If you were using this in a real ability, it would be better if you gave the counting over to an integer variable (or array), but for this example we can use custom value just as easily.
Start Spinner
|
All we are doing here is the basic junk for a unit casting a specific ability, we set the target equal to YourUnit, pause the unit to fake our stun, reset the unit's custom value to 0, and then start a timer to release the locust effect units - here the timer runs every .25 seconds.
In our final trigger, we just create two effect units each time the timer repeats, one in the direction that the target unit is facing and one behind it. Then we add those units to the unit group (SpinnerGroup) and count how many units have been released. In this example, we will release 20 effect units (two every 0.25 seconds). You can set it to whatever you'd like if ten seems like too many or too few.
Spinner Release
|
Most of this trigger just is controlling the leaks being generated by all of the points you are creating. As with our periodics, our timer turns itself off after the twenty units have been created, which is why we are using the if/then fork. If you wanted to create 4 units at once, you could do that just as easily, making the units create at the facing angle + 90.00 degrees or just create one at the facing angle. I've done 2 simply because I like the effect it creates for a small column of frosty effects.
This is all you need. When the periodic shuts off our target unit is unpaused so that the fake stun ends. As I mentioned above, if you are going to make this multi-instanced, you can use variable arrays for the unit, counting integers, and reals. Hopefully you will look at this example and think of other interesting ways to revolve units around a point or unit. Some very fancy special effects for cinematics have been made using similar triggers just like these.
I am including my example map with the functional triggers for anyone who wants to mess with it a bit to get the hang of what we've done here. Feel free to check it out. Hopefully I will add more examples of fun with triggered unit movement soon.

