Як я можу обчислити кут та правильний напрямок повороту між двома двовимірними векторами?


26

Я працюю над деяким AI руху, де немає перешкод і рух обмежений площиною XY. Я обчислюю два вектори, v , напрямок повернення судна 1 і ш , вектор, що вказує з положення корабля 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. Таким чином ви будете використовувати лише одну триггерну функцію, але все одно матимете фактичний кут. Однак я не рекомендую проводити цю оптимізацію, поки ви не переконаєтесь, що відстеження кутів AI помітно впливає на продуктивність вашої гри.
deft_code

2
Так, при перетворенні між векторами і кутами atan2 (), безумовно, є вашим другом.
bluescrn

1
Спасибі! Я виявив, що мені насправді не потрібен кут, захоплення 2D-перехресного продукту досить просте для моїх потреб.
Помилка 454

2
Крім того, ваш if( cross == -0.0f )проти if( cross == 0.0f )перевірки виглядає дуже крихким.
bobobobo

1
@bobobobo, з двигуном фізики це може бути не просто вибором напрямку та рухом. Чарівне поворот може призвести до того, що фізичні двигуни зникають. Подальше магічне обертання також виглядає жахливим для анімації. Так, так, ви можете вирішити це без поняття ліворуч або праворуч, але відполіроване рішення часто потребує цього.
deft_code

10

Використовуйте арцин двовимірного поперечного продукту (тобто z компонента перехресного вектора продукту). Це дасть вам від -90 до 90, що дозволить вам знати, чи їхати вліво або вправо.

Будьте обережні, тому що хрест B - це не те саме, що B хрест А.

Інша стратегія (але, мабуть, не така прямо вперед) - обчислити "заголовок" двох векторів, використовуючи atan2, а потім з'ясувати, чи потрібно вказівкою на X градусів вліво або вправо, щоб перейти до B, вказуючи на y градуси.


Дякую за відповідь. Щоб бути зрозумілим для майбутніх браузерів, прийняття зворотного синуса величини 2d поперечного продукту дасть значення між 0 і 90. Прийняття синуса z-складової 2-х поперечного продукту дає бажані результати.
Помилка 454

@Error 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;

абсолютне значення x коефіцієнта = 2;

абсолютне значення коефіцієнта y = 3;

кут = атан (2/3); [кут буде від 0 до 90]

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

якщо (x коефіцієнт <0 і y коефіцієнт> 0), то кут = 180-кут;

якщо (x коефіцієнт <0 і y коефіцієнт <0), тоді кут = 180 + кут;

якщо (x коефіцієнт> 0 і y коефіцієнт <0), тоді кут = 360-кут;

якщо (х коефіцієнт> 0 і коефіцієнт у> 0), тоді кут = кут;

знайшовши кут першого та другого векторів, просто відніміть перший кут вектора від другого вектора. Тоді ви отримаєте абсолютний кут між двома векторами.


5
Саме це реалізує функція atan2 (). :)
Натан Рід

@NathanReed, так, але хіба ви не могли використовувати цей метод з крапковим продуктом, щоб уникнути накладних витрат?
jdk1.0

0

Можливо, я повинен поставити дещо інше питання і відповісти на нього сам; це найближче питання, яке я міг би знайти до того, що у мене було.

Я роблю 2D роботу в HTML-полотні, де у мене кути повороту в радіанах, а не у векторах. Мені знадобився "кут повороту" (ta), щоб дістатися з "поточного заголовка" (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.