두 2D 벡터 사이의 각도와 적절한 회전 방향을 어떻게 계산할 수 있습니까?


26

장애물이없고 움직임이 XY 평면으로 제한된 움직임 AI를 작업 중입니다. 나는 선박 1의 방향을 향한 두 개의 벡터 v 와 선박 1 의 위치에서 선박 2를 가리키는 벡터 w 를 계산하고 있습니다.

그런 다음 공식을 사용 하여이 두 벡터 사이의 각도를 계산합니다.

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

내가 겪고있는 문제 arccos는 0 °와 180 ° 사이의 값 만 반환 한다는 것입니다. 이로 인해 내가 다른 배를 향하도록 왼쪽 또는 오른쪽으로 돌릴 지 여부를 결정할 수 없습니다.

더 좋은 방법이 있습니까?


1
Unity를 사용하는 경우를 확인하십시오 Mathf.DeltaAngle().
Russell Borogove

답변:


22

2D 교차 제품을 사용하는 것이 훨씬 빠릅니다. 고가의 삼각 기능이 필요하지 않습니다.

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

여전히 실제 각도가 필요한 경우을 사용하는 것이 좋습니다 atan2. atan2벡터의 절대 각도를 제공합니다. 벡터와 벡터 간의 상대 각도를 얻으려면 절대 각도를 계산하고 간단한 빼기를 사용하십시오.

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
@Tetrad의 답변을 읽은 후 교차 제품과을 결합 할 수 있다고 가정합니다 arccos. 이렇게하면 하나의 삼각 함수 만 사용하지만 실제 각도는 그대로 유지됩니다. 그러나 AI 각도 추적이 게임 성능에 눈에 띄게 영향을 줄 때까지이 최적화를 사용하지 않는 것이 좋습니다.
deft_code

2
예, 벡터와 각도를 변환 할 때 atan2 ()가 가장 확실합니다.
bluescrn

1
감사! 나는 실제로 각도가 필요하지 않다는 것을 알았습니다 .2D 크로스 제품을 잡는 것은 내 요구에 충분히 간단합니다.
454

2
또한 귀하의 if( cross == -0.0f )vs if( cross == 0.0f )수표는 매우 약해 보입니다.
bobobobo

1
@bobobobo, 물리 엔진을 사용하면 방향을 선택하고 이동하는 옵션이 아닐 수도 있습니다. 마법의 회전은 물리 엔진을 놀라게 할 수 있습니다. 더 마술처럼 회전하는 것은 애니메이션에 끔찍한 것처럼 보입니다. 예, 왼쪽 또는 오른쪽의 개념 없이이 문제를 해결할 있지만 세련된 솔루션에는 종종 이것들이 필요합니다.
deft_code

10

2D 교차 곱의 아크 신 (즉, 교차 곱 벡터의 z 성분)을 사용하십시오. 왼쪽에서 오른쪽으로 갈지 여부를 알려주는 -90 ~ 90을 줄 것입니다.

A 크로스 B는 B 크로스 A와 같지 않으므로주의하십시오.

또 다른 전략 (아마도 직진하지는 않음)은 atan2를 사용하여 두 벡터의 "제목"을 계산 한 다음 y도를 가리키는 B로 이동하기 위해 X도를 가리키는 A가 왼쪽 또는 오른쪽으로 이동해야하는지 여부를 알아내는 것입니다.


답변 주셔서 감사합니다. 미래의 브라우저에서 명확하게하기 위해, 2 차원 교차 곱의 크기의 역 사인을 취하면 0과 90 사이의 값을 얻을 수 있습니다. 2 차원 교차 곱의 z 성분의 사인을 취하면 원하는 결과가 나타납니다.
454

@ 오류 454, 당신은 절대적으로 맞습니다, 내 게시물을 수정했습니다.
Tetrad

1

벡터 를 사용 하여 선박을 리디렉션하십시오. 이것이 "스티어링 행동"이 작동하는 방식입니다. 각도를 계산할 필요가 없으며 보유한 벡터 만 사용하십시오. 이것은 계산 상 훨씬 저렴합니다.

벡터 w(선박 1에서 선박 2 로의 벡터)는 필요한 모든 정보입니다. 가중 버전을 사용하여 1 선의 속도 벡터 또는 1 선의 가속 벡터 (또는 직접 향하는 벡터)를 수정하십시오 w.

여기에 이미지 설명을 입력하십시오

크기 얼마나 멀리 선박 1 오프는 물론 꺼져 사용하여 볼 수 있습니다 (V는 w와 일치하지 않는 방법을 심하게) ( 1 - dot(v,w))

  • ( dot(v,w)때 극대화 v하고 w정확하게 정렬)
  • ( 1 - dot(v,w))는 0을 제공 v하고 w완전히 일렬로 제공 v하고 w) 정규화

0

법선 지오메트리를 통해 벡터의 절대 각도를 찾는 간단한 방법이 있습니다.

예를 들어 벡터 V = 2i-3j;

x 계수의 절대 값 = 2;

y 계수의 절대 값 = 3;

각도 = 아탄 (2/3); [각도는 0 ~ 90 사이입니다]

사분면 각도에 ​​따라 변경됩니다.

(x 계수 <0 및 y 계수> 0)이면 각도 = 180 각;

(x 계수 <0 및 y 계수 <0)이면 각도 = 180+ 각;

(x 계수> 0 및 y 계수 <0)이면 각도 = 360 각;

(x 계수> 0 및 y 계수> 0)이면 각도 = 각도;

첫 번째와 두 번째 벡터의 각도를 찾은 후 두 번째 벡터에서 첫 번째 벡터 각도를 빼십시오. 그런 다음 두 벡터 사이의 절대 각도를 얻습니다.


5
이것이 바로 atan2 () 함수가 구현 한 것입니다. :)
Nathan Reed

@NathanReed, 예, 그러나 삼각법 오버 헤드를 피하기 위해이 방법을 도트 제품과 함께 사용할 수 없습니까?
jdk1.0

0

어쩌면 약간 다른 질문을 게시하고 스스로 대답해야 할 수도 있습니다. 이것은 내가 가진 질문에 가장 가까운 질문입니다.

html 캔버스에서 2D 작업을하고 있는데 여기서 벡터가 아닌 라디안으로 회전 각도가 있습니다. "현재 제목"(h)에서 "대상 제목"(t)으로 이동하려면 "회전 각도"(ta)가 필요했습니다.

내 해결책은 다음과 같습니다.

var ta = t - h,
    ata = Math.abs(ta)
;
if (ta > Math.PI) ta = (ta / ata) * (Math.PI - ata)
return ta;
당사 사이트를 사용함과 동시에 당사의 쿠키 정책개인정보 보호정책을 읽고 이해하였음을 인정하는 것으로 간주합니다.
Licensed under cc by-sa 3.0 with attribution required.