• 🏆 Texturing Contest #33 is OPEN! Contestants must re-texture a SD unit model found in-game (Warcraft 3 Classic), recreating the unit into a peaceful NPC version. 🔗Click here to enter!
  • It's time for the first HD Modeling Contest of 2024. Join the theme discussion for Hive's HD Modeling Contest #6! Click here to post your idea!

comparing facing and angle[math help]

Status
Not open for further replies.
Level 10
Joined
Sep 21, 2007
Messages
517
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",

JASS:
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,

JASS:
facing = Deg2Rad(GetUnitFacing(udg_PlayerHero[unitId]))

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

JASS:
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

JASS:
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.

thanks for your time
 
Level 11
Joined
Feb 22, 2006
Messages
752
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.
 
Level 10
Joined
Sep 21, 2007
Messages
517
thanks man. il do it ^^ +rep ofc :p
EDIT: i messed up, meh... sry not so good at math, can u tell me what i did wrong? thanks again
JASS:
        local unit t = GetTriggerUnit()
        local integer unitId = GetPlayerId(GetTriggerPlayer())+1
        local real Cooldown = udg_PLAYERGUN_RELOADTIME[unitId]
        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:
Level 10
Joined
Sep 21, 2007
Messages
517
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 :D
btw im sure they both work accordingly, since i damge the triggered unit and do various things to the unit in the variable.
 
Level 11
Joined
Feb 22, 2006
Messages
752
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:

JASS:
    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
        // ...
 
Level 4
Joined
May 17, 2005
Messages
94
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:

JASS:
    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
        // ...
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:
Acos((A dot B) / A^2)

It should be:

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

So, in other words:

JASS:
    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
 
Level 10
Joined
Sep 21, 2007
Messages
517
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? :p
here is code: I declared the global threshold
JASS:
private real threshold = .45 //in radians
Here are the actions
JASS:
       local unit t = GetTriggerUnit()
        local integer unitId = GetPlayerId(GetTriggerPlayer())+1
        local real Cooldown = udg_PLAYERGUN_RELOADTIME[unitId]
        local real Delay = udg_PLAYERGUN_SHOOTDELAY[unitId]
        local real Dist
        //
        local real facing = GetUnitFacing(udg_PlayerHero[unitId])*bj_DEGTORAD
        local real dx = GetUnitX(t)-GetUnitX(udg_PlayerHero[unitId])
        local real dy = GetUnitY(t)-GetUnitY(udg_PlayerHero[unitId])
        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)))  
        //local real threshold = Atan2(dy,dx)
        //
        call DisplayTimedTextToPlayer(GetTriggerPlayer(),0,0,.45, R2S(angleBetween))
        if (angleBetween < threshold and angleBetween > -threshold) then
            return
        endif
 
Last edited:
Status
Not open for further replies.
Top