Как я могу рассчитать угол и правильное направление поворота между двумя 2D векторами?


26

Я работаю над искусственным интеллектом, в котором нет препятствий и движение ограничено плоскостью XY. Я рассчитываю два вектора, v , направление движения корабля 1 и w , вектор, указывающий от положения корабля 1 к кораблю 2.

Затем я рассчитываю угол между этими двумя векторами, используя формулу

arccos((v · w) / (|v| · |w|))

У меня проблема в том, что arccosвозвращает значения только между 0 ° и 180 °. Это делает невозможным определить, должен ли я повернуть налево или направо, чтобы повернуться лицом к другому кораблю.

Есть лучший способ сделать это?


1
Если вы используете Unity, проверьте Mathf.DeltaAngle().
Рассел Борогове

Ответы:


22

Гораздо быстрее использовать 2d кросс-продукт. Никаких дорогостоящих функций триггера.

b2Vec2 target( ... );
b2Vec2 heading( ... );

float cross = b2Cross( target, heading );

if( cross == -0.0f )
   // turn around

if( cross == 0.0f )
  // already traveling the right direction

if( cross < 0.0f)
  // turn left

if( cross > 0.0f)
  // turn right

Если вам все еще нужны реальные углы, я рекомендую использовать atan2. atan2даст вам абсолютный угол любого вектора. Чтобы получить относительный угол между любыми векторами, рассчитайте их абсолютные углы и используйте простое вычитание.

b2Vec2 A(...);
b2Vec2 B(...);

float angle_A = std::atan2(A.y,A.x);
float angle_B = B.GetAngle(); // Box2D already figured this out for you.

float angle_from_A_to_B = angle_B-angle_A;
float angle_from_B_to_A = angle_A-angle_B;

1
Прочитав ответ @ Tetrad, я полагаю, вы могли бы объединить перекрестное произведение и an arccos. Таким образом, вы будете использовать только одну функцию триггера, но фактический угол все равно будет. Однако я рекомендую не использовать эту оптимизацию, пока вы не убедитесь, что отслеживание угла искусственного интеллекта заметно влияет на производительность вашей игры.
deft_code

2
Да, при преобразовании векторов в углы atan2 () определенно будет вашим другом.
Bluescrn

1
Благодарность! Я обнаружил, что мне на самом деле не нужен угол, захват перекрестного 2D-продукта достаточно прост для моих нужд.
Ошибка 454

2
Кроме того, ваш if( cross == -0.0f )против if( cross == 0.0f )проверки выглядит очень хрупким.
Бобобобо

1
@bobobobo, с физическим движком может быть не просто выбрать направление и двигаться. Магическое вращение может привести к срыву физических двигателей. Дальнейшее магическое вращение выглядит ужасно и для анимации. Так что да, вы можете решить это без понятия левого или правого, но часто это нужно для отлаженного решения.
deft_code

10

Используйте арксинус двумерного перекрестного произведения (то есть компонент z вектора перекрестного произведения). Это даст вам от -90 до 90, что даст вам знать, идти ли налево или направо.

Будьте осторожны, потому что крест A не совпадает с крестом B

Другая стратегия (но, вероятно, не такая прямолинейная) состоит в том, чтобы вычислить «курс» двух векторов с использованием atan2, а затем выяснить, нужно ли A, указывающий на X градусов, идти влево или вправо, чтобы перейти к B, указывающему на y градусов.


Спасибо за ответ. Для будущих браузеров будет понятно, что если взять синус от величины 2-мерного перекрестного произведения, то получим значения от 0 до 90. Из-за синуса z-компонента 2-го перекрестного произведения получим желаемые результаты.
Ошибка 454

@ Ошибка 454, вы абсолютно правы, исправили мой пост.
Тетрад

1

Используйте векторы, чтобы перенаправить корабль. Вот как работает «поведение руля» - вам никогда не нужно вычислять угол, просто используйте имеющиеся у вас векторы. Это в вычислительном отношении намного дешевле.

Вектор w(вектор от корабля 1 до корабля 2) - это вся необходимая вам информация. Измените либо вектор скорости корабля 1, либо вектор ускорения корабля 1 (или даже вектор курса), используя взвешенную версию w.

введите описание изображения здесь

Величина , как далеко от корабля 1 выключен , конечно (как плохо v не совпадает с ш) можно найти с помощью ( 1 - dot(v,w))

  • ( dot(v,w)максимально, когда vи wвыстроиться точно)
  • ( 1 - dot(v,w)) дает 0, когда vи wполностью выстроены, предоставлены vи wнормализованы)

0

Существует простой способ найти абсолютный угол вектора через нормальную геометрию.

например, вектор V = 2i - 3j;

абсолютное значение коэффициента х = 2;

абсолютное значение коэффициента y = 3;

угол = атан (2/3); [угол будет в диапазоне от 0 до 90]

На основании квадранта угол будет изменен.

если (коэффициент x <0 и коэффициент y> 0), то угол = угол 180;

если (х коэффициент <0 и у коэффициент <0), то угол = 180 + угол;

если (коэффициент x> 0 и коэффициент y <0), то угол = угол 360;

если (коэффициент х> 0 и коэффициент у> 0), то угол = угол;

после нахождения угла первого и второго векторов просто вычтите угол первого вектора из второго вектора. Тогда вы получите абсолютный угол между двумя векторами.


5
Это именно то, что функция atan2 () реализует для вас. :)
Натан Рид

@NathanReed, да, но не могли бы вы использовать этот метод с точечным продуктом, чтобы избежать накладных расходов?
jdk1.0

0

Может быть, я должен опубликовать немного другой вопрос и ответить на него сам; это самый близкий вопрос, который я мог найти к тому, который у меня был.

Я делаю 2D работу на холсте html, где у меня есть углы поворота в радианах, а не в векторах. Мне нужен был «угол поворота» (та), чтобы перейти от «текущего курса» (h) к «целевому курсу» (t).

Вот мое решение:

var ta = t - h,
    ata = Math.abs(ta)
;
if (ta > Math.PI) ta = (ta / ata) * (Math.PI - ata)
return ta;
Используя наш сайт, вы подтверждаете, что прочитали и поняли нашу Политику в отношении файлов cookie и Политику конфиденциальности.
Licensed under cc by-sa 3.0 with attribution required.