问题
So I am making a little game where I am checking if a character can "see" another where character A can see character B if A is within a certain distance of B, and the direction in degrees of A is +/- 45 degrees of the angle B is facing.
Currently, I do a little calculation where I'm checking if
(facingAngle - 45) =< angleOfTarget =< (facingAngle + 45)
This works fine except for when we cross the 360 degree line.
Let's say facingAngle = 359, angleOfTarget = 5
. In this situation, the target is only 6 degrees off center, so I want my function to return true. Unfortunately, 5 is not between 314 and 404.
回答1:
Just try
anglediff = (facingAngle - angleOfTarget + 180 + 360) % 360 - 180
if (anglediff <= 45 && anglediff>=-45) ....
The reason is that the difference in angles is facingAngle - angleOfTarget
although due to wrapping effects, might be off by 360 degrees.
The add 180+360 then modulo 360 then subtract 180, effectively just converts everything to the range -180 to 180 degrees (by adding or subtracting 360 degrees).
Then you can check the angle difference easily, whether it is within -45 to 45 degrees.
回答2:
There is a trigonometric solution that avoids the wrapping problem.
I'm assuming that you have (x, y) coordinates for both characters P1
and P2
. You've already specified that you know the distance between the two which you presumably calculated using Pythagoras' theorem.
You can use the dot product of two vectors to calculate the angle between them:
A . B = |A| . |B| . cos(theta).
If you take A
as the facingAngle
vector it will be [cos(fA), sin(fA)]
, and will have magnitude |A|
of 1.
If you take B
as the vector between the two characters, and your distance above you get:
cos(theta) = (cos(fA) * (P2x - P1x) + sin(fA) * (P2y - P1y)) / |B|
where |B|
is the distance you've already calculated.
You don't need to actually take the inverse cosine to find theta
, since for range of -45 to +45 you just need to check for cos(theta) >= 0.70710678
(i.e. 1 / sqrt(2)
).
This might seem slightly complicated, but the chances are that you've already got all of the required variables hanging around in your program anyway.
回答3:
Here is a simple function I found online, and modified. It works correctly for any angles (can be outside of 0-360). (This function is made to work in c, works in Xcode.)
Remember, it checks COUNTER-CLOCKWISE from angle A to angle B. It returns YES (true) if the angle is between :)
First, a simple conversion function to make all angles 1-360
//function to convert angle to 1-360 degrees
static inline double angle_1to360(double angle){
angle=((int)angle % 360) + (angle-trunc(angle)); //converts angle to range -360 + 360
if(angle>0.0)
return angle;
else
return angle + 360.0;
}
Check if angle is between :)
//check if angle is between angles
static inline BOOL angle_is_between_angles(float N,float a,float b) {
N = angle_1to360(N); //normalize angles to be 1-360 degrees
a = angle_1to360(a);
b = angle_1to360(b);
if (a < b)
return a <= N && N <= b;
return a <= N || N <= b;
}

Eg. To check if the angle 300 is between 180 and 10 degrees:
BOOL isBetween=angle_is_between_angles( 300, 180,10);
//RETURNS YES
回答4:
A simple solution to handle wrapping at the low end (into negative values), is just to add 360 to all your values:
(facingAngle + 315) =< (angleOfTarget + 360) =< (facingAngle + 405)
That way, the subtraction of 45 can never go negative, because it no longer happens.
To handle wrapping at the top end, you need to check again, adding another 360 to the angleOfTarget
value:
canSee = (facingAngle + 315 <= angleOfTarget + 360) &&
(angleOfTarget + 360 <= facingAngle + 405);
canSee |= (facingAngle + 315 <= angleOfTarget + 720) &&
(angleOfTarget + 720 <= facingAngle + 405);
回答5:
Another way using always minimum positive difference and comparing with threshold:
anglediff = Math.min(Math.abs(facingAngle - angleOfTarget), 360 - Math.abs(angleOfTarget - allowDirection));
if (anglediff <= 45)
回答6:
Restating Alnitak's answer in a different way, a solution that avoids the angle wrap at 360 degrees is to restate the problem in a different coordinate system where the angles are always small. Here is the code:
def inside_angle(facing, target):
dot = cos(facing)*cos(target) + sin(facing)*sin(target)
angle = acos(dot)
return angle <= pi/4
This is done using vector projection. Assuming the vectors |facing> = [cos(facing) sin(facing)] and |target> = [cos(target) sin(target)], when projecting the target into the facing vector, the angle will range from zero, when the target is exactly at the facing vector or will increase to either side. This way we can just compare it to pi/4 (45 degrees). The formula for the angle is the following:
cos(angle) = <facing|target> / <target|target> <facing|facing>
That is, the cosine of the angle is the dot product between the vectors |facing> and |target> divided their modules, which is 1 in this case, which becomes:
angle = acos(<facing|target>)
Reference: https://en.wikipedia.org/wiki/Vector_projection
来源:https://stackoverflow.com/questions/12234574/calculating-if-an-angle-is-between-two-angles