**Introduction**
Since I left school 17 years ago, I thought I'd never have to use the stuff (trig) again, but then I discovered I needed it for the most important thing of all. SPELLS! SPELLS! SPELLS! Also, I thought I'd better write it all down before I forgot.

When your hero needs to push, pull or swing another unit or doodad, or if you just need to know where to place your special effects, trigonometry is the way to go, as plotting positions is like drawing a right-angled triangle. See the similarities in these two diagrams?

**Trig **
Trig formulas. We may be referring to some of these in this tutorial, but all are helpful. We will also need to refer to the green triangle (above) quite a bit. If I mention "c", I'm referring to the length of side c. If I mention "A", I'm referring to the size of angle "A", so if you find it hard to follow, scroll back up here for a look-see.

a2+b2=c2

Sin A = a / c, Sin B = b / c

Cos A = b / c, Cos B = a / c

Tan A = a / b, Tan B = b / a

arcsine(a / c) = A, arcsine(b / c) = B

arccosine(b / c) = A, arccosine(a / c) = B

arctangent(a / b) = A, arctangent(b / a) = B

radian measure times radius = arc length

**Limitations**
__IMPORTANT:__ Use a new map or make sure you save a backup! I don't want to be held responsible for any crashes. This works fine for me, but I don't know what you've got on your map.

__IMPORTANT:__ If you move a unit off the playable map area the map will crash. There are many functions that check for this, but I won't be writing one here. If you want to use this code in your map, replace all instances of "CS_SafeX" and "CS_SafeY" (from Vexorian's

Caster System) with the functions you choose to use, or download and implement the latest

Caster System.

NOTE: I also make use of Vexorian's

CheckPathability() function to ensure the stuff I move doesn't go through trees and over cliffs. You can use this, or replace this with whatever you normally use.

NOTE: Used as is, these functions don't actually look cool. You'd need to increment the positioning, looping the movement in small amounts to get it to look realistic, but that will be covered in the next tutorial (Moving Stuff With JASS 2 - Realistic Movement).

**Degrees and Radians**
There are 2 main standards for measuring angles. Degrees and Radians. 360 degrees = 2*PI radians. In this tutorial, we will use Degrees to measure angles, since I understand them. Trouble is, JASS uses Radians in it's trig functions. I don't understand them. There are, however, a couple of global constants which convert between the two: bj_DEGTORAD and bj_RADTODEG. So when you see those in the code, that's what they're doing.

Now the intro is over....let's get started.

###################################################

**Positioning Helper Function**
I wrote a function to help me with repositioning, which I'd like to share with you now.

It uses the formulae "Cos A = b / c" and "Sin A = a / c". The function takes the "c" distance, the "A" angle, the point of origin (usually the position of the caster) and a boolean for whether or not you want to check pathability.

What we are returning is the position where sides c & a of the triangle meet, the destination. The first thing we need to do is find the X,Y coordinates of the point of origin (on the triangle, this is where sides c & b meet).

JASS:

function knutz_NewPos takes location origin, real c, real A, boolean checkpath returns location
local real oX = GetLocationX(origin)
local real oY = GetLocationY(origin)

This established, we need to use Cos A = b / c to find the length of side "b", the difference in X from origin to destination.

JASS:

local real newX = CS_SafeX(oX+(Cos(A*bj_DEGTORAD)*c))

Notice here that we multiplied the angle by bj_DEGTORAD before calling Sin. We do this because the trig functions in JASS are set to Radians mode, and we start off with degrees. Then we add the X of the origin. Now we have the X coordinate of the destination.

Now, we need to use Sin A = a / c to find the length of side "a", the difference in Y from origin to destination.

JASS:

local real newY = CS_SafeY(oY+(Sin(A*bj_DEGTORAD)*c))

Same explanation as above, but for the Y coordinate. Now it's time to see if we CAN move stuff to this location.

JASS:

if ((check_path == true) and (CheckPathability(newX,newY) != true)) then
return null
endif

What this section does is checks for obsticles, and returns a null if the way is blocked. The calling function should filter out the nulls, as I'm not sure what would happen if you assign a null location to an object.

Then the finale, returning the destination:

JASS:

return Location(newX,newY)
endfunction

Here's the function all together:

