Tutorial Mathematics - Basic Uses and Application

Reaction score
86
Mathematics - Basic Uses and Application

Note: I know that there are already quite a few tutorials that deal with mathematics, however, some are very advanced while others deal less with mathematics and more with Warcraft. Anyway I decided to post this because I know, that I was always at a loss when it came to stuff like this, that was till I learned Jass!

Another Note: If you have programmed in another language I suggest you learn Jass! It is alot easier to program in compared to GUI.

Ok, enough preaching...

I. Table of Contents
I.Table of Contents :p
II. Uses of Math
III. Distance
IV. Angles
V.Combination- Distance + Angles
VI. 3D Section

II. Uses of Math


Lets talk about how math is implemented in Warcraft functions. Okay, many of the functions that you take for granted utilize math. For one, obviously all the "Math" functions utilize math, as well as the integer and real comparisons. Now, if you use a normal world editor, there is no condition that states "Is within range of." When many people here see that they think of a circle. Of course, it is a circle, however it is also, what some math nerds like to call it, a "Locus of points." What that means that, if you drew a line from the center of that circle to any point in on the edge of the circle, the distance will ALWAYS be the same... Theoretically of course! And since Warcraft is a video game and this theoretical fact applies to it, we can easily create a new condition " Is within range of."

Take a look:
Locus.gif


Therefore, here's your first lesson! (These are the basics if you haven't realized, if you think your a big boy you can skip ahead ;D)

So, for this new condition we will have to make use of a formula I like to call the "Distance" formula! Now, if you know what this, skip ahead, if you don't just keep this formula in mind. You'll need it later! Anyway, the formula is:
D (Distance) = Sqrt(SquareRoot)((X1-X2)^2 + (Y1-Y2)^2)

Now some people, who don't know this stuff are like "WtF..." Don't worry I'll explain it a bit, however, there will be a larger analysis in the section dedicated to this. Anyway, first to explain the X's and Y's. If anyone knows Coordinate geometry they should be saying, "Hey, I know that!" Anyway, X represents how far to the right you are of a particular point and Y represents how far up you are to a particular point. Now, because numbers go up to infinity that means that X and Y can go up to infinity as well! See, logic! Anyway, there lies a problem, infinity is a large number and it doesn't just go from infinity to 0. It goes from infinity to -infinity! Why -? Why is it negative when there are no negatives in the real world? Well, negative is there because we need to measure how far down we are, right? And if you are some amount down, then you can't be above 0 because its already stated that Y measures how far up you are. So if Y was negative where would you most likely be? Down! Now this was so simple that it was agonizing for me to write and it and surely agonizing for people to read it. So that is a bad explanation of the coordinate plane, but, because a lot of people know what it is, I will continue on.

Here is a picture:
CoordinatePlane.gif


So, back to distance, if X measures horizontally and Y measures Vertically then what about this X1 and X2 and Y1 and Y2? Well, in distance you can't measure it from one point! What's the distance from your home to nowhere? Thats a silly question isn't it? Therefore, when calculating distance we need to take into account a start and an end!

Now, after being segwayed quite a bit, time to explain this condition.
So if you were to make an "Is within Range" Condition it would be the same as a real comparison. Why? Because I say so! No, the reason why is because Real's have decimals, and integers don't. And, as we are square rooting, this often becomes a decimal. Therefore, we use reals. So far so good. Now we need to find the distance! Luckily, Warcraft 3 provides us with a built-in distance formula. Now your thinking, why did we need to learn the formula if it does it for us! Well, we will go into this at length later but before we do I'll give you a few reasons. For people who know Jass, this is evil because it wastes a function call. Needless to say, calling functions like that is bad. Now if you are a gui user, this may be all good. Your happy and having a good time, but say you want to make some sort of Impale spell. Well you know the target point and you know the point where you start from, so it should be easy to find the distance? Here is where the distance formula falls short? How do you break it down? It's a number right? BUT, this is very important :D, IT DOES NOT INDICATE DIRECTION!!!!!! So even if you know the distance you can't break it apart piece by piece. That is until you've read this :D Okay moving on!

Ok that was long. Back to the condition once again! So we know the distance function, and we know why it can be evil at times. Therefore it is recommended that you make out the distance formula. So to make this function you need a real comparison. You need to compare the distance formula using the values of whatever you want. For example, I kept saying "Is Within Range of", but that translates to "Point is within range of point" or "Unit is within range of Unit," after all, a unit is a point that has a unit on it is it not? So you plug in your formula with the necessary locations and stuff, now what? Well, you did the hardest part. After you have correctly written the Distance part, all thats left is the range part. So if the distance between these points is less then 200.00 then that means that the point has to be within 200 of the other point for the trigger to activate. There, finally created that condition. Now it's time to dissapoint you... This has already been created. Well, it wasn't meant to be used, just to be told. That's it for the basics, now it's time for some big boy stuff! We delve deeper into the distance formula!

III. Distance Formula

Now, finally some stuff that can be used and is pretty useful. But first, I want to go over a concept or two.

So, if you read the few paragraphs earlier you now know distance has a fatal flaw! It can't tell direction. This is important because if you cast firebolt or something and you want fire to trail it, you won't know which way its going or how to do it unless you used a special concept. Now, lets take a look at that concept. Ok, so if you were paying attention earlier, I explained that Warcraft 3 is based on a coordinate system. This system utilizes X's, Y's, and Z's. For now, you don't need to know Z. I might put something in about it but not now. Anyway, in this system you can specify any location based on X's and Y's. It's important that you realize that 1 point in X value is congruent to 1 point in Y value. Why is it important? Because then we can utilize an old friend of ours called Slope. Slope is the change in Y/Change in X.
This is important because it will help us in our quest to conquering the problem I presented earlier. The reason why slope is useful is because it CAN tell direction. Why? Because it can be negative. Other than that I want to explain why slope is important. From the formula given above, Slope is a fraction. You want to leave it that way in this case.

So say you want to use firebolt on a unit standing 50 "units" to the right of you, and 75 "units" above you.
______________________________________Target
_________________________________________|
_________________________________________|
_________________________________________|
_________________________________________|
it would look something like this. You-------------

Take a look at this image describing Distance:
Distance.gif


