27

所以我正在制作一个小游戏,我正在检查一个角色是否可以“看到”另一个角色,如果 A 在 B 的一定距离内,角色 A 可以看到角色 B,并且 A 度数的方向是 +/- 45 度B所面对的角度。

目前,我在检查是否

(facingAngle - 45) =< angleOfTarget =< (facingAngle + 45)

除了当我们越过 360 度线时,这工作正常。

比方说facingAngle = 359, angleOfTarget = 5。在这种情况下,目标仅偏离中心 6 度,所以我希望我的函数返回 true。不幸的是,5 不在 314 和 404 之间。

4

6 回答 6

35

你试一试

anglediff = (facingAngle - angleOfTarget + 180 + 360) % 360 - 180

if (anglediff <= 45 && anglediff>=-45) ....

原因是角度的差异facingAngle - angleOfTarget虽然是由于包裹效应,但可能会偏离 360 度。

加 180+360 然后模 360 然后减去 180,实际上只是将所有内容转换为 -180 到 180 度的范围(通过加或减 360 度)。

然后您可以轻松检查角度差,是否在 -45 到 45 度之间。

于 2012-09-02T08:58:49.260 回答
13

这是我在网上找到的一个简单的功能,并进行了修改。它适用于任何角度(可以在 0-360 之外)。(这个函数在 c 中工作,在 Xcode 中工作。)

请记住,它检查从角度 A 到角度 B 的逆时针方向。如果角度介于两者之间,则返回YES (true):)

一、简单的转换函数,让所有角度都为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 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;
 }

在此处输入图像描述

例如。检查角度 300 是否在 180 到 10 度之间:

BOOL isBetween=angle_is_between_angles( 300, 180,10);

//返回是

于 2015-04-18T18:38:18.433 回答
9

有一个避免环绕问题的三角函数解决方案。

我假设你有 (x, y) 两个字符的坐标P1P2. 您已经指定您知道您可能使用毕达哥拉斯定理计算的两者之间的距离。

您可以使用两个向量的点积来计算它们之间的角度:

A . B = |A| . |B| . cos(theta).

如果您将A其作为facingAngle向量,它将是[cos(fA), sin(fA)],并且大小|A|为 1。

如果您B将两个字符之间的向量作为向量,并且您在上方的距离得到:

cos(theta) = (cos(fA) * (P2x - P1x) + sin(fA) * (P2y - P1y)) / |B|

|B|您已经计算出的距离在哪里。

您不需要实际取反余弦来查找theta,因为对于 -45 到 +45 的范围,您只需要检查cos(theta) >= 0.70710678(ie 1 / sqrt(2))。

这可能看起来有点复杂,但很有可能你已经在程序中找到了所有必需的变量。

于 2012-09-02T09:14:07.147 回答
1

处理低端包装的一个简单解决方案(成负值)就是将 360 添加到所有值:

(facingAngle + 315) =< (angleOfTarget + 360) =< (facingAngle + 405)

这样,45 的减法永远不会变成负数,因为它不再发生。

要在顶端处理换行,您需要再次检查,将另一个360 添加到该angleOfTarget值:

canSee  = (facingAngle + 315 <= angleOfTarget + 360) &&
          (angleOfTarget + 360 <= facingAngle + 405);
canSee |= (facingAngle + 315 <= angleOfTarget + 720) &&
          (angleOfTarget + 720 <= facingAngle + 405);
于 2012-09-02T08:55:41.900 回答
0

另一种使用始终最小正差并与阈值进行比较的方法:

anglediff = Math.min(Math.abs(facingAngle - angleOfTarget), 360 - Math.abs(angleOfTarget - allowDirection));
if (anglediff <= 45)
于 2015-10-09T10:14:33.283 回答
0

以不同的方式重述 Alnitak 的答案,避免 360 度角包绕的解决方案是在角度总是很小的不同坐标系中重述问题。这是代码:

def inside_angle(facing, target):
    dot = cos(facing)*cos(target) + sin(facing)*sin(target)
    angle = acos(dot)

    return angle <= pi/4

这是使用矢量投影完成的。假设向量 |face> = [cos(facing) sin(facing)] 和 |target> = [cos(target) sin(target)],当将目标投影到面对向量时,角度将从零开始,当目标正好在面向向量处或将增加到任一侧。这样我们就可以将其与 pi/4(45 度)进行比较。角度公式如下:

cos(angle) = <facing|target> / <target|target> <facing|facing>

也就是说,角度的余弦是向量|face>和|target>划分它们的模块之间的点积,在这种情况下为1,它变为:

angle = acos(<facing|target>)

参考: https ://en.wikipedia.org/wiki/Vector_projection

于 2016-07-21T23:33:53.930 回答