# comparing facing and angle[math help]

Discussion in 'Triggers & Scripts' started by Diehard@Azeroth, Oct 5, 2009.

1. ### Diehard@Azeroth

Joined:
Sep 21, 2007
Messages:
483
Resources:
1
Spells:
1
Resources:
1
ok, im doing a game where, if shooting is initiated, the units face is close to the angle between the shooting units position and the being shooted's position. Here is what i did for the comparison:

i had a variable as a "degree boundary",

Code (vJASS):
degBoundary = .65

lets say (im doing this in radians, but can do it in degrees if you want me to)
so i put the units facing in a variable and converted it to Radians,

Code (vJASS):

and then got the angle in a variable and left it as is (since its already radians). Now here is the comparison part:

Code (vJASS):
if facing > angle+degBoundary or facing < angle-degBoundary then

facing being the facing, angle being the angle between the units, from the shooting unit to the unit being shooted: i used

Code (vJASS):
Atan2(y2-y,x2-x)

. Sorry had to use some JASS here just incase you didnt know, its fairly basic though and can be understood through logic.

2. ### Saia_Djinn

Joined:
Feb 15, 2009
Messages:
458
Resources:
0
Resources:
0
and what exactly was the trouble

3. ### aznricepuff

Joined:
Feb 22, 2006
Messages:
749
Resources:
4
Maps:
2
Spells:
1
Tutorials:
1
Resources:
4
Yea, you didn't exactly tell us what your problem is. But I'm guessing it has something to do with this:
`if facing > angle+degBoundary or facing < angle-degBoundary then`
If you're trying to see if the facing is outside some range, then this bugs because of how angles are represented. GetUnitFacing() returns something between 0 and 2pi. Atan2 returns something between -pi and pi. So if the unit is facing 270 degrees (south), i.e. 3pi/2 and the shooting angle is the SAME angle (270 degrees or 3pi/2), Atan2 will actually give you -pi/2, not 3pi/2. You do your check:

`if facing > angle+degBoundary or facing < angle-degBoundary then`

facing will be 3pi/2. angle will be -pi/2. Obviously 3pi/2 is way more than -pi/2 + 0.65, so your check thinks the unit isn't facing the right way even when it is.

This angle thing is always annoying. There's multiple ways to solve it. One is to use some function to convert all angles to between some value (i.e. always between 0 and 2pi or always between -pi and pi) before working with them. Then all you need to worry about is the wraparound (2pi to 0 or -pi to pi). Alternatively (and this is the method i prefer), you can use vectors to compare angles. Remember that for any two vectors u and v:

cos (theta) = (u dot v) / (|u||v|)

where theta is the angle between the two vectors. This method never fails. The only caveat is that you have to take the absolute value of theta before doing checks:

`if (RAbsBJ(theta) < threshold) then`

or if you want to avoid the BJ call:

`if (theta < threshold and theta > -threshold) then`

You don't have to worry about anything else when using the vector method.

4. ### Diehard@Azeroth

Joined:
Sep 21, 2007
Messages:
483
Resources:
1
Spells:
1
Resources:
1
thanks man. il do it ^^ +rep ofc
EDIT: i messed up, meh... sry not so good at math, can u tell me what i did wrong? thanks again
Code (vJASS):
local unit t = GetTriggerUnit()
local integer unitId = GetPlayerId(GetTriggerPlayer())+1
local real Delay = udg_PLAYERGUN_SHOOTDELAY[unitId]
local real Dist
local real facing = Cos( Deg2Rad(GetUnitFacing(udg_PlayerHero[unitId])) )
local real threshold = Cos( Atan2(GetUnitY(t)-GetUnitY(udg_PlayerHero[unitId]),GetUnitX(t)-GetUnitX(udg_PlayerHero[unitId]))+degBoundary )
call DisplayTimedTextToPlayer(GetTriggerPlayer(),0,0,.45, R2S(threshold))
call DisplayTimedTextToPlayer(GetTriggerPlayer(),0,0,.45, R2S(facing))
if (facing < threshold and facing > -threshold) then
return
endif

Last edited: Oct 6, 2009
5. ### YourNameHere

Joined:
Apr 29, 2007
Messages:
745
Resources:
4
Maps:
1
Spells:
2
JASS:
1
Resources:
4
Actually GetUnitFacing() returns a angle in degrees, but I assume you mean that it's between 0 and 2pi when its converted into radians.

6. ### aznricepuff