Now we can all agree that the slope is equal to 75/50 right?
But, because it's a fraction, we can reduce it to say... 3/2 right? (Dividing by 25) Now, about that trail of fire. Say we made two variables an x and a y, and set them to the casters position. Say we added 3 to the y and we added 2 to the x every some amount of seconds. We did this until these values are equal to the x and y values of the target. Would this work? Yes, (Theoretically, but locations are in reals, so use real instead of integers)
it would because if we add 3 to the y value 25 times, it would be 75, and if we add 2 to the x value 25 times (notice the number of times MUST BE EQUAL!) it will equal 50! Therefore reaching our target. And, look! We solved our problem!!! Not quite... To solve this problem so that it would work every time we need to make use of the distance formula.

Slope Example:
Slope.gif


So were back to the distance formula after reviewing the slope. Now, I will post some code, unfortunately in Jass, then pick it apart for you, because this is exactly how it works. That and it introduces a new concept which will be very useful (the map is also attached.)
JASS:
            set TargetLoc = GetUnitLoc(Target)
                set Temp = GetUnitLoc(Object)
                if IsUnitInRangeLoc(Object,GetUnitLoc(Target),10) then
<Exit out of the timer>
endif
                set DistanceX = (GetLocationX(Temp)-GetLocationX(TargetLoc))
                set DistanceY = (GetLocationY(Temp)-GetLocationY(TargetLoc))
                set RateX = DistanceX/AttackTime
                set RateY = DistanceY/AttackTime
                call RemoveLocation(Temp)
                set Temp = GetUnitLoc(Object)
                set PolarLoc = Location(GetLocationX(Temp)-RateX, GetLocationY(Temp)-RateY)
                call SetUnitPositionLoc(Object,PolarLoc)
                call RemoveLocation(PolarLoc)
                call RemoveLocation(Temp)

