Checking for angle using no angles

This tutorial helper you?

  • Yes

    Votes: 1 14.3%
  • I still have some questions.

    Votes: 2 28.6%
  • No

    Votes: 4 57.1%

  • Total voters
    7

mapguy

New Member
Reaction score
46
This tutorial is about dodging the WC3 bug when we want to detect an interval between 2 angles. If you already know how to do this don't read this tutorial.

This tutorial teaches a way you can use to check angles without using angles, only distances.
The reason for this is because WC3 has problems with it's angles, the angle between point goes from -180 to +180, which means you have a lot of problems when you want to detect if damage comes from behind, if units are inside a cone, if a unit is looking to the other, etc.


the problem:
If you have a simple spell that you want to get unit in a cone, which means:
Angle between (caster) and (enemy) is a value between ((angle between (caster) and (target point of ability beeing cast)) + or - 10).

if the angle between (caster) and (target point of ability beeing cast) is 175, the angle between (caster) and (target) must be between +185 and +165. But there is no +185 angle on warcraft!.
Usually you will create a very complicated trigger to dodge this problems, this tutorial will show an easier way to do this using mathematical logic.

I need any mathematical knoledge to do this?
No, you don't. I will only show and explain how the method works, and then, you just have to putthe values inside a formula.


TUTORIAL

First of all, I must say that I'm going to use a backstab detection as example.

Imagine this spell:
Backstab (passive)
Everytime you hit an enemy from behind you deal 25 bonus damage.
Note: angle between target's back and damage source must be +or- 30 degrees.

THIS IS THE FORMULA! YOU CAN JUST USE IT IF YOU UNDERSTAND, IF NOT, READ THE REST OF THE TUTORIAL
this is the formula, you just have to change the value 30 to the limit angle you would like to use.
Code:
set A = position of (damage source)
set B = position of (triggering unit)
set C = B offset by (distance between A and B) and (fancing of (triggering unit)) degrees.
If (Cos(30/2)) < ((Distance between C and A) / ((Distance between C and B)x2) then do your action

No need for mathematical Knoledge, just use the formula above!


Code:
set A = position of (damage source)
set B = position of (triggering unit)
set C = B offset by (distance between A and B) and (fancing of (triggering unit)) degrees.
set cb = (distance between C and B) x 2
if cb = 0 then (set cb = 0.01)
If (Cos(30/2)) < ((Distance between C and A) / (cb) then do your actions
this is to prevent divisions by zero, but, I think it is almost impossible to have a distance between target and sorce equal to zero...so, use it just if you think your map could lead to a value like 0, if not, just use the first formula)

you can download the demo map in the bottom of this post and just Copy and Paste the trigger inside your map


---------------------------------------IF YOU DON'T UNDERSTOOD THE FORMULA READ THIS----------------
1 - First of all you have to use a damage detection system (but this is not a tutorial about damage systems, so, I'll not explain how to use them or create them).

2 - when a unit takes damage and this is a damage that results from an attack you have to pick a lot of points:
A = the position of the damage source.
B = the position of the target.
-ab- = the distance between A and B.
C = B offset by -ab- and (facing of target)
after you pick all the 3 points listed above you will have a mathematical circle with:
B as its center.
A as a point in the border of the circle.
C as another point in the border.
-ab- as the radius of the circle.

Using mathematical logic I can say that the the distance between B and A is equal to the radius, also, the distance between B and C is equal to the radius and equal to -ab-.

TO detect the angles we have to transform out circle into a rectangle triangle, for this, we just have to pick 2 distance values:
Hip = 2x(Distance between C and B)
Cat = Distance between A and C.
Then we detect a Cosine:
Cosine = Cat / Hip
The only thing we will use now is this Cosine.
Do you remember that the angle between the source and the back of the target must be +or- 30 degrees?
So, just do this:
set Limit_Cosine = Cosine of (30/2) degrees
check if Cosine > Limit_Cosine

--------------end----------------------

This tutorial helped you?
Is this tutorial too abstract?
Is it to complicated?
Did you find any bug when using this?
Do you want help with other types of spells like cone detection, see if unit is looking to another, etc?
I'll check this tutorial for comments, and I can update it if someone is having troubles to use this mathematical content.

I Hope at least one person find help here.:thup:

Here is the demo map, it works PERFECTLY!
 

Attachments

  • Backstab demo.w3x
    17.2 KB · Views: 262

Moridin

Snow Leopard
Reaction score
144
There are loads of grammatical and spelling errors in this. Either proof-read it yourself or get someone to do that for you.

I personally didn't really understand this.

1) Why are Hip and Cat called "Hip" and "Cat" ? :S

2) I think the cone you've given here is 45 degrees, how would I go about changing that? How would I detect a cone behind a unit?

