Вычисление, если угол находится между двумя углами

Итак, я делаю небольшую игру, где я проверяю, может ли персонаж "видеть" другого, где символ 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