Joined:
Feb 22, 2006
Messages:
749
Resources:
4
Maps:
2
Spells:
1
Tutorials:
1
Resources:
4
What exactly is your event? You're using GetTriggerUnit() and GetTriggerPlayer() in the same actions, and since I can't think of an event that has both a triggering unit and a triggering player, I'm thinking one of those won't work.

7. ### Diehard@Azeroth

Joined:
Sep 21, 2007
Messages:
483
Resources:
1
Spells:
1
Resources:
1
A unit is selected by player, unitId = player id of player + 1, and the units are already preset in the variables,. Thanks for responding pretty dam fast man
btw im sure they both work accordingly, since i damge the triggered unit and do various things to the unit in the variable.

8. ### aznricepuff

Joined:
Feb 22, 2006
Messages:
749
Resources:
4
Maps:
2
Spells:
1
Tutorials:
1
Resources:
4
Ah, unit selection event...forgot about tht one cuz I've never used tht before...ever.

Anyway, the way you get vectors is: for the vector between selected unit and the hero, just use the dx and dy of their locations, and for the vector representing the facing angle, just create a unit vector with direction of the facing, where dx = Cos(facing) and dy = Sin(facing). So:

Code (vJASS):

local unit u = GetTriggerUnit()
local integer id = GetPlayerId(GetTriggerPlayer()) + 1
local real facing = GetUnitFacing(udg_PlayerHero[i]) * bj_DEGTORAD
local real dx = GetUnitX(u) - GetUnitX(udg_PlayerHero[id])
local real dy = GetUnitY(u) - GetUnitY(udg_PlayerHero[id])
local real angleBetween = Acos((dx * Cos(facing) + dy * Sin(facing)) / (dx * dx + dy * dy))
// the line above computes dot product of the two vectors, divides it by the product of their magnitudes, and then takes the arccosine to get the angle between the two vectors

if (angleBetween < threshold and angleBetween > -threshold) then
// ...

9. ### uberfoop

Joined:
May 17, 2005
Messages:
85
Resources:
1
Maps:
1
Resources:
1
That's...not how you use scalar product to find the angle difference. Particularily since, as you said, you're supposed to divide by the product of their magnitudes; right now you're just dividing by the square of the magnitude of A.

Right now, you're doing:
Code (Text):

Acos((A dot B) / A^2)

It should be:

Code (Text):

ACos((A dot B) / (lAl*lBl)

So, in other words:

Code (vJASS):

local real facing = GetUnitFacing(u2) * bj_DEGTORAD
local real dx = GetUnitX(u1) - GetUnitX(u2)
local real dy = GetUnitY(u1) - GetUnitY(u2)
local real bx = Cos(facing)
local real by = Sin(facing)
local real angleBetween = Acos((dx * bx + dy * by) / (SquareRoot(dx * dx + dy * dy) * SquareRoot(bx * bx + by * by)))
if (angleBetween < threshold and angleBetween > -threshold) then

10. ### aznricepuff

Joined:
Feb 22, 2006
Messages:
749
Resources:
4
Maps:
2
Spells:
1
Tutorials:
1
Resources:
4
O right. I forgot to square root the magnitude. But I left out the magnitude of the other vector for good reason. It's a unit vector, therefore magnitude = 1.

Joined:
May 9, 2006
Messages:
3,271
Resources:
18
Tools:
1
Maps:
5
Spells:
3
Tutorials:
2
JASS:
7
Resources:
18

12. ### Diehard@Azeroth

Joined:
Sep 21, 2007
Messages:
483
Resources:
1
Spells:
1
Resources:
1
Thanks for all your resposnes guys!, il do the changes right away!
EDIT~~~

ok im sorry for not telling you my problem, so you prob assumed the wrong problem, basically a unit is going to shoot a unit, therefore, the units facing angle and angle between them should not differ much, thats why i compared with facing of unit > Angle+degBoundary or facing of unit < angle-degBoundary so that if his facing goes outside a range of the angle itself, then the shooting will not be correct. I think that what you guys gave me does the complete opposite tho,
...or did i do another sad mistake?
here is code: I declared the global threshold
Code (vJASS):
private real threshold = .45 //in radians

Here are the actions
Code (vJASS):
local unit t = GetTriggerUnit()
local integer unitId = GetPlayerId(GetTriggerPlayer())+1
local real Delay = udg_PLAYERGUN_SHOOTDELAY[unitId]
local real Dist
//