Come posso calcolare l'angolo e la direzione di svolta corretta tra due vettori 2D?


26

Sto lavorando su alcuni movimenti di AI in cui non ci sono ostacoli e il movimento è limitato al piano XY. Sto calcolando due vettori, v , la direzione di fronte della nave 1 e w , il vettore che punta dalla posizione della nave 1 alla nave 2.

Sto quindi calcolando l'angolo tra questi due vettori usando la formula

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

Il problema che sto riscontrando è che arccosrestituisce solo valori compresi tra 0 ° e 180 °. Questo rende impossibile determinare se dovrei girare a sinistra o a destra per affrontare l'altra nave.

C'è un modo migliore per farlo?


1
Se stai usando Unity, dai un'occhiata Mathf.DeltaAngle().
Russell Borogove,

Risposte:


22

È molto più veloce usare un prodotto incrociato 2d. Nessuna funzione di innesco costosa coinvolta.

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

Se hai ancora bisogno degli angoli reali che ti consiglio di usare atan2. atan2ti darà l'angolo assoluto di qualsiasi vettore. Per ottenere l'angolo relativo tra uno qualsiasi dei vettori, calcola i loro angoli assoluti e usa la sottrazione semplice.

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
Dopo aver letto la risposta di @Tetrad suppongo che potresti combinare un prodotto incrociato e uno arccos. In questo modo utilizzerai solo una funzione di attivazione, ma avrai comunque l'angolazione effettiva. Tuttavia, sconsiglio questa ottimizzazione fino a quando non sei sicuro che il rilevamento dell'angolo AI abbia un impatto notevole sulle prestazioni del tuo gioco.
deft_code

2
Sì, durante la conversione tra vettori e angoli, atan2 () è sicuramente tuo amico.
bluescrn,

1
Grazie! Ho scoperto che in realtà non ho davvero bisogno dell'angolo, afferrare il prodotto a croce 2D è abbastanza semplice per le mie esigenze.
Errore 454

2
Anche il tuo controllo if( cross == -0.0f )vs if( cross == 0.0f )sembra estremamente fragile.
Bobobobo,

1
@bobobobo, con un motore fisico potrebbe non essere un'opzione per scegliere una direzione e muoverti. La rotazione magica può causare il panico dei motori fisici. L'ulteriore rotazione magica sembra terribile anche per l'animazione. Quindi sì, puoi risolverlo senza una nozione di sinistra o destra, ma una soluzione raffinata spesso ha bisogno di questi.
deft_code,

10

Utilizzare arcsin del prodotto incrociato 2D (ovvero il componente z del vettore prodotto incrociato). Questo ti darà da -90 a 90 che ti faranno sapere se andare a sinistra o a destra.

Fai attenzione perché una croce B non è uguale alla croce B A.

Un'altra strategia (ma probabilmente non così semplice) è calcolare la "direzione" dei due vettori usando atan2 e quindi capire se A che punta a X gradi deve andare a sinistra o destra per andare a B che punta a y gradi.


Grazie per la risposta. Per essere chiari per i futuri browser, prendere il seno inverso della grandezza del prodotto incrociato 2d produrrebbe valori compresi tra 0 e 90. Prendendo il seno del componente z del prodotto incrociato 2d si ottengono i risultati desiderati.
Errore 454

@Error 454, hai perfettamente ragione, risolto il mio post.
Tetrad

1

Usa i vettori per reindirizzare la nave. Ecco come funzionano i "comportamenti di guida": non è mai necessario calcolare l'angolo, basta usare i vettori che si hanno. Computazionalmente è molto più economico.

Il vettore w(vettore dalla nave 1 alla nave 2) è tutte le informazioni necessarie. Modifica il vettore di velocità della nave 1 o il vettore di accelerazione della nave 1 (o anche il vettore di rotta direttamente) usando una versione ponderata di w.

inserisci qui la descrizione dell'immagine

La grandezza di quanto lontano dalla nave 1 è fuori rotta (quanto male v non corrisponde a w) può essere trovata usando ( 1 - dot(v,w))

  • ( dot(v,w)è massimizzato quando ve wallinea esattamente)
  • ( 1 - dot(v,w)) dà 0 quando ve wsono completamente allineati, forniti ve wnormalizzati)

0

C'è un modo semplice per trovare l'angolo assoluto di un vettore attraverso la normale geometria.

per esempio vettore V = 2i - 3j;

valore assoluto del coefficiente x = 2;

valore assoluto del coefficiente y = 3;

angolo = atan (2/3); [l'angolo sarà compreso tra 0 e 90]

In base all'angolo del quadrante verrà modificato.

se (coefficiente x <0 e coefficiente y> 0) allora angolo = angolo di 180;

se (coefficiente x <0 e coefficiente y <0) allora angolo = 180 + angolo;

se (coefficiente x> 0 e coefficiente y <0) allora angolo = angolo di 360;

se (coefficiente x> 0 e coefficiente y> 0) allora angolo = angolo;

dopo aver trovato l'angolo del primo e del secondo vettore, basta sottrarre l'angolo del primo vettore dal secondo vettore. Quindi otterrai un angolo assoluto tra due vettori.


5
Questo è esattamente ciò che la funzione atan2 () implementa per te. :)
Nathan Reed,

@NathanReed, sì, ma non hai potuto usare questo metodo con un prodotto dot per evitare il sovraccarico?
jdk1.0,

0

Forse dovrei pubblicare una domanda leggermente diversa e rispondere a me stesso; questa è la domanda più vicina che ho potuto trovare a quella che avevo.

Sto facendo un lavoro 2D nella tela html, dove ho angoli di rotazione in radianti piuttosto che in vettori. Avevo bisogno dell '"angolo di svolta" (ta) per passare da "direzione corrente" (h) alla "direzione target" (t).

Ecco la mia soluzione:

var ta = t - h,
    ata = Math.abs(ta)
;
if (ta > Math.PI) ta = (ta / ata) * (Math.PI - ata)
return ta;
Utilizzando il nostro sito, riconosci di aver letto e compreso le nostre Informativa sui cookie e Informativa sulla privacy.
Licensed under cc by-sa 3.0 with attribution required.