¿Cómo puedo calcular el ángulo y la dirección de giro adecuada entre dos vectores 2D?


26

Estoy trabajando en algún movimiento AI donde no hay obstáculos y el movimiento está restringido al plano XY. Estoy calculando dos vectores, v , la dirección de orientación del barco 1 yw , el vector que apunta desde la posición del barco 1 al barco 2.

Luego estoy calculando el ángulo entre estos dos vectores usando la fórmula

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

El problema que tengo es que arccossolo devuelve valores entre 0 ° y 180 °. Esto hace que sea imposible determinar si debo girar a la izquierda o derecha para enfrentar la otra nave.

¿Hay una mejor manera de hacer esto?


1
Si estás usando Unity, échale un vistazo Mathf.DeltaAngle().
Russell Borogove

Respuestas:


22

Es mucho más rápido usar un producto cruzado 2D. No hay una función trigonométrica costosa involucrada.

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

Si aún necesita los ángulos reales, le recomiendo usar atan2. atan2le dará el ángulo absoluto de cualquier vector. Para obtener el ángulo relativo entre cualquiera de los vectores, calcule sus ángulos absolutos y use una resta simple.

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
Después de leer la respuesta de @ Tetrad, supongo que podría combinar un producto cruzado y un arccos. De esta manera solo usará una función trigonométrica, pero aún tendrá el ángulo real. Sin embargo, recomiendo esta optimización hasta que esté seguro de que el seguimiento del ángulo AI está teniendo un impacto notable en el rendimiento de su juego.
deft_code el

2
Sí, al convertir entre vectores y ángulos, atan2 () es definitivamente tu amigo.
bluescrn

1
¡Gracias! Descubrí que en realidad no necesito el ángulo, agarrar el producto cruzado 2D es lo suficientemente simple para mis necesidades.
Error 454

2
También su cheque if( cross == -0.0f )vs se if( cross == 0.0f )ve extremadamente frágil.
bobobobo

1
@bobobobo, con un motor de física puede que no sea una opción simplemente elegir una dirección y moverse. El giro mágico puede hacer que los motores físicos se asusten. Además, la rotación mágica también es terrible para la animación. Entonces, sí, puede resolver esto sin una noción de izquierda o derecha, pero una solución pulida a menudo los necesita.
deft_code el

10

Utilice arcsin del producto cruzado 2D (es decir, el componente z del vector del producto cruzado). Eso le dará -90 a 90, lo que le permitirá saber si debe ir hacia la izquierda o hacia la derecha.

Tenga cuidado porque A cross B no es lo mismo que B cross A.

Otra estrategia (pero probablemente no tan directa) es calcular el "rumbo" de los dos vectores usando atan2 y luego averiguar si A apuntando a X grados necesita ir a la izquierda o derecha para ir a B apuntando a y grados.


Gracias por la respuesta. Para que quede claro para futuros navegadores, tomar el seno inverso de la magnitud del producto cruzado 2d arrojaría valores entre 0 y 90. Tomar el seno del componente z del producto cruzado 2d produce los resultados deseados.
Error 454

@Error 454, tienes toda la razón, arregló mi publicación.
Tetrad

1

Usa vectores para redirigir la nave. Así es como funcionan los "comportamientos de dirección": nunca necesita calcular el ángulo, solo use los vectores que tiene. Esto es computacionalmente mucho más barato.

El vector w(vector del Barco 1 al Barco 2) es toda la información que necesita. Modifique el vector de velocidad del barco 1 o el vector de aceleración del barco 1 (o incluso el vector de rumbo directamente) utilizando una versión ponderada de w.

ingrese la descripción de la imagen aquí

La magnitud de cuán lejos del barco 1 está fuera de curso (qué tan mal v no coincide con w) se puede encontrar usando ( 1 - dot(v,w))

  • ( dot(v,w)se maximiza cuando vy se walinea exactamente)
  • ( 1 - dot(v,w)) da 0 cuando vy westán completamente alineados, siempre vy cuando westán normalizados)

0

Hay una manera simple de encontrar el ángulo absoluto de un vector a través de la geometría normal.

por ejemplo vector V = 2i - 3j;

valor absoluto del coeficiente x = 2;

valor absoluto del coeficiente y = 3;

ángulo = atan (2/3); [el ángulo estará entre 0 y 90]

Según el ángulo del cuadrante se cambiará.

if (coeficiente x <0 y coeficiente y> 0) entonces ángulo = ángulo de 180;

if (coeficiente x <0 e coeficiente y <0) entonces ángulo = 180 + ángulo;

if (coeficiente x> 0 y coeficiente y <0) entonces ángulo = ángulo 360;

if (coeficiente x> 0 y coeficiente y> 0) entonces ángulo = ángulo;

después de encontrar el ángulo del primer y segundo vectores, solo resta el primer ángulo del vector del segundo vector. Entonces obtendrás un ángulo absoluto entre dos vectores.


55
Esto es exactamente lo que implementa la función atan2 () para usted. :)
Nathan Reed

@NathanReed, sí, pero ¿no podrías usar este método con un producto de puntos para evitar la sobrecarga de trigonometría?
jdk1.0

0

Tal vez debería publicar una pregunta ligeramente diferente y responderla yo mismo; Esta es la pregunta más cercana que pude encontrar a la que tenía.

Estoy trabajando en 2D en el lienzo html, donde tengo ángulos de rotación en radianes en lugar de vectores. Necesitaba el "ángulo de giro" (ta) para pasar del "rumbo actual" (h) al "rumbo objetivo" (t).

Aquí está mi solución:

var ta = t - h,
    ata = Math.abs(ta)
;
if (ta > Math.PI) ta = (ta / ata) * (Math.PI - ata)
return ta;
Al usar nuestro sitio, usted reconoce que ha leído y comprende nuestra Política de Cookies y Política de Privacidad.
Licensed under cc by-sa 3.0 with attribution required.