3) I really didn't get how you used Cosine and Limit Cosine.

4) I've done a backstab mechanic before. Wouldn't it be easier just to check the absolute difference between facing angles of the two units?
 

13lade619

is now a game developer :)
Reaction score
399
it's been done before.. you should've just asked.

IsUnitBehind
JASS:
function IsUnitBehindUnit takes unit u2, unit u1, real def returns boolean
    local real face = GetUnitFacing(u1)
    local real rangle = bj_RADTODEG*Atan2(GetUnitY(u2)-GetUnitY(u1),GetUnitX(u2)-GetUnitX(u1))
    set def = def / 2
    return not (RAbsBJ(face-rangle) &lt; def or RAbsBJ(face-rangle-360) &lt; def)
endfunction


Units in Sector
http://www.thehelper.net/forums/showthread.php?t=103445&highlight=Units+sector

Plus.

your methods are complicated and cumbersome.
you dont really need 3 points and you dont even need the Cosine function.

you only need some rather simple arithmetic on angles.
 

Nherwyziant

Be better than you were yesterday :D
Reaction score
96
I made a test and it doesn't work, maybe I just misunderstood. Can you make a test map or somethin

Anyway, spelling errors.
 

mapguy

New Member
Reaction score
46
There are loads of grammatical and spelling errors in this. Either proof-read it yourself or get someone to do that for you.

I personally didn't really understand this.

1) Why are Hip and Cat called "Hip" and "Cat" ? :S
Hip = Hipotenuse
Cat = Catet

2) I think the cone you've given here is 45 degrees, how would I go about changing that? How would I detect a cone behind a unit?
The cone pick units in an angle of +or- 15degrees from C and +or- 30 degrees from B.

3) I really didn't get how you used Cosine and Limit Cosine.

4) I've done a backstab mechanic before. Wouldn't it be easier just to check the absolute difference between facing angles of the two units?
THe WC3 as I said has bugged angles. it goes from -180 to +180, which means you find some bugs when you use absolute angles.
because 170 +or- 20 = 150 and 190 (there is no angle 190 on WC3, the equivalent angle is -170)

I made a test and it doesn't work, maybe I just misunderstood. Can you make a test map or somethin

Anyway, spelling errors.
maybe you misunderstood, I'll post a demo map.
the spelling errors is because my english is not native, I am still lerning it.

it's been done before.. you should've just asked.

IsUnitBehind
Jass:

function IsUnitBehindUnit takes unit u2, unit u1, real def returns boolean
local real face = GetUnitFacing(u1)
local real rangle = bj_RADTODEG*Atan2(GetUnitY(u2)-GetUnitY(u1),GetUnitX(u2)-GetUnitX(u1))
set def = def / 2
return not (RAbsBJ(face-rangle) < def or RAbsBJ(face-rangle-360) < def)
endfunction


Units in Sector
http://www.thehelper.net/forums/show...t=Units+sector

Plus.

your methods are complicated and cumbersome.
you dont really need 3 points and you dont even need the Cosine function.

you only need some rather simple arithmetic on angles.
are you sure this works for angles that are very close to +180 and -180?
this code can be translate do GUI? I dont understand JASS.
 

Moridin

Snow Leopard
Reaction score
144
Here's my backstab trigger. The actual condition you need for it is much smaller because of all the actions meant for my map.