Simplification:
TargetLocation = Location of Target
Temp = Location of Object (Object is the "Effect" that I'm moving towards the target)
Next line is an if statement that basically means, if object is close to target (within 10 units), then end trigger.
Now to the distance part. This will be explained right now!

DistanceX = X of Temp - X of TargetLoc
DistanceY = Y of Temp - Y of TargetLoc

Now, this is important. First, your probably saying "WHY IS IT IN TWO DIFFERENT VARIABLES?!!?" and second "THAT ISN'T EVEN THE DISTANCE FORMULA!"

Well, in fact, it is. Let's go over it.
Watch:
DistanceX = SquareRoot((XofTemp-XofTarget)^2+(0-0)^2)
DistanceY = SquareRoot((YofTemp-YofTarget)^2+(0-0)^2)
Now, the reason why you don't have to write all the 0's and Squares and square roots out is because 0-0 =0. 0^2=0. 0 + anynumber = thatnumber.
Now lets look at the "thatnumber"
Xoftemp-Xoftarget = difference in x's. Differences in X's^2 = itself^2. The squareroot of that (because Squared and Squareroot are inverse functions), it equals Difference in X's, or, the distance between the x's. Now, it's the same for the y's.

So, why is this useful? Well, for one, we have the slope, and we can do that whole system, but I will show you a different method.

RateX = DistanceX/AttackTime
RateY = DistanceY/AttackTime
Now, what is RateX and RateY? The rate at which X and Y increase! But you may also recognize rate in this little formula, d=r*t which is Distance = Rate * Time. In our case, we have the distance, and we can specify the time! So if we wanted to find the rate, it would be Rate = Distance/Time. Looks very similar to what's above doesn't it? That's because it is :D So know we have a rate at which to increase the x and y's. What's next? We need to increase them don't we???

PolarLoc = X of Temp -RateX, Y of Temp-RateY
Interesting isn't it? Now to explain this. Why is it minus instead of +? Well, to understand why we need to take a look at our distance's.

DistanceX = X of Temp - X of TargetLoc
DistanceY = Y of Temp - Y of TargetLoc

So think of in terms of an if statement. So if the x of temp is greater than x of targetloc meaning farther right, then the distance will be positive.
If the distance is positive and we want to get the distance =0, meaning x values are equal, then we need to subtract from it don't we? Easy, isn't it? Same for the Y. Now if it was negative, your thinking, wait, then do we add? Nope, because the rate is derived from the distance, that means the rate has the same sign so if you subtract a negative, your really just adding it! And when you add to a negative, it gets closer to 0! There ya go!

Now, the reason why I used d = r*t is because I have multiple objects, and I wanted them to reach the same place at the same time! That is why it's useful. (Very important. The example posted above is simply an example, it does not work right. If you really wanted to use this formula, you need to have a constant rate and not a changing rate like this!)

Ok, now we will move onto Angles, but keep Distance in mind as we will come back to it later!

IV.Angles

Yay! Time for angles! But before you jump for joy, remember this, is more difficult and advanced then distance!

So we’re going to take it slow and start with how it’s used in Warcraft 3 once again. Let’s see. First, we have the facing angle of any unit (Very Useful!), then we have Polar Projections! Also very useful, and there’s a lot more but let’s start with these first.

Ok, what about facing angle? Very useful, returns a real, and it’s very useful when we use it with Slides, and other Abilities. Later, we will make an example of a missile that curves and attacks when it finds a target. Facing angle can also be set.

Yay! Polar Projection! Sounds evil! But its really not! Those of you who are familiar with Polar Coordinates should find this as a refresher for you! Say you have a unit and you want to make fire appear around him in a circular fashion. Ok, so you use the Polar Projection. It takes a location as a source, a distance, and an angle. Ok, now imagine a circle. The distance that the polar projection takes is the radius. An example of the angle is say, a clock. When the time is 1:00 the minute hand is on the 12:00. If we were to imagine the clock as a Circle, the minute hand is the same length as the radius of the circle, and then the angle of the minute hand is 90 because it is aiming straight up!

The only difference is exactly to the right is 0 and 360, directly up is 90, farthest to the left is 180, and down is 270. Quick quiz, where would the minute hand be on at 45 Degrees? In between 1 and 2! Now that we have angle down, let’s continue. Finally, the source is the center of the circle! So, to make the objects in a circle, you would make one at every multiple of 45! (Don’t need 360! 360 = 0)

Polar Projection:
PolarProj.gif


Now, GUI users may not care about this, but Jass users need to know… What is PolarProjection. Why do you need to know? So you avoid a needless function call of course! Now to learn about this you need to know trigonometry! That is Sin, Cos, and Tan. Soh Cah Toa. What is this useful phrase? Soh Cah Toa?

Soh = “Sin = Opposite/Hypotenuse” Cah= “Cos= Adjacent/Hypotenuse” and Toa= “Tan = Opposite/Adjacent”

We will be dealing with Sin and Cos for PolarProjection. Anyway, what do these terms refer to? (Opposite,Adjacent, and Hypotenuse?) These refer to triangles, triangles that have right angles!
They refer to the sides of a triangle, from the perspective of an angle!

Think of a right triangle, and think as if you are looking from one of the angles that is not 90 Degrees. From that perspective, Opposite refers to the side opposite the angle, Adjacent refers to the side next to it THAT ISN’T THE HYPOTENUSE! Why is this important? The hypotenuse exists in EVERY right triangle. It is the side opposite the right angle. Now, this is very simple trigonometry. Therefore, if you don’t know this, then Polar Co-ordinates will be very difficult to learn.

Here a pic on Trigonometry:
BasicTrig.gif


Now back to Polar Projection. Ok, I’m not going to explain the Sin and Cos curve, but let’s just say, for every point on the circle you can find the sides given an angle.

The formula (in normal math is something like this):
Point(Cos(Angle)*Radius,Sin(Angle)*Radius)

You see, the range of values that Cos and Sin return range from 1 to -1, therefore you need to multiply it by the radius to get the Polar Coordinate. That’s it! I wish, now to explain it to you in the warcraft way.
This is what happens when you use the PolarProjection Function
It takes, source, dist, and angle.
local real x = GetLocationX(source) + dist * Cos(angle * bj_DEGTORAD)
local real y = GetLocationY(source) + dist * Sin(angle * bj_DEGTORAD)
return Location(x, y)

And returns a location. However Cos and Sin are there own functions and they take there own values; Which is why it takes the angle you give it and multiplies it by bj_DEGTORAD, OR Degree to Radian. Now let’s just say Radians are another way to express Degrees. bj_DEGTORAD takes the degrees multiplies it by pi and divides it by 180! Wonderful isn’t it? Similarly to convert back, it multiplies by 180 and divides by pi.

That is Polar Projection! Let’s see, now how can we find the Angle Between Points? There’s already a function for that! It finds the angle between two 2 points. PROBLEM! Unfortunately, it’s a little more difficult if you want to use this angle. You see this function returns an angle from 0 – 180 or from 0 - -180. Meaning, if you wanted to detect if this unit is in front then it would be a bit weird… Which is why I made this snippet:

So it takes the angle: (Doesn’t do it for you because this also is a function call, and can be taken out.)
And returns an angle.
So lets say the angle in question is stored in variable Angle
So If Angle < 0 then
Angle = Angle + 360
// What this does is if the angle is less than 0 it will make it positive! That’s it!
There are a few more snags with this, but for the basics that’s it!
But before we continue, for Jass people we want to look into how to make this call one less of a function!
bj_RADTODEG * Atan2(GetLocationY(locB) - GetLocationY(locA), GetLocationX(locB) - GetLocationX(locA))

Confusing no? Well, it was to me. You see, this is similar to a polar co-ordinate except now, its called a Rectangular co-ordinate! Unfortunately, I am not confident in my knowledge of this co-ordinate so I won’t teach you about it.

Now, how can we convert from a simple X,Y coordinate BACK into a polar coordinate? Well, one you need to pick a center for it. Because you need another point to measure the angle from, just like in the beginning. Now how do we convert back? Well, one, we need the angle! So, for this, we will use the Angle Between Points function to get the angle. Now that we have the angle, simply add 360 to it if its negative and voila, we now have the angle. Now to get the radius we simply use the distance formula to calculate the distance between the two points. And their is you radius and angle!

Now let's jump back to the distance problem we were having earlier! If you think about it logically, doesn't polar projection do the same thing as slope in telling direction? And yes, your right! It does, just by simply calculating the distance and dividing it into smaller parts you can achieve the same effect!

For example: If you wanted to cast a spell that creates ten summoned units from the position of the caster, to the target point, all you would have to do is calculate the distance, divide it by ten. Then in a loop like this:
JASS:
local integer count = 1
loop
exitwhen count &gt; 10

Here is where the magic happens. You see count increases from 1 to 10. Using Polar Projection which takes the source, in this case, casting location, the angle from the casting location and target, and finally the distance. Now, lets take a look at this formula for distance.
distance * count
Seems simple? Well it is. It's also very effective. The distance increases specially till it eventually reaches the target point! This way it requires fewer variables and achieves a similar effect!
JASS:
set count = count+1
endloop


V. Combination- Distance + Angles
Ok, the example that I had earlier was pretty crappy because –
1) it was some scrap ability/code that I had never truly finished
2) It was not made for this tutorial
3) It did not stress the right points.

This new example hopefully covers these points better~

So, now I will make a new example (leave the old one there in a spoiler), and thoroughly explain the spell both in GUI and Jass. Okay, I know this is supposed to be a math tutorial, it obviously relates back to Warcraft 3, and thus, to triggers and spells. So, the requirements for this section is basically a little bit of experience with trigger enhanced spells. Anyway, here’s how it’s going to work

Spell Name: Ring of Flame
Screenshot:
RingofFlame.jpg

What it does: Allows for the casting of one flame pillar, which, then propagates into multiple pillars that work their way around the caster.

For the first run through of GUI it will not be MUI (Multi-unit instanceability), but for the jass run through it will be. And I will put a little section at the end on making the GUI ver MUI.

Okay, so basically here are the steps:
1) Unit casts spell.
2) Calculate the distance from the unit to casting point.
3) Wait X amount of seconds.
4) Use polar coordinates to summon 2 dummy units surrounding the first pillar of flame and casting 2 more pillars.
5) Continue until we get behind the caster.

K, so, first thing we want to do, for a simple version of this, is to make a dummy unit, and make a dummy ability of flame strike.

NOTE: This is a math tutorial after all, so, even though it is a spell, I will not go into depth about every other line that doesn’t involve math.

So now to explain what distance has to do with this. So, in this spell, we are assuming that the user is casting the spell (which is flame strike btw) and it is a point cast spell. So in order to determine the radius of the circle in which we shall cast the flame strikes around, we need to determine the distance between the caster and the casting point of spell.

