Вычисление, если угол находится между двумя углами
Итак, я делаю небольшую игру, где я проверяю, может ли персонаж "видеть" другого, где символ A может видеть символ B, если A находится на определенном расстоянии от B, а направление в градусах A равно +/- 45 градусов угла B обращены.
В настоящее время я делаю небольшой расчет, где я проверяю, если
(facingAngle - 45) =< angleOfTarget =< (facingAngle + 45)
Это прекрасно работает, за исключением случаев, когда мы пересекаем линию на 360 градусов.
Скажем facingAngle = 359, angleOfTarget = 5
. В этой ситуации цель находится всего на 6 градусов от центра, поэтому я хочу, чтобы моя функция вернула значение true. К сожалению, 5 не находится между 314 и 404.
Ответы
Ответ 1
Просто попробуйте
anglediff = (facingAngle - angleOfTarget + 180 + 360) % 360 - 180
if (anglediff <= 45 && anglediff>=-45) ....
Причина в том, что разница в углах facingAngle - angleOfTarget
, хотя из-за эффектов обертывания может быть отключена на 360 градусов.
Затем добавьте 180 + 360, затем по модулю 360, затем вычитайте 180, эффективно просто преобразует все в диапазон от -180 до 180 градусов (путем добавления или вычитания на 360 градусов).
Затем вы можете легко проверить разницу в углах, независимо от того, находится ли она в пределах от -45 до 45 градусов.
Ответ 2
Существует тригонометрическое решение, которое позволяет избежать проблемы с упаковкой.
Я предполагаю, что у вас есть (x, y) координаты для обоих символов P1
и P2
. Вы уже указали, что знаете расстояние между ними, которое вы предположительно вычисляете с помощью теоремы Пифагора.
Вы можете использовать произведение точек двух векторов для вычисления угла между ними:
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
(т.е. 1 / sqrt(2)
).
Это может показаться немного сложным, но есть вероятность, что у вас уже есть все необходимые переменные, зависающие в вашей программе.
Ответ 3
Вот простая функция, которую я нашел в Интернете и изменил. Он корректно работает для любых углов (может быть вне 0-360). (Эта функция предназначена для работы в c, работает в Xcode.)
Помните, что он проверяет COUNTER-CLOCKWISE от угла 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;
}
![enter image description here]()
Eg. Чтобы проверить, находится ли угол 300 между 180 и 10 градусами:
BOOL isBetween=angle_is_between_angles( 300, 180,10);
//RETURNS YES
Ответ 4
Простое решение для обработки обтекания на нижнем конце (в отрицательные значения) просто добавляет 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);
Ответ 5
Другой способ использования всегда минимальной положительной разницы и сравнения с порогом:
anglediff = Math.min(Math.abs(facingAngle - angleOfTarget), 360 - Math.abs(angleOfTarget - allowDirection));
if (anglediff <= 45)
Ответ 6
Повторяя ответ Alnitak по-другому, решение, которое позволяет избежать обертывания углов на 360 градусов, заключается в том, чтобы повторить проблему в другой системе координат, где углы всегда малы. Вот код:
def inside_angle(facing, target):
dot = cos(facing)*cos(target) + sin(facing)*sin(target)
angle = acos(dot)
return angle <= pi/4
Это делается с использованием векторной проекции. Предполагая, что векторы | сталкиваются >= [cos (обрамление) sin (обрамление)] и | target >= [cos (target) sin (target)], при проецировании мишени в вектор-ориентир угол будет варьироваться от нуля, когда цель находится точно в обратном векторе или будет увеличиваться с обеих сторон. Таким образом, мы можем просто сравнить его с pi/4 (45 градусов). Формула для угла такова:
cos(angle) = <facing|target> / <target|target> <facing|facing>
То есть косинус угла - это точечный продукт между векторами | сталкиваются и | мишт > , делят их модули, что в этом случае равно 1, что становится:
angle = acos(<facing|target>)
Ссылка:
https://en.wikipedia.org/wiki/Vector_projection