Trigger:
  • Ninja Backstab
    • Events
      • Unit - A unit Is attacked
    • Conditions
      • ((Triggering unit) is A structure) Equal to False
    • Actions
      • If (All Conditions are True) then do (Then Actions) else do (Else Actions)
        • If - Conditions
          • (Unit-type of (Attacking unit)) Equal to Ninja
        • Then - Actions
          • Set Unit_A_Angle = (Facing of (Triggering unit))
          • Set Unit_B_Angle = (Facing of (Attacking unit))
          • If (All Conditions are True) then do (Then Actions) else do (Else Actions)
            • If - Conditions
              • Or - Any (Conditions) are true
                • Conditions
                  • (Abs(((Integer(Unit_A_Angle)) - (Integer(Unit_B_Angle))))) Less than or equal to 45
                  • (Abs(((Integer(Unit_A_Angle)) - (Integer(Unit_B_Angle))))) Greater than or equal to 315
            • Then - Actions
              • Game - Display to (Player group((Owner of (Attacking unit)))) the text: Backstab! Ninajs do...
              • Floating Text - Create floating text that reads Backstab! above (Triggering unit) with Z offset 0.00, using font size 10.00, color (100.00%, 100.00%, 100.00%), and 0.00% transparency
              • Floating Text - Change (Last created floating text): Disable permanence
              • Floating Text - Change the lifespan of (Last created floating text) to 1.00 seconds
              • If (All Conditions are True) then do (Then Actions) else do (Else Actions)
                • If - Conditions
                  • ((Triggering unit) is alive) Equal to True
                • Then - Actions
                  • Unit - Cause (Attacking unit) to damage (Triggering unit), dealing 500.00 damage of attack type Spells and damage type Normal
                • Else - Actions
            • Else - Actions
        • Else - Actions
 

roXplosive

New Member
Reaction score
15
I've taken a look at aceheart's solution and i agree it's elegant because we all know the tangent function is monotonous and growing he used this property to get the units in a cone by making it verify the inequality

tan(minangle)<=tan(unit_angle)<=tan(maxangle)

he neglected division by zero and eliminated the fractions obtaining (this removes the domain limitation of tan function from -pi/2 to pi/2 domain) :

sin(minangle)*cos(unitangle)<=sin(unitangle)*cos(minangle) and similar for the max angle . Also cos(unitangle) and sin(unitangle) are divided by the distance between the unit and the caster which is constant and thus eliminated from calculations . He notes sin(minangle)=y1 and cos(minangle)=x1 ,y=yunit-ycaster and x=xunit-xcaster and it all becomes :

y1*x<=y*x1 and the other inequality . Quite nice and fast to compute since there are no divisions and trigonometry involved .
 

mapguy

New Member
Reaction score
46
Here's my backstab trigger. The actual condition you need for it is much smaller because of all the actions meant for my map.

Trigger:
  • Ninja Backstab
    • Events
      • Unit - A unit Is attacked
    • Conditions
      • ((Triggering unit) is A structure) Equal to False
    • Actions
      • If (All Conditions are True) then do (Then Actions) else do (Else Actions)
        • If - Conditions
          • (Unit-type of (Attacking unit)) Equal to Ninja
        • Then - Actions
          • Set Unit_A_Angle = (Facing of (Triggering unit))
          • Set Unit_B_Angle = (Facing of (Attacking unit))
          • If (All Conditions are True) then do (Then Actions) else do (Else Actions)
            • If - Conditions
              • Or - Any (Conditions) are true
                • Conditions
                  • (Abs(((Integer(Unit_A_Angle)) - (Integer(Unit_B_Angle))))) Less than or equal to 45
                  • (Abs(((Integer(Unit_A_Angle)) - (Integer(Unit_B_Angle))))) Greater than or equal to 315
            • Then - Actions
              • Game - Display to (Player group((Owner of (Attacking unit)))) the text: Backstab! Ninajs do...
              • Floating Text - Create floating text that reads Backstab! above (Triggering unit) with Z offset 0.00, using font size 10.00, color (100.00%, 100.00%, 100.00%), and 0.00% transparency
              • Floating Text - Change (Last created floating text): Disable permanence
              • Floating Text - Change the lifespan of (Last created floating text) to 1.00 seconds
              • If (All Conditions are True) then do (Then Actions) else do (Else Actions)
                • If - Conditions
                  • ((Triggering unit) is alive) Equal to True
                • Then - Actions
                  • Unit - Cause (Attacking unit) to damage (Triggering unit), dealing 500.00 damage of attack type Spells and damage type Normal
                • Else - Actions
            • Else - Actions
        • Else - Actions