Here’s another reason to switch to JASS o_O… Look at how many parentheses there are xD.
Code:
Set Distance = (Square root(((Power(((X of loc) - (X of loc2)), 2.00)) + (Power(((Y of loc) - (Y of loc2)), 2.00)))))

In my version of the spell, I removed my leaks (read a tutorial on cleaning leaks) by storing the points in a variable; which is why I have loc as target point of ability being cast, and loc2 as position of triggering unit. And there is the distance formula in Warcraft III GUI-speak. Very very ugly… lol Anyway, moving on away from distance, the next thing we need is the angle from the caster to casting point. Why? Because we want to have the ring of flame expand away from the starting point. In order to do this, we need to find the angle from center to first flame strike. So, in order to do this, we use polar coordinates to find the points that are next to the first one, and so on and so on. Here is the code that finds the angle:

Code:
 Set angle = (Angle from loc2 to loc)
Multiple FunctionsIf (All Conditions are True) then do (Then Actions) else do (Else Actions)
    If - Conditions
        angle Less than (<) 0.00
    Then - Actions
        Set angle = (angle + 360.00)
    Else - Actions

Once again were using loc2 and loc. However, the next part is simply because I want to obtain a positive angle, rather than a negative angle. (Although it does not matter if it’s negative or positive.) Anyway, here is where the magic of loops become really amazing:

Code:
 Do Multiple ActionsFor each (Integer A) from 1 to 5, do (Actions)
    Loop - Actions
        Wait 0.10 seconds
        Set loc = (Position of (Triggering unit))
        Set loc2 = (loc offset by Distance towards (angle + ((Real((Integer A))) x 30.00)) degrees)
        Unit - Create 1 dummy for (Owner of (Triggering unit)) at loc2 facing Default building facing (270.0) degrees
        Unit - Add a 3.00 second Generic expiration timer to (Last created unit)
        Unit - Add dummy pillar  to (Last created unit)
        Unit - Order (Last created unit) to Human Blood Mage - Flame Strike loc2
        Custom script:   call RemoveLocation(udg_loc2)
        Set loc2 = (loc offset by Distance towards (angle + ((Real((Integer A))) x -30.00)) degrees)
        Unit - Create 1 dummy for (Owner of (Triggering unit)) at loc2 facing Default building facing (270.0) degrees
        Unit - Add a 3.00 second Generic expiration timer to (Last created unit)
        Unit - Add dummy pillar  to (Last created unit)
        Unit - Order (Last created unit) to Human Blood Mage - Flame Strike loc2
        Custom script:   call RemoveLocation(udg_loc)
So, in this I have a loop that goes from 1 to 5. The angle increment that I am using (btw) is 30. 180/30 = 6, 6- 1 = 5. (the -1 at the end is for the one directly behind the unit. We don’t want to have two dummy units cast flame strike directly on top of each other.) Anyway, so this basically creates one unit integer A * 30 deg on the circle surrounding the unit. We cast twice because we are enclosing the unit in a circle from both sides. Making it, actually, very fun to watch. Anyway, here is the GUI code for the entire trigger:
Code:
 GUI
    Events
        Unit - A unit Starts the effect of an ability
    Conditions
        (Ability being cast) Equal to (==) Flame Strike
    Actions
        Set loc = (Target point of ability being cast)
        Set loc2 = (Position of (Triggering unit))
        Set Distance = (Square root(((Power(((X of loc) - (X of loc2)), 2.00)) + (Power(((Y of loc) - (Y of loc2)), 2.00)))))
        Set angle = (Angle from loc2 to loc)
        Custom script:   call RemoveLocation(udg_loc)
        Custom script:   call RemoveLocation(udg_loc2)
         Multiple FunctionsIf (All Conditions are True) then do (Then Actions) else do (Else Actions)
            If - Conditions
                angle Less than (<) 0.00
            Then - Actions
                Set angle = (angle + 360.00)
            Else - Actions
         Do Multiple ActionsFor each (Integer A) from 1 to 5, do (Actions)
            Loop - Actions
                Wait 0.10 seconds
                Set loc = (Position of (Triggering unit))
                Set loc2 = (loc offset by Distance towards (angle + ((Real((Integer A))) x 30.00)) degrees)
                Unit - Create 1 dummy for (Owner of (Triggering unit)) at loc2 facing Default building facing (270.0) degrees
                Unit - Add a 3.00 second Generic expiration timer to (Last created unit)
                Unit - Add dummy pillar  to (Last created unit)
                Unit - Order (Last created unit) to Human Blood Mage - Flame Strike loc2
                Custom script:   call RemoveLocation(udg_loc2)
                Set loc2 = (loc offset by Distance towards (angle + ((Real((Integer A))) x -30.00)) degrees)
                Unit - Create 1 dummy for (Owner of (Triggering unit)) at loc2 facing Default building facing (270.0) degrees
                Unit - Add a 3.00 second Generic expiration timer to (Last created unit)
                Unit - Add dummy pillar  to (Last created unit)
                Unit - Order (Last created unit) to Human Blood Mage - Flame Strike loc2
                Custom script:   call RemoveLocation(udg_loc)
        Wait 0.10 seconds
        Set loc = (Position of (Triggering unit))
        Set loc2 = (loc offset by Distance towards (angle + 180.00) degrees)
        Unit - Create 1 dummy for (Owner of (Triggering unit)) at loc2 facing Default building facing (270.0) degrees
        Unit - Add a 3.00 second Generic expiration timer to (Last created unit)
        Unit - Add dummy pillar  to (Last created unit)
        Unit - Order (Last created unit) to Human Blood Mage - Flame Strike loc2
        Custom script:   call RemoveLocation(udg_loc2)
        Custom script:   call RemoveLocation(udg_loc)
It’s a pretty short, simple, and to the point spell that basically makes use of the basics of trigger enhancing spells as well as distances and polar coordinates. Hope you enjoyed that example~

Here is the Jass ver for the above spell. Simply open the map named tutorial, disable the trigger called GUI, and add the trigger "JASS", convert it to custom script and copy and paste this code in there.
Why is this better than the above one? Well its not that the GUI ver COULDNT be as good, its that its easier to make the Jass ver better. Simply because of local variables which make MUI much easier to achieve. And that is exactly the difference, the jass trigger is INHERENTLY MUI :D It would be harder to make this NOT MUI then to make it MUI lol.
Here it is:

JASS:
function Trig_JASS_Conditions takes nothing returns boolean
    return( GetSpellAbilityId() == &#039;AHfs&#039; )
endfunction

function Trig_JASS_Actions takes nothing returns nothing
    local location loc = GetSpellTargetLoc()
    local unit u = GetTriggerUnit()
    local real D = SquareRoot(Pow(GetUnitX(u)-GetLocationX(loc),2)+Pow(GetUnitY(u)-GetLocationY(loc),2))
    local real Angle = bj_RADTODEG*Atan2(GetLocationY(loc)-GetUnitY(u),GetLocationX(loc)-GetUnitX(u))
    local integer i = 1
    local unit tmp
    loop
    exitwhen i&gt;5
        call TriggerSleepAction(.30)
        set tmp = CreateUnit(GetOwningPlayer(u),&#039;u000&#039;,GetUnitX(u)+D*Cos(bj_DEGTORAD*(Angle+30*i)),GetUnitY(u)+D*Sin(bj_DEGTORAD*(Angle+30*i)),0)
        call UnitAddAbility(tmp,&#039;A000&#039;)
        call IssuePointOrder(tmp,&quot;flamestrike&quot;,GetUnitX(u)+D*Cos(bj_DEGTORAD*(Angle+30*i)),GetUnitY(u)+D*Sin(bj_DEGTORAD*(Angle+30*i)))
        call UnitApplyTimedLife(tmp,&#039;BTFL&#039;,3)
        set tmp = CreateUnit(GetOwningPlayer(u),&#039;u000&#039;,GetUnitX(u)+D*Cos(bj_DEGTORAD*(Angle-30*i)),GetUnitY(u)+D*Sin(bj_DEGTORAD*(Angle-30*i)),0)
        call UnitAddAbility(tmp,&#039;A000&#039;)
        call IssuePointOrder(tmp,&quot;flamestrike&quot;,GetUnitX(u)+D*Cos(bj_DEGTORAD*(Angle-30*i)),GetUnitY(u)+D*Sin(bj_DEGTORAD*(Angle-30*i)))
        call UnitApplyTimedLife(tmp,&#039;BTFL&#039;,3)
        set i = i +1
    endloop
    call TriggerSleepAction(.30)
    set tmp = CreateUnit(GetOwningPlayer(u),&#039;u000&#039;,GetUnitX(u)+D*Cos(bj_DEGTORAD*(Angle+180)),GetUnitY(u)+D*Sin(bj_DEGTORAD*(Angle+180)),0)
    call UnitAddAbility(tmp,&#039;A000&#039;)
    call IssuePointOrder(tmp,&quot;flamestrike&quot;,GetUnitX(u)+D*Cos(bj_DEGTORAD*(Angle+180)),GetUnitY(u)+D*Sin(bj_DEGTORAD*(Angle+180)))
    call UnitApplyTimedLife(tmp,&#039;BTFL&#039;,3)
    call RemoveLocation(loc)
    set u = null
    set tmp = null
    set loc = null
endfunction

function InitTrig_JASS takes nothing returns nothing
    set gg_trg_JASS = CreateTrigger(  )
    call TriggerRegisterAnyUnitEventBJ( gg_trg_JASS, EVENT_PLAYER_UNIT_SPELL_EFFECT )
    call TriggerAddCondition( gg_trg_JASS, Condition( function Trig_JASS_Conditions ) )
    call TriggerAddAction( gg_trg_JASS, function Trig_JASS_Actions )
endfunction
(Older Example)
Now, an assignment… This assignment will recap a lot on what we learned and some new aspects to that I will introduce.

Ok, so, create a missile that moves (Slide) in the direction it faces. It will home on to an enemy that is nearby. It also CANNOT turn instantly. It must turn in a circular fashion.

Ok. So, in this case, we will make the missile a unit. Pick whatever you want, look up a tutorial for a dummy unit, and make it like that except for the model. Or you can model it after Locust! Make the model whatever you think looks like a missile. I chose Ability- Firebolt

Also, for the sake of time and space, it will not be MUI. Anyway, you need a few global variables. Let’s see. You need a unit for the missile, two integers for Velocity X and Velocity Y, and a Target unit.

Ok first, you need to make a find target trigger. This will manage both the tasks of finding the targets AND adjusting the speed. The event will be a periodic timer, and make sure you make this triggers timer slower than the move trigger! (Introduced Later :D) Anyway, so first we need to check if the current target is in range. Well, remember that Condition “Is unit within Range” feature I taught you earlier? Break it out! I won’t be mad if you use the built in feature of DistanceBetweenPoints. So find the distance between the position of the target and the position of the missile. Once that’s done check if the distance is greater than, say, 1000.

Quick note, in some cases, a target will not be picked and its position will be 0.00, 0.00. And if the missile was near the 0.00, 0.00 it would circle around there forever . So what you have to do is, if the X and Y is equal to 0.00 (Practically impossible) then set distance to something impossible so it would HAVE to pick a new target.


If it is then we will choose a new target! How do we do this? Let’s see. So we make a unit group and pick all enemy units within a range of the missile. Say 1500! Then we pick a random one! Now, we have a target!

Now we can adjust our velocities! Oh, wait… Never explained velocities to you did I? Well, the reason why I have an x velocity and a y velocity is so I can keep track of the speed of the missile. A missile cannot change direction instantly. It does not have such capabilities, which is why the velocities increase by ones or twos. Or decreases by ones or twos! Here is where the rest of the trigger comes in! So now we need another 4 if statements!

If X of Missile > X of Target then
Meaning the missile is farther to the right, we have to decrease the X Velocity!
So set Xvelocity = Xvelocity -1
Endif
Similarly for the other three!
If X of Missile < X of Target then
Meaning the missile is farther to the left, we have to increase the X Velocity!
So set Xvelocity = Xvelocity +1
Endif

If Y of Missile < Y of Target then
Meaning the missile is farther down, we have to increase the Y Velocity!
So set Yvelocity = Yvelocity +1
Endif
If Y of Missile > Y of Target then
Meaning the missile is farther up, we have to decrease the Y Velocity!
So set Yvelocity = Yvelocity -1
Endif

Here is the code I came up with. (Made it to be leakless and have other features such as a speed cap.)
Code:
 Find Target
    Events
        Time - Every 0.10 seconds of game time
    Conditions
    Actions
        Set Location = (Position of Missile)
        Set temp = (Position of Target)
        Set Distance = (Distance between Location and temp)
         Multiple FunctionsIf (All Conditions are True) then do (Then Actions) else do (Else Actions)
            If - Conditions
                 Multiple ConditionsAnd - All (Conditions) are true
                    Conditions
                        (X of temp) Equal to (==) 0.00
                        (Y of temp) Equal to (==) 0.00
            Then - Actions
                Set Distance = 100000.00
            Else - Actions
        Cinematic - Clear the screen of text messages for (All players)
        Game - Display to (All players) the text: ((String((X of temp))) + (, + (String((Y of temp)))))
        Game - Display to (All players) the text: (String(Distance))
         Multiple FunctionsIf (All Conditions are True) then do (Then Actions) else do (Else Actions)
            If - Conditions
                 Multiple ConditionsOr - Any (Conditions) are true
                    Conditions
                        Distance Greater than or equal to (>=) 700.00
                        TargetCount Greater than (>) 500
            Then - Actions
                Set TargetCount = 0
                Set UnitGroup = (Units within 1500.00 of Location matching (((Matching unit) belongs to an enemy of Player 1 (Red)) Equal to (==) True))
                Set Target = (Random unit from UnitGroup)
                Custom script:   call RemoveLocation(udg_temp)
                Custom script:   call DestroyGroup(udg_UnitGroup)
                Set temp = (Position of Target)
            Else - Actions
         Multiple FunctionsIf (All Conditions are True) then do (Then Actions) else do (Else Actions)
            If - Conditions
                (X of Location) Greater than (>) (X of temp)
            Then - Actions
                 Multiple FunctionsIf (All Conditions are True) then do (Then Actions) else do (Else Actions)
                    If - Conditions
                        Xvelocity Greater than (>) -20
                    Then - Actions
                        Set Xvelocity = (Xvelocity - 2)
                    Else - Actions
            Else - Actions
         Multiple FunctionsIf (All Conditions are True) then do (Then Actions) else do (Else Actions)
            If - Conditions
                (X of Location) Less than (<) (X of temp)
            Then - Actions
                 Multiple FunctionsIf (All Conditions are True) then do (Then Actions) else do (Else Actions)
                    If - Conditions
                        Xvelocity Less than (<) 20
                    Then - Actions
                        Set Xvelocity = (Xvelocity + 2)
                    Else - Actions
            Else - Actions
         Multiple FunctionsIf (All Conditions are True) then do (Then Actions) else do (Else Actions)
            If - Conditions
                (Y of Location) Less than (<) (Y of temp)
            Then - Actions
                 Multiple FunctionsIf (All Conditions are True) then do (Then Actions) else do (Else Actions)
                    If - Conditions
                        Yvelocity Less than (<) 20
                    Then - Actions
                        Set Yvelocity = (Yvelocity + 2)
                    Else - Actions
            Else - Actions
         Multiple FunctionsIf (All Conditions are True) then do (Then Actions) else do (Else Actions)
            If - Conditions
                (Y of Location) Greater than (>) (Y of temp)
            Then - Actions
                 Multiple FunctionsIf (All Conditions are True) then do (Then Actions) else do (Else Actions)
                    If - Conditions
                        Yvelocity Greater than (>) -20
                    Then - Actions
                        Set Yvelocity = (Yvelocity - 2)
                    Else - Actions
            Else - Actions
        Custom script:   call RemoveLocation(udg_temp)
        Custom script:   call RemoveLocation(udg_Location)
        Set TargetCount = (TargetCount + 1)
There is my code. Now moving onto the simple “Move” trigger

Now this trigger must have a periodic timer that is FASTER than the find target. If not the Homing part will make it home instantly and it will turn almost instantly.

Now, for this, all you have to do is move the missile. So move it to a point offset by the Xvelocity and the YVelocity. Then calculate the distance from the target and the missile. If it less than say 100, then remove target from game! What this means is if the missile hit a unit, it will remove the unit. You can, also, in this part remove the missile, but in my example, I had the missile continue on.

Here is my code
Code:
 Move
    Events
        Time - Every 0.05 seconds of game time
    Conditions
    Actions
        Set Location = (Position of Missile)
        Set temp = (Location offset by ((Real(Xvelocity)), (Real(Yvelocity))))
        Set Degrees = (Angle from Location to temp)
        Unit - Move Missile instantly to temp, facing Degrees degrees
        Custom script:   call RemoveLocation(udg_Location)
        Custom script:   call RemoveLocation(udg_temp)
        Set Location = (Position of Target)
        Set temp = (Position of Missile)
        Set Distance = (Distance between Location and temp)
         Multiple FunctionsIf (All Conditions are True) then do (Then Actions) else do (Else Actions)
            If - Conditions
                Distance Less than (<) 75.00
            Then - Actions
                Special Effect - Create a special effect at Location using Abilities\Spells\Human\MarkOfChaos\MarkOfChaosTarget.mdl
                Special Effect - Destroy (Last created special effect)
                Unit - Remove Target from the game
            Else - Actions
        Custom script:   call RemoveLocation(udg_Location)
        Custom script:   call RemoveLocation(udg_temp)

There, now onto the create function. So if you want this to be a spell, you should read up on triggering spells. There are a lot in the tutorial index. But, whatever the event, I will show you the actions. All you have to do is create the missile and set the variable Missile = last created unit. This will cause your other triggers to take effect and move the missile on its own. However, if you wanted to get really technical, what you could do is take the facing angle of the caster, and do something special with it. Instead of making a polar projection from it, you can make velocities equal to it using polar co-ordinates! This is your own task, and I will only give you a few hints on how to do this. One, X is computed using Cos, and Y is computed using Sin. The “radius of the circle” should be around 10. That is all I’ll tell you. It’s up to you to find out the rest.

Anyway here is my code for the create trigger. In my map however, this is different because I chose to create the missile from the center of the map, with no connection or anything. But here ya go.
Code:
 Create Missile
    Events
        Player - Player 1 (Red) types a chat message containing M as An exact match
    Conditions
    Actions
        Set Location = (Center of (Playable map area))
        Unit - Create 1 Missile for Player 1 (Red) at Location facing Default building facing (270.0) degrees
        Set Missile = (Last created unit)
        Set Yvelocity = -5
        Set TargetCount = 0
        Set UnitGroup = (Units within 1500.00 of Location matching (((Matching unit) belongs to an enemy of Player 1 (Red)) Equal to (==) True))
        Set Target = (Random unit from UnitGroup)
        Custom script:   call RemoveLocation(udg_temp)
        Custom script:   call DestroyGroup(udg_UnitGroup)
        Custom script:   call RemoveLocation(udg_Location)


VI. 3D Section

For lack of a better name, here it is! The 3D Section. Now, let's talk about this for a bit. What does it mean to be in 3D? Well, it means their are now 3 axes (plural of axis) instead of 2. Allow me to introduce to you, the "Z" Axis! So now, we have X, Y, and Z. I have mentioned this earlier, but I didn't expect you to remember. Anyway, let me warn you, that this is at least 3 times more difficult then Angles... And if you found that difficult, good luck... Now, let's talk about how Warcraft 3 uses the "Z" of a point. Unfortunately, I *currently* do not know how to set the Z of a point or location. Which is very annoying, but, thankfully, you can set the "Z" of a unit. How? Well, first the unit must meet one of two conditions. They must be a flyer, or they have the ability Crow Form. Now, if your unit has neither of these in the beginning of the game, fear not! Thankfully, you can simply add Crow Form to the unit, and magically, it can fly!

Now, to set the "Z" of a unit, for example, in Jass: call SetUnitFlyHeight(unit someUnit, real someZ, real Rate) and in GUI, under "Animation-" there is a Set Unit's fly height. Now, using either of these functions, you can set a unit's Z! Now that you can finally set a unit's Z, you now have access to a multitude of 3 dimensional functions. One, for example, the distance formula reborn!

Distance = SquareRoot((X1-X2)^2+(Y1-Y2)^2+(Z1-Z2)^2)

Now, let's make this into a Jass function!

JASS:
function 3DDistance takes real X1, real Y1 , real Z1, real X2, real Y2 , real Z2 returns nothing
local real X = X1-X2
local real Y = Y1-Y2
local real Z = Z1-Z2
return SquareRoot(Pow(X,2)+Pow(Y,2)+Pow(Z,2))
endfunction


What did we do? Simple, we took the differences of the X's, Y's, and Z's, Squared them, added them together, then squarerooted them!


And there is the distance! Like it? Me too. Now, this is important! For people using GUI, here's a bit of a let down! Unfortunately, when your moving your unit, if you use the move unit instantly function, it, unfortunately, causes a problem. What problem? Well, like I said, I currently don't know how to set a Location's Z. Therefore, when you set a unit's location, it set's the Z of the unit to 0! Problem? I think so! That's why Jass users are in luck. It is necessary to use the call SetUnitX and call SetUnitY functions! GUI: custom script call SetUnitX(unit u, real newX), same with Y.

Now, that we have distance down, lets talk about another special coordinate that I have just learned about, called Spherical Coordinate. Why is this amazing? It's like having a polar coordinate except combining it with a Z factor. Hence, sphere, a 3 dimensional circle. Now for an intro, in normal math, if it was a function, it takes 3 parameters. "rho", "phi" and "theta." Now, this may sound confusing, but you already know 2 of these parameters. "rho" is basically, the radius of the sphere. You also learned theta earlier in this tutorial. Theta is the same as the angle in Polar coordinates. Now what about phi? Let me explain this through examples. If phi is 0, then the point is Directly above the origin. If phi is 90 then it is on the same plane as the X and Y axis or Z = 0.00 and finally, phi is 180 then the point is directly BELOW the origin.

Take a look at this badly drawn picture:
SphericalCoord.gif


That is, roughly what I was talking about. Now, onto the actual formula. Hehe...
Note: Because "rho," "phi," and "theta" are annoying to type, I substitute it for Radius, ZDegrees and Degrees respectively.

X = Radius * Sin(ZDegrees) * Cos(Degrees)
Y = Radius * Sin(ZDegrees) * Sin(Degrees)
Z = Radius * Cos(ZDegrees)

There it is :D Pretty simple right? Well, lets convert it into Warcraft language and then set a unit's location to it.

JASS:
function SetUnitSphereProj takes real SourceX, real SourceY, real SourceZ, unit SomeUnit, real ZDegrees, real Degrees returns nothing
local real X = SourceX + Radius * Sin(ZDegrees*bj_DEGTORAD) * Cos(Degrees*bj_DEGTORAD)
local realset Y = SourceY + Radius * Sin(ZDegrees*bj_DEGTORAD) * Sin(Degrees*bj_DEGTORAD)
local real set Z = SourceZ + Radius * Cos(ZDegrees*bj_DEGTORAD)
call SetUnitX(SomeUnit,X)
call SetUnitY(SomeUnit,Y)
call SetUnitFlyHeight(SomeUnit,Z,0)
endfunction


That is basically it! (In this example I'm assuming the unit has crow form or can fly already. If it doesn't line for it is: "call UnitAddAbility(SomeUnit,'Amrf')" ). Now, you can find a spherical coordinate and the distance. Now, say you wanted to get the ZDegrees ?

Here is a way that works pretty well, I suggest testing it before you use it and see if it works how you like it!
+Rep to Trollvottel for this :D
JASS:
function AngleBetweenPointsPhi takes real x0, real y0, real z0, real x1, real y1, real z1 returns real
    local real x = x0 - x1
    local real y = y0 - y1
    local real z = z0 - z1
    return Acos( z, SquareRoot ( ( x * x ) + ( y * y ) ) ) * bj_RADTODEG
endfunction


That was all I know about 3D functions so far. If someone knows more, I'm all ears :D

I hope you learned a bit more about math and its uses! Have fun! Below are maps that I used in the process of making this tutorial.
 

Attachments

  • Missile.w3x
    19.7 KB · Views: 334
  • Tutorial.w3x
    18.1 KB · Views: 294

Flare

Stops copies me!
Reaction score
662
Ugh... too much text... few things I noticed though

Ok, let's see... Where to begin... Math is pretty much everything that is behind Warcraft. Every pixel from its color down to its location is determined by math. In fact, as many know, the computer can only understand 1's and 0's. The reason why it's a one and a zero is because it is either not receiving electricity or it is. Hence, only two states. One and Zero.

This helps people use math in WC3 how? Seems like a useless piece of info (in relation to the tutorial subject)

Watch:
DistanceX = SquareRoot((XofTemp-XofTarget)^2+(0+0)^2)
DistanceY = SquareRoot((YofTemp-YofTarget)^2+(0+0)^2)
Now, the reason why you don't have to write all the 0's and Squares and square roots out is because 0+0 =0. 0^2=0. 0 + anynumber = thatnumber.
Now lets look at the "thatnumber"
Xoftemp-Xoftarget = difference in x's. Differences in X's^2 = itself^2. The squareroot of that (because Squared and Squareroot are inverse functions), it equals Difference in X's, or, the distance between the x's. Now, it's the same for the y's.

Hmmm, I can't see how that works. Distance formula gets the square root of the sum of the squares of X and Y, whereas, from the looks of it, you are getting the sum of the square root of x squared, and the square root of y squared i.e.
Code:
The formula itself
SquareRoot (xDifference*xDifference + yDifference*yDifference)

What you're doing (from my understanding)
(SquareRoot (xDifference*xDifference)) + (SquareRoot (yDifference*yDifference))

And, if we take some values for those (something simple, let's say 10, 10)

SquareRoot (10*10 + 10*10)
SquareRoot (100 + 100)
SquareRoot (200)
~14

VS

(SquareRoot (10*10)) + (SquareRoot (10*10))
SquareRoot (100) + SquareRoot (100)
10 + 10
20

Two totally different values

PolarProjection Method
Which is why I made this method:

They are functions, not methods :) Methods are used in relation to structs
 
Reaction score
86
1st Problem: Your right :D It is pretty useless, but I like general intros :D I'll remove it considering this is so lengthy o_O...

2nd: Just showing that the difference of the X's and the Y's is equal to the distance between them respectively, by simply plugging in 0's you can see the formula still holds true. Edit: Fixed it, it was not 0+0, it should have been 0-0. (0 is just the substitute for the y-value.)

Edit Once again: Oh, I see what your saying. But the aim of it was not to find the distance between the points. It was used in conjunction with slope, which is (Change in Y)/(Change in X) or similarly the distance between the Y values over distance between the X values.

3rd: Indeed, your right :D Have to fix that. (Was in struct mode for a bit there :D)

Thanks for the constructive criticism, and yes, I know, it's a lot of text :( I will fix it and add pictures where a picture is due, IF people even find this tutorial useful >,<
 

Flare

Stops copies me!
Reaction score
662
by simply plugging in 0's you can see the formula still holds true

It should be true for every other value you use. If you think about it in relation to science, you can prove your hypothesis to be correct under a number of circumstances, but if it fails under any legitimate circumstances, it disproves everything.

And, to be honest, you should probably just stick to getting the distance between the points, not going over and up/down. There isn't really much point in getting X distance and Y distance individually. You can easily work with the straight-line distance once it's calculated (and, to be honest, it's not hard to calculate it once you know the formula). And, it saves time and space (since you need one variable for total distance, as opposed to one for X distance, and one for Y)
 
Reaction score
86
I see what your saying but if you were to use distance, then find the polar projection based on the angle that it needs to take to get to the target it would get even more confusing... By calculating the distance for both X and Y, then creating a rate, you can avoid using polar co-ordinates or polar projection in general. But, considering that the section was centered around distance, I didn't want to introduce angles, or polar co-ordinates, at least until the angle section.

HOWEVER, yes you are correct, if you know what your doing, there is no need to calculate distance separately for X and Y. Which is why this tutorial focuses on the basics :D
 
Reaction score
86
Yep :D, I'll add that method in, completely left it out xD... Thanks for the reply, give me a couple minutes and I'll put it in

+rep for the suggestion :D Err... When I can XD.
 

Ninja_sheep

Heavy is credit to team!
Reaction score
64
I still wait for a wc3 tutorial that is about 3d math.
That would be hell of difficult.
For example a circle of firebolts that twists and turns in the air randomly would be really awesome. (like a circle that goes around the shape of a ball).
hmmm, mb I should do some expiriments with this :O

oh, and about the tutorial... humm...
It was pretty nice, but the thing that misses: pictures
if you do anything with gemoetry you need pictures or it's really difficult to understand.

I didn't read the part about world edit cause it doesn't intrest me too much :p
 
Reaction score
86
Thanks for the suggestions: And, I will start working on the pictures as soon as possible!

Also, about the 3D spatial environment. It basically deals with vectors, along with normals, frames, and a lot more. I'm taking a summer course that deals with this, but we've only had 3 classes so far. Haven't learned a lot. I will try and add something that deals with 3 dimensional aspects, but can't yet, don't know enough...
 
Reaction score
86
Added Images :D, if anyone needs any more pictures to clarify things tell me. I didn't make too many, short on time... But if someone needs a picture to clarify something please say something :D
 

Ninja_sheep

Heavy is credit to team!
Reaction score
64
Thanks for the suggestions: And, I will start working on the pictures as soon as possible!

Also, about the 3D spatial environment. It basically deals with vectors, along with normals, frames, and a lot more. I'm taking a summer course that deals with this, but we've only had 3 classes so far. Haven't learned a lot. I will try and add something that deals with 3 dimensional aspects, but can't yet, don't know enough...

Doh, I noticed 3D distances are very annoying in Wc3. You always have to use pythagoras for distance calculation :/

Oh well, gj at the pictures. Forgot to give +rep at my last post, so here it is.
 

Trollvottel

never aging title
Reaction score
262
the problem i see with the turorial:

everybody knows it, you learn it in school.

but nice written and explained.
 
Reaction score
86
Yes, indeed you do learn a lot of this in school. But their are people who start threads about simple stuff like this every day. Instead of explaining it every time, I thought it would be easier just to put into a tutorial :D

Thanks for the comments :D
 

Trollvottel

never aging title
Reaction score
262
should work, test it.
And i think its a bit confusing that you marked in the 3D-picture the lines with Theta and Phi and not the angles themselves
 
General chit-chat
Help Users
  • No one is chatting at the moment.

      The Helper Discord

      Staff online

      Members online

      Affiliates

      Hive Workshop NUON Dome World Editor Tutorials

      Network Sponsors

      Apex Steel Pipe - Buys and sells Steel Pipe.
      Top