Wie kann ich den Winkel und die richtige Drehrichtung zwischen zwei 2D-Vektoren berechnen?

26

Ich arbeite an einer Bewegungs-KI, bei der es keine Hindernisse gibt und die Bewegung auf die XY-Ebene beschränkt ist. Ich berechne zwei Vektoren, v , die Blickrichtung von Schiff 1 und w , wobei der Vektor von der Position von Schiff 1 zu Schiff 2 zeigt.

Ich berechne dann den Winkel zwischen diesen beiden Vektoren mit der Formel

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

Das Problem, das ich habe, ist, dass arccosnur Werte zwischen 0 ° und 180 ° zurückgibt. Dies macht es unmöglich zu bestimmen, ob ich nach links oder rechts abbiegen soll, um mich dem anderen Schiff zuzuwenden.

Gibt es einen besseren Weg, dies zu tun?

Fehler 454
quelle
1
Wenn Sie Unity verwenden, überprüfen Sie Mathf.DeltaAngle().
Russell Borogove

Antworten:

22

Es ist viel schneller, ein 2D-Cross-Produkt zu verwenden. Keine kostspielige Auslösefunktion.

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

Wenn Sie noch die tatsächlichen Winkel benötigen, empfehle ich mit atan2. atan2Gibt den absoluten Winkel eines Vektors an. Um den relativen Winkel zwischen beliebigen Vektoren zu erhalten, berechnen Sie deren absoluten Winkel und verwenden Sie eine einfache Subtraktion.

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;
deft_code
quelle
1
Nachdem Sie die Antwort von @ Tetrad gelesen haben, können Sie vermutlich ein Kreuzprodukt mit einem kombinieren arccos. Auf diese Weise verwenden Sie nur eine Triggerfunktion, haben aber immer noch den tatsächlichen Winkel. Ich empfehle jedoch, gegen diese Optimierung vorzugehen, bis Sie sicher sind, dass das Verfolgen des KI-Winkels einen spürbaren Einfluss auf die Leistung Ihres Spiels hat.
deft_code
2
Ja, wenn Sie zwischen Vektoren und Winkeln konvertieren, ist atan2 () definitiv Ihr Freund.
Bluescrn
1
Vielen Dank! Ich habe festgestellt, dass ich den Winkel eigentlich nicht wirklich brauche. Das Ergreifen des 2D-Kreuzprodukts ist einfach genug für meine Bedürfnisse.
Fehler 454
2
Auch dein if( cross == -0.0f )Vs- if( cross == 0.0f )Scheck sieht extrem zerbrechlich aus.
Bobobobo
1
@bobobobo, mit einer Physik-Engine ist es möglicherweise keine Option, einfach eine Richtung auszuwählen und sich zu bewegen. Durch magisches Drehen können physikalische Motoren ausfallen. Ein weiteres magisches Drehen sieht auch für die Animation furchtbar aus. Also ja, Sie können dies ohne die Vorstellung von links oder rechts lösen, aber eine polierte Lösung benötigt diese oft.
deft_code
10

Verwenden Sie ArcSin des 2D-Kreuzprodukts (dh die z-Komponente des Kreuzproduktvektors). Das gibt dir -90 bis 90, wodurch du weißt, ob du nach links oder rechts gehen sollst.

Seien Sie vorsichtig, da A-Kreuz B nicht mit B-Kreuz A identisch ist.

Eine andere Strategie (aber wahrscheinlich nicht so einfach) besteht darin, die "Überschrift" der beiden Vektoren mit atan2 zu berechnen und dann herauszufinden, ob A, das auf X Grad zeigt, nach links oder rechts gehen muss, um zu B zu gelangen, das auf y Grad zeigt.

Tetrade
quelle
Danke für die Antwort. Um für zukünftige Browser klar zu sein, würde die Verwendung des inversen Sinus der Größe des 2d-Kreuzprodukts Werte zwischen 0 und 90 ergeben. Die Verwendung des Sinus der z-Komponente des 2d-Kreuzprodukts liefert die gewünschten Ergebnisse.
Fehler 454
@Error 454, du hast vollkommen recht, mein Beitrag wurde repariert.
Tetrad
1

Verwenden Sie Vektoren , um das Schiff umzuleiten. So funktioniert "Lenkverhalten" - Sie müssen den Winkel nie berechnen, sondern nur die Vektoren verwenden, die Sie haben. Dies ist rechnerisch wesentlich günstiger.

Der Vektor w(Vektor von Schiff 1 nach Schiff 2) enthält alle Informationen, die Sie benötigen. Ändern Sie entweder den Geschwindigkeitsvektor von Schiff 1 oder den Beschleunigungsvektor von Schiff 1 (oder sogar den Kursvektor direkt) mit einer gewichteten Version von w.

Bildbeschreibung hier eingeben

Die Größe, wie weit Schiff 1 vom Kurs abweicht (wie schlecht v nicht mit w übereinstimmt), kann mit ( 1 - dot(v,w)) ermittelt werden.

  • ( dot(v,w)Wird maximiert, wenn vund wgenau ausgerichtet)
  • ( 1 - dot(v,w)) gibt 0, wenn vund wvollständig aufgereiht, bereitgestellt vund wnormalisiert sind)
Bobobobo
quelle
0

Es gibt eine einfache Möglichkeit, den absoluten Winkel eines Vektors durch normale Geometrie zu ermitteln.

zum Beispiel Vektor V = 2i - 3j;

absoluter Wert von x Koeffizient = 2;

absoluter Wert des y-Koeffizienten = 3;

Winkel = atan (2/3); [Winkel liegt zwischen 0 und 90]

Basierend auf Quadrantenwinkel wird geändert.

wenn (x-Koeffizient <0 und y-Koeffizient> 0) dann Winkel = 180-Winkel;

wenn (x-Koeffizient <0 und y-Koeffizient <0) dann Winkel = 180 + Winkel;

wenn (x-Koeffizient> 0 und y-Koeffizient <0) dann Winkel = 360-Winkel;

wenn (x-Koeffizient> 0 und y-Koeffizient> 0), dann ist Winkel = Winkel;

Nach dem Finden des Winkels des ersten und des zweiten Vektors subtrahieren Sie einfach den ersten Vektorwinkel vom zweiten Vektor. Dann erhalten Sie den absoluten Winkel zwischen zwei Vektoren.

manojyerra
quelle
5
Genau dies implementiert die Funktion atan2 () für Sie. :)
Nathan Reed
@ NathanReed, ja, aber könnten Sie diese Methode nicht mit einem Skalarprodukt anwenden, um den Trigger-Overhead zu vermeiden?
jdk1.0
0

Vielleicht sollte ich eine etwas andere Frage stellen und sie selbst beantworten. Das ist die Frage, die ich am ehesten finden konnte.

Ich mache 2D-Arbeit im HTML-Canvas, wo ich Rotationswinkel im Bogenmaß anstelle von Vektoren habe. Ich brauchte den "Drehwinkel" (ta), um vom "aktuellen Kurs" (h) zum "Zielkurs" (t) zu gelangen.

Hier ist meine Lösung:

var ta = t - h,
    ata = Math.abs(ta)
;
if (ta > Math.PI) ta = (ta / ata) * (Math.PI - ata)
return ta;
Shavais
quelle