I am pretty sure it dont works when one unit is facing like -175 and the other +170
 

Moridin

Snow Leopard
Reaction score
144
I am pretty sure it dont works when one unit is facing like -175 and the other +170

170 - (- 175) = 170 + 175 = 345

(Abs(((Integer(Unit_A_Angle)) - (Integer(Unit_B_Angle))))) Greater than or equal to 315

Or:

-175 - 170 = -345 -> Absolute = 345. Implies the same result.

It works.

----------------

Also for a cone you can use this:
Angle A = Angle from (Position of (Triggering unit)) to (Position of (Target point of ability being cast))
Angle B = Angle from (Position of (Triggering unit)) to (Position of (<The unit you are checking to see if it's in the cone or not>))

Again,

If
Abs(Angle A - Angle B) < 45
OR
Abs(Angle A - Angle B) > 315

Then the unit is inside a cone of 90 degrees.

Edit: You should know that facing angle is from -180 to 180 while angle from point A to point B is from 0 to 360. Also, the function absolute simply ignores any "-" minus sine.
 

roXplosive

New Member
Reaction score
15
I don't say this won't work . It's pretty obvious but i think the solution using angles isn't as good as using differences and multiplication as Aceheart suggested and leads to excessive calculation . I expect the trigonometric functions and their inverses to have some tabeled values and those are used for calculation but getting the angle requires using arctangent (most common) function . This returns values from -90 to 90 degrees and you have to check yourself if the y_unit-y_caster<0 and if so add 180 degrees to the angle to get the real one . I'm saying that computation time wise there are better alternatives than using engine's angle functions .
 

Moridin

Snow Leopard
Reaction score
144
No doubt Acehart's method is more efficient than mine. I only use angles because I understand what's going on :p lol. Unfortunately, even though I have learned trig for the past 4 years, I still don't really understand it in a practical sense.
 

mapguy

New Member
Reaction score
46
Moridin:

your method has a problem.

sometimes a unit deals damage before turning, which means you hit the target on the back but it DONT CONT!
also, if the target is in your back and you turn on to hit him, your first strike count as backstab, even if it is not.

(you created the same bug as rikimaru in dota)

my method uses the facing angle of the target and the angle between 2 points (target - agressor), which returns better results and never bugs.
 

Moridin

Snow Leopard
Reaction score
144
sometimes a unit deals damage before turning, which means you hit the target on the back but it DONT CONT!
I'm pretty sure this is a bug in itself. A unit isn't supposed to be able to hit something without turning around to face it unless it has no facing angle in the first place (towers).

also, if the target is in your back and you turn on to hit him, your first strike count as backstab, even if it is not.
I didn't get what you mean by this.
 

mapguy

New Member
Reaction score
46
I'm pretty sure this is a bug in itself. A unit isn't supposed to be able to hit something without turning around to face it unless it has no facing angle in the first place (towers).


I didn't get what you mean by this.

play dota...the bug is there.
sometimes a unit deals the damage before turning if it's attack animation is too fast or if it`s turn speed is low.

check my demo map and check the part of the tutorial, I placed a global formula that can be used almost everytime and never bugs.
 
General chit-chat
Help Users
  • No one is chatting at the moment.

      The Helper Discord

      Members online

      No members online now.

      Affiliates

      Hive Workshop NUON Dome World Editor Tutorials

      Network Sponsors

      Apex Steel Pipe - Buys and sells Steel Pipe.
      Top