Como posso calcular o ângulo e a direção correta da curva entre dois vetores 2D?

26

Estou trabalhando em algum movimento AI onde não há obstáculos e o movimento é restrito ao plano XY. Estou calculando dois vetores, v , a direção oposta do navio 1 ew , o vetor apontando da posição do navio 1 para o navio 2.

Estou então calculando o ângulo entre esses dois vetores usando a fórmula

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

O problema que estou tendo é que arccosapenas retorna valores entre 0 ° e 180 °. Isso torna impossível determinar se devo virar à esquerda ou à direita para enfrentar o outro navio.

Existe uma maneira melhor de fazer isso?

Erro 454
fonte
11
Se você estiver usando o Unity, confira Mathf.DeltaAngle().
Russell Borogove

Respostas:

22

É muito mais rápido usar um produto cruzado 2D. Nenhuma função trigonométrica dispendiosa envolvida.

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 você ainda precisa dos ângulos reais, recomendo o uso atan2. atan2lhe dará o ângulo absoluto de qualquer vetor. Para obter o ângulo relativo entre qualquer vetor, calcule seus ângulos absolutos e use subtração simples.

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
fonte
11
Depois de ler a resposta do @ Tetrad, suponho que você possa combinar um produto cruzado e um arccos. Dessa forma, você usará apenas uma função trigonométrica, mas ainda terá o ângulo real. No entanto, recomendo essa otimização até ter certeza de que o rastreamento do ângulo da IA ​​está causando um impacto notável no desempenho do seu jogo.
Deft_code
2
Sim, ao converter entre vetores e ângulos, atan2 () é definitivamente seu amigo.
Bluescrn
11
Obrigado! Descobri que realmente não preciso do ângulo, agarrar o produto em cruz 2D é simples o suficiente para minhas necessidades.
Erro 454
2
Além disso, o seu cheque if( cross == -0.0f )vs if( cross == 0.0f )parece extremamente frágil.
bobobobo
11
@obobobo, com um mecanismo de física pode não ser uma opção apenas para escolher uma direção e se mover. Girar magicamente pode fazer com que os motores físicos surtem. Rotações mágicas adicionais também parecem terríveis para animação. Portanto, sim, você pode resolver isso sem a noção de esquerda ou direita, mas uma solução polida geralmente precisa delas.
Deft_code 26/11/12
10

Use arcsin do produto cruzado 2D (ou seja, o componente z do vetor produto cruzado). Isso lhe dará de 90 a 90, o que permitirá saber se você deve ir para a esquerda ou direita.

Tenha cuidado porque A cruz B não é igual a B cruz A.

Outra estratégia (mas provavelmente não tão direta) é calcular o "cabeçalho" dos dois vetores usando atan2 e depois descobrir se A apontando para graus X precisa ir para a esquerda ou direita para ir para B apontando para graus y.

Tetrad
fonte
Obrigado pela resposta. Para ficar claro para futuros navegadores, tomar o seno inverso da magnitude do produto cruzado 2d renderia valores entre 0 e 90. Tomar o seno do componente z do produto cruzado 2d produz os resultados desejados.
Erro 454
@ Erro 454, você está absolutamente certo, corrigiu minha postagem.
Tétrada
1

Use vetores para redirecionar o navio. É assim que os "comportamentos de direção" funcionam - você nunca precisa calcular o ângulo, basta usar os vetores que possui. Isso é computacionalmente muito mais barato.

O vetor w(vetor do navio 1 ao navio 2) é toda a informação que você precisa. Modifique o vetor de velocidade do navio 1 ou o vetor de aceleração do navio 1 (ou mesmo o vetor de cabeçalho diretamente) usando uma versão ponderada de w.

insira a descrição da imagem aqui

A magnitude de quão longe o navio 1 está fora do rumo (o quanto v não combina com w) pode ser encontrada usando ( 1 - dot(v,w))

  • ( dot(v,w)é maximizado quando ve walinhado exatamente)
  • ( 1 - dot(v,w)) fornece 0 quando ve westá completamente alinhado, fornecido ve wnormalizado)
bobobobo
fonte
0

Existe uma maneira simples de encontrar o ângulo absoluto de um vetor através da geometria normal.

por exemplo vetor V = 2i - 3j;

valor absoluto de x coeficiente = 2;

valor absoluto de coeficiente y = 3;

ângulo = atan (2/3); [o ângulo estará entre 0 e 90]

Com base no ângulo do quadrante será alterado.

se (coeficiente x <0 e coeficiente y> 0) então ângulo = ângulo 180;

se (coeficiente x <0 e coeficiente y <0) então ângulo = 180 + ângulo;

se (coeficiente x> 0 e coeficiente y <0) então ângulo = ângulo de 360;

se (coeficiente x> 0 e coeficiente y> 0) então ângulo = ângulo;

depois de encontrar o ângulo do primeiro e do segundo vetores, basta subtrair o ângulo do primeiro vetor do segundo vetor. Então você obterá um ângulo absoluto entre dois vetores.

manojyerra
fonte
5
É exatamente isso que a função atan2 () implementa para você. :)
Nathan Reed
@ NathanReed, sim, mas você não poderia usar esse método com um produto escalar para evitar a sobrecarga de trigonometria?
Jdk1.0
0

Talvez eu deva postar uma pergunta um pouco diferente e responder a mim mesma; esta é a pergunta mais próxima que eu poderia encontrar da que eu tinha.

Estou fazendo um trabalho 2D na tela html, onde tenho ângulos de rotação em radianos em vez de vetores. Eu precisava do "ângulo de virada" (ta) para ir do "cabeçalho atual" (h) para o "cabeçalho alvo" (t).

Aqui está a minha solução:

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