Comment puis-je calculer l'angle et la bonne direction de virage entre deux vecteurs 2D?

26

Je travaille sur un mouvement AI où il n'y a pas d'obstacles et le mouvement est limité au plan XY. Je calcule deux vecteurs, v , la direction opposée du navire 1 et w , le vecteur pointant de la position du navire 1 au navire 2.

Je calcule ensuite l'angle entre ces deux vecteurs en utilisant la formule

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

Le problème que j'ai, c'est qu'il arccosne renvoie que des valeurs comprises entre 0 ° et 180 °. Cela rend impossible de déterminer si je dois tourner à gauche ou à droite pour faire face à l'autre navire.

Y a-t-il une meilleure manière de faire cela?

Erreur 454
la source
1
Si vous utilisez Unity, consultez Mathf.DeltaAngle().
Russell Borogove

Réponses:

22

Il est beaucoup plus rapide d'utiliser un produit croisé 2D. Aucune fonction trigonométrique coûteuse n'est impliquée.

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 vous avez toujours besoin des angles réels, je recommande d'utiliser atan2. atan2vous donnera l'angle absolu de n'importe quel vecteur. Pour obtenir l'angle relatif entre l'un et les vecteurs, calculez leurs angles absolus et utilisez une soustraction 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;
deft_code
la source
1
Après avoir lu la réponse de @ Tetrad, je suppose que vous pourriez combiner un produit croisé et un arccos. De cette façon, vous n'utiliserez qu'une seule fonction trig, mais aurez toujours l'angle réel. Cependant, je déconseille cette optimisation jusqu'à ce que vous soyez sûr que le suivi de l'angle de l'IA a un impact notable sur les performances de votre jeu.
deft_code
2
Oui, lors de la conversion entre vecteurs et angles, atan2 () est certainement votre ami.
bluescrn
1
Merci! J'ai trouvé que je n'ai pas vraiment besoin de l'angle, saisir le produit croisé 2D est assez simple pour mes besoins.
Erreur 454
2
De plus, votre chèque if( cross == -0.0f )vs if( cross == 0.0f )semble extrêmement fragile.
bobobobo
1
@bobobobo, avec un moteur physique, il peut ne pas être possible de simplement choisir une direction et de se déplacer. Magiquement, tourner peut faire paniquer les moteurs physiques. Une rotation magique semble également terrible pour l'animation. Alors oui, vous pouvez résoudre cela sans notion de gauche ou de droite, mais une solution polie en a souvent besoin.
deft_code
10

Utilisez l'arcsin du produit croisé 2D (c'est-à-dire la composante z du vecteur du produit croisé). Cela vous donnera -90 à 90, ce qui vous permettra de savoir si vous devez aller à gauche ou à droite.

Attention car la croix A n'est pas la même chose que la croix A.

Une autre stratégie (mais probablement pas aussi simple) consiste à calculer le "cap" des deux vecteurs en utilisant atan2, puis à déterminer si A pointant vers X degrés doit aller à gauche ou à droite pour aller à B pointant à y degrés.

Tetrad
la source
Merci pour votre réponse. Pour être clair pour les futurs navigateurs, prendre le sinus inverse de la magnitude du produit croisé 2d donnerait des valeurs comprises entre 0 et 90. Prendre le sinus de la composante z du produit croisé 2d donne les résultats souhaités.
Erreur 454
@Error 454, vous avez absolument raison, a corrigé mon message.
Tetrad
1

Utilisez des vecteurs pour rediriger le navire. C'est ainsi que fonctionnent les "comportements de direction" - vous n'avez jamais besoin de calculer l'angle, utilisez simplement les vecteurs dont vous disposez. C'est beaucoup moins cher sur le plan informatique.

Le vecteur w(vecteur du navire 1 au navire 2) contient toutes les informations dont vous avez besoin. Modifiez le vecteur de vitesse du navire 1 ou le vecteur d'accélération du navire 1 (ou même le vecteur de cap directement) à l'aide d'une version pondérée de w.

entrez la description de l'image ici

L' amplitude de la distance par rapport au navire 1 (à quel point v ne correspond pas à w) peut être trouvée en utilisant ( 1 - dot(v,w))

  • ( dot(v,w)est maximisé quand vet waligné exactement)
  • ( 1 - dot(v,w)) Donne 0 quand vet wsont complètement alignés, à condition vet wsont normalisés)
bobobobo
la source
0

Il existe un moyen simple de trouver l'angle absolu d'un vecteur à travers une géométrie normale.

par exemple le vecteur V = 2i - 3j;

valeur absolue du coefficient x = 2;

valeur absolue du coefficient y = 3;

angle = atan (2/3); [l'angle sera compris entre 0 et 90]

Basé sur l'angle du quadrant sera modifié.

si (coefficient x <0 et coefficient y> 0) alors angle = angle 180;

si (coefficient x <0 et coefficient y <0) alors angle = 180 + angle;

si (coefficient x> 0 et coefficient y <0) alors angle = 360 °;

si (coefficient x> 0 et coefficient y> 0) alors angle = angle;

après avoir trouvé l'angle des premier et deuxième vecteurs, il suffit de soustraire le premier angle de vecteur du deuxième vecteur. Ensuite, vous obtiendrez un angle absolu entre deux vecteurs.

manojyerra
la source
5
C'est exactement ce que la fonction atan2 () implémente pour vous. :)
Nathan Reed
@NathanReed, oui, mais ne pourriez-vous pas utiliser cette méthode avec un produit scalaire pour éviter la surcharge de trigonométrie?
jdk1.0
0

Je devrais peut-être poster une question légèrement différente et y répondre moi-même; c'est la question la plus proche que j'ai pu trouver à celle que j'avais.

Je fais du travail 2D dans le canevas html, où j'ai des angles de rotation en radians plutôt qu'en vecteurs. J'avais besoin de «l'angle de virage» (ta) pour passer du «cap actuel» (h) au «cap cible» (t).

Voici ma solution:

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