JASS:

function knutz_NewPos takes location origin, real c, real A, boolean check_path returns location
local real oX = GetLocationX(origin)
local real oY = GetLocationY(origin)
local real newX = CS_SafeX(oX+(Cos(A*bj_DEGTORAD)*c))
local real newY = CS_SafeY(oY+(Sin(A*bj_DEGTORAD)*c))
if ((check_path == true) and (CheckPathability(newX,newY) != true)) then
return null
endif
return Location(newX,newY)
endfunction

This function is used in every section from here on. If you are using this in a map, you will need to copy this into your custom text section, below the functions you use in place of CS_SafeX, CS_SafeY and CheckPathability. So all we need to find out now is the point of origin, the angle and distance to the destination and whether or not we want stuff to go through trees and up cliffs.

####################################################

**Moving Stuff in a Straight Line**
OK, so now we have our helper function to reposition our stuff, we just need to find the information it requires. When moving stuff in a straight line, this is fairly easy. Set up a trigger to act on a spell event, and we'll play with the "actions" function.

What we need to know for the helper function:

- The point of origin( We'll assume it's the position of the caster. )
- The angle from the caster to the destination (angle "A")
- The distance from the point of origin to the destination (distance between the caster and target + the move distance. The length of side "c")

Once we know these readily available variables, we can work out the rest. Here's how we get them:

JASS:

function Trig_<SomeTrigger>_Actions takes nothing returns nothing
local unit caster = GetTriggerUnit()
local unit target = GetSpellTargetUnit()
local location locC = GetUnitLoc(caster)
local location locT = GetUnitLoc(target)

Now we have the point of origin (LocC), using the location of the target (LocT) we can find both angle "A" and distance "c".

JASS:

local real A = AngleBetweenPoints(locC,locT)
local real OLDc = DistanceBetweenPoints(locC,locT)
local real c = OLDc + 100.00

We have all we need to use the helper function now. If we assume we don't want the target to go over cliffs or through trees, we'll ask the function to check the pathability.

JASS:

local location NewLoc = knutz_NewPos(locC,c,A,true)

Now to move the target. We need to remember that the helper function returns a null if the way is blocked. So if it is, we'll skip moving the target.

JASS:

if (NewLoc != null) then
call SetUnitPositionLoc(target,NewLoc)
endif

Done! But before we end the function we should clean up any memory leaks.

JASS:

set caster = null
set target = null
call RemoveLocation(locC)
set locC = null
call RemoveLocation(locT)
set locT = null
call RemoveLocation(NewLoc)
set NewLoc = null
endfunction

Now let's see it all together:

JASS:

function Trig_<SomeTrigger>_Actions takes nothing returns nothing
local unit caster = GetTriggerUnit()
local unit target = GetSpellTargetUnit()
local location locC = GetUnitLoc(caster)
local location locT = GetUnitLoc(target)
local real A = AngleBetweenPoints(locC,locT)
local real OLDc = DistanceBetweenPoints(locC,locT)
local real c = OLDc + 100.00
local location NewLoc = knutz_NewPos(locC,c,A,true)
if (NewLoc != null) then
call SetUnitPositionLoc(target,NewLoc)
endif
set caster = null
set target = null
call RemoveLocation(locC)
set locC = null
call RemoveLocation(locT)
set locT = null
call RemoveLocation(NewLoc)
set NewLoc = null
endfunction

####################################################

**Moving in a Curve by degrees**
So we want our caster to swing the enemy sideways along a curve (or just place stuff in a circle from a central location). We already have the useful "NewPos" function, we just have to alter our "Trig_<SomeTrigger>_Actions" a bit for this. Instead of changing the lenth of "c", we'll change the size of "A".

What we need to know for the helper function:

- The point of origin( We'll assume it's the position of the caster. )
- The angle from the caster to the destination (angle "A"+ or - angle of move)
- The distance from the point of origin to the destination (distance between the caster and target. The length of side "c")

Once we know these readily available variables, we can work out the rest. Here's how we get them:

JASS:

function Trig_<SomeTrigger>_Actions takes nothing returns nothing
local unit caster = GetTriggerUnit()
local unit target = GetSpellTargetUnit()
local location locC = GetUnitLoc(caster)
local location locT = GetUnitLoc(target)

Now to retrieve the original angle and adjust it for the move. If you subtract from the original angle the movement will be clockwise, if you add, anti-clockwise.

JASS:

local real OldA = AngleBetweenPoints(locC,locT)
local real A = OldA + 15.00

Next, we will find out the "c" distance.

JASS:

local real c = DistanceBetweenPoints(locC,locT)

Now to set the new location, checking the path is clear.

JASS:

local location NewLoc = knutz_NewPos(locC,c,A,true)

At last, we move the target, remembering that if the path is blocked a null value will be returned.

JASS:

if (NewLoc != null) then
call SetUnitPositionLoc(target,NewLoc)
endif

Finally, we clean up any memory leaks before ending the function.

JASS:

set caster = null
set target = null
call RemoveLocation(locC)
set locC = null
call RemoveLocation(locT)
set locT = null
call RemoveLocation(NewLoc)
set NewLoc = null
endfunction

Now let's see it all together:

JASS:

function Trig_<SomeTrigger>_Actions takes nothing returns nothing
local unit caster = GetTriggerUnit()
local unit target = GetSpellTargetUnit()
local location locC = GetUnitLoc(caster)
local location locT = GetUnitLoc(target)
local real OldA = AngleBetweenPoints(locC,locT)
local real A = OldA + 15.00
local real c = DistanceBetweenPoints(locC,locT)
local location NewLoc = knutz_NewPos(locC,c,A,true)
if (NewLoc != null) then
call SetUnitPositionLoc(target,NewLoc)
endif
set caster = null
set target = null
call RemoveLocation(locC)
set locC = null
call RemoveLocation(locT)
set locT = null
call RemoveLocation(NewLoc)
set NewLoc = null
endfunction

####################################################

**Moving in a Curve by distance**
So we want our caster to swing the enemy sideways along a curve (or just place stuff in a circle from a central location). We already have the useful "NewPos" function, we just have to alter our "Trig_<SomeTrigger>_Actions" a bit for this. Instead of changing the lenth of "c", we'll change the size of "A". As we're using distance along the curve though, we'll have to refer to another formula:

radian measure times radius = arc length

(the arc length being our move distance, the radius being our "c" distance)

We need to find our new angle from the distance along the curve. So we must divide the move distance by "c", then convert the radians to degrees before we use our knutz_NewPos function.

What we need to know for the helper function:

- The point of origin( We'll assume it's the position of the caster. )
- The angle from the caster to the destination (angle "A"+ or - angle of move)
- The distance from the point of origin to the destination (distance between the caster and target. The length of side "c")

Let's get all the info:

JASS:

function Trig_<SomeTrigger>_Actions takes nothing returns nothing
local unit caster = GetTriggerUnit()
local unit target = GetSpellTargetUnit()
local location locC = GetUnitLoc(caster)
local location locT = GetUnitLoc(target)

As we need the "c" distance to find the angle adjustment, let's get that first.

JASS:

local real c = DistanceBetweenPoints(locC,locT)

Now to sort out the angle using our new formula. Remember, subtract from the original angle for clockwise, add for anti-clockwise.

JASS:

local real OldA = AngleBetweenPoints(locC,locT)
local real A = OldA + ((400.00/c)*bj_RADTODEG)

Now to set the new location, checking the path is clear.

JASS:

local location NewLoc = knutz_NewPos(locC,c,A,true)

At last, we move the target along the curve! Remembering that a blocked path returns a null location.

JASS:

if (NewLoc != null) then
call SetUnitPositionLoc(target,NewLoc)
endif

Finally, we clean up the memory leaks before ending the function.

JASS:

set caster = null
set target = null
call RemoveLocation(locC)
set locC = null
call RemoveLocation(locT)
set locT = null
call RemoveLocation(NewLoc)
set NewLoc = null
endfunction

EASY AS PI! Now let's see it all together.

JASS:

function Trig_<SomeTrigger>_Actions takes nothing returns nothing
local unit caster = GetTriggerUnit()
local unit target = GetSpellTargetUnit()
local location locC = GetUnitLoc(caster)
local location locT = GetUnitLoc(target)
local real c = DistanceBetweenPoints(locC,locT)
local real OldA = AngleBetweenPoints(locC,locT)
local real A = OldA + ((400.00/c)*bj_RADTODEG)
local location NewLoc = knutz_NewPos(locC,c,A,true)
if (NewLoc != null) then
call SetUnitPositionLoc(target,NewLoc)
endif
set caster = null
set target = null
call RemoveLocation(locC)
set locC = null
call RemoveLocation(locT)
set locT = null
call RemoveLocation(NewLoc)
set NewLoc = null
endfunction

####################################################

**Moving something sideways In a straight line**
Ok. This one freaked me out until I realised it was just 3 right-angled triangles where our original "c" distance becomes the second triangle's "b" distance. Only now that "b" isn't parallel to the east-west of the map, we can't use "b2" and "a2" for "x" and "y". We'll have to come up with yet a third triangle to do that. Confused? I was too, but to help, I'll give the suffix "2" to the second triangle and "3" to the third. And here's a little diagram to explain what I just said:

Formulas we will refer to here are "arctangent of (a/b) = A" and "a2+b2=c2"

What we need to know for the helper function:

- The point of origin( We'll assume it's the position of the caster. )
- The angle from the caster to the destination (angle "A"+ or - angle of move)
- The distance from the point of origin to the destination (we will need to work this out with trig first)

Let's get all the info:

Note: I'll be creating allot of unnecessary variables to help you follow what I'm doing.

JASS:

function Trig_<SomeTrigger>_Actions takes nothing returns nothing
local unit caster = GetTriggerUnit()
local unit target = GetSpellTargetUnit()
local location locC = GetUnitLoc(caster)
local location locT = GetUnitLoc(target)

Now to get the info we need about triangle 1

JASS:

local real c = DistanceBetweenPoints(locC,locT)
local real A = AngleBetweenPoints(locC,locT)

Now for the second triangle. This is joined to the first on the "c" side, which has become "b2".

JASS:

local real b2 = c
local real a2 = 100.00
local real c2 = SquareRoot(Pow(a2,2)+Pow(b2,2))
local real A2 = Atan2(a2,b2)*bj_RADTODEG

Now for the third triangle, the one we use for our final positioning.

JASS:

local real A3 = A+A2
local real c3 = c2

Next, we call our helper function for the location of the destination, and to check the pathing.

JASS:

local location NewLoc = knutz_NewPos(locC,c3,A3,true)

Remembering that a blocked path will return a null value, move the target if it doesn't.

JASS:

if (NewLoc != null) then
call SetUnitPositionLoc(target,NewLoc)
endif

Finally, clean up the memory leaks and end the function.

JASS:

set caster = null
set target = null
call RemoveLocation(locC)
set locC = null
call RemoveLocation(locT)
set locT = null
call RemoveLocation(NewLoc)
set NewLoc = null
endfunction

Now let's see it all together.

JASS:

function Trig_<SomeTrigger>_Actions takes nothing returns nothing
local unit caster = GetTriggerUnit()
local unit target = GetSpellTargetUnit()
local location locC = GetUnitLoc(caster)
local location locT = GetUnitLoc(target)
local real c = DistanceBetweenPoints(locC,locT)
local real A = AngleBetweenPoints(locC,locT)
local real b2 = c
local real a2 = 100.00
local real c2 = SquareRoot((a2*a2)+(b2*b2))
local real A2 = Atan2(a2,b2)*bj_RADTODEG
local real A3 = A+A2
local real c3 = c2
local location NewLoc = knutz_NewPos(locC,c3,A3,true)
if (NewLoc != null) then
call SetUnitPositionLoc(target,NewLoc)
endif
set caster = null
set target = null
call RemoveLocation(locC)
set locC = null
call RemoveLocation(locT)
set locT = null
call RemoveLocation(NewLoc)
set NewLoc = null
endfunction

####################################################

**Author's Notes **
For the experienced JASSers on this site, thanks for answering all my stupid questions. I hope I can repay the favours by answering some for other members now.

Coming Tutorials:

(If this one is approved that is)

- Moving Stuff With JASS 2 -Realistic movement (incrementing using distance and speed.)
- Moving Stuff With JASS 3 - 3D movement (yes more trig! (Although I'll have to learn this first, which I will as I need it for a system I'm developing))