2012-06-13 2 views
4

제 질문은 2D에서 두 벡터 사이의 최소 각도 방향을 만드는 것입니다. 나는 C++로 게임을 만들고 있는데, 장애물 중 하나가 열 찾기 미사일 발사기입니다. 나는 목표와 총알 사이의 벡터를 계산하고 벡터를 정규화 한 다음 속도로 곱하는 식으로 작업했습니다. 그러나, 나는 더 나은 그것을 만들기 위해이 수업으로 돌아오고 있습니다. 플레이어에 순간적으로 고정하는 대신, 총알 벡터가 특정 각도 (총알 벡터와 벡터 bulletloc-> 목표 사이의 각도) 내에있을 때만 플레이어가 원하는대로합니다. 그렇지 않으면 나는 천천히 목표물을 향해도 (degree)만큼 패닝하여 플레이어에게 그것을 피할 수있는 충분한 공간을 제공하기를 원합니다. 나는이 모든 것을했다 (vb.net 프로젝트에서 문제를 단순화하고, C++로 다시 작성한다). 그러나 가장 빠른 경로가 시계 반대 방향 일지라도 총알은 항상 목표를 향해 시계 방향으로 회전합니다. 그래서 문제는 회전을 적용 할 방향을 설정하여 가장 작은 각도를 커버합니다. 내가 가진두 벡터 사이의 최단 회전 방향

Function Rotate(ByVal a As Double, ByVal tp As Point, ByVal cp As Point, ByVal cv As Point) 
    'params a = angle, tp = target point, cp = current point, cv = current vector of bullet' 
    Dim dir As RotDir 'direction to turn in' 
    Dim tv As Point 'target vector cp->tp' 
    Dim d As Point 'destination point (d) = cp + vector' 
    Dim normal As Point 
    Dim x1 As Double 
    Dim y1 As Double 
    Dim VeritcleResolution As Integer = 600 

    tp.Y = VeritcleResolution - tp.Y 'modify y parts to exist in plane with origin (0,0) in bottom left' 
    cp.Y = VeritcleResolution - cp.Y 
    cv.Y = cv.Y * -1 

    tv.X = tp.X - cp.X 'work out cp -> tp' 
    tv.Y = tp.Y - cp.Y 

    'calculate angle between vertor to target and vecrot currntly engaed on' 
    Dim tempx As Double 
    Dim tempy As Double 

    tempx = cv.X * tv.X 
    tempy = cv.Y * tv.Y 

    Dim DotProduct As Double 

    DotProduct = tempx + tempy 'dot product of cp-> d and cp -> tp' 

    Dim magCV As Double 'magnitude of current vector' 
    Dim magTV As Double 'magnitude of target vector' 

    magCV = Math.Sqrt(Math.Pow(cv.X, 2) + Math.Pow(cv.Y, 2)) 
    magTV = Math.Sqrt(Math.Pow(tv.X, 2) + Math.Pow(tv.Y, 2)) 

    Dim VectorAngle As Double 

    VectorAngle = Acos(DotProduct/(magCV * magTV)) 
    VectorAngle = VectorAngle * 180/PI 'angle between cp->d and cp->tp' 

    If VectorAngle < a Then 'if the angle is small enough translate directly towards target' 
     cv = New Point(tp.X - cp.X, tp.Y - cp.Y) 
     magCV = Math.Sqrt((cv.X^2) + (cv.Y^2)) 

     If magCV = 0 Then 
      x1 = 0 
      y1 = 0 
     Else 
      x1 = cv.X/magCV 
      y1 = cv.Y/magCV 
     End If 

     normal = New Point(x1 * 35, y1 * 35) 
     normal.Y = normal.Y * -1 

     cv = normal 
    ElseIf VectorAngle > a Then 'otherwise smootly translate towards the target' 
     Dim x As Single 
     d = New Point(cp.X + cv.X, cp.Y + cv.Y) 


     a = (a * -1) * PI/180 'THIS LINE CONTROL DIRECTION a = (a*-1) * PI/180 would make the rotation counter clockwise' 

     'rotate the point' 
     d.X -= cp.X 
     d.Y -= cp.Y 

     d.X = (d.X * Cos(a)) - (d.Y * Sin(a)) 
     d.Y = (d.X * Sin(a)) + (d.Y * Cos(a)) 

     d.X += cp.X 
     d.Y += cp.Y 

     cv.X = d.X - cp.X 
     cv.Y = d.Y - cp.Y 

     cv.Y = cv.Y * -1 
    End If 

    Return cv 

End Function 

하나의 아이디어는 두 벡터의 베어링을 해결하는 것이었다과 차이가 큰 180도 이상이면 시계 방향으로 달리 회전 회전 : 당신이 시도하고 내가 설명하고 무엇을 볼 수 있도록 여기 내 코드입니다 시계 반대 방향으로 가면 어떤 아이디어라도 도움이 될 것입니다. 감사.

편집 :이 사이트는 매우 유용하다고 덧붙이고 싶습니다. 자주 다른 사람들이 제기 한 질문을 사용하여 자신의 문제를 해결하고 감사의 말을 전하고 싶습니다.

+0

사이드 노트 :이 물건을 조금 캡슐화하기 위해 (또는 다른 사람의 것) 벡터 구조를 작성하면 코드를 읽고 수정하고 유지하는 것이 훨씬 쉬울 것입니다. – Cameron

+0

예 벡터를 나타 내기 위해 점을 사용했습니다. 나는 그것을 VB에서 작업에 문제를 해결하기 위해 작성했습니다.내 주요 프로젝트에는 벡터 클래스가 있습니다. –

답변

9

코드에서 작성한 것처럼 두 (정규화 된) 벡터 사이의 각도는 내적의 역 코사인입니다.

각도가 이되도록하려면 다른 두 벡터가 놓여있는 평면의 법선을 나타내는 세 번째 벡터를 사용할 수 있습니다. 2D 케이스의 경우 "위로 향하는"3D 벡터가됩니다. 말 (0, 0, 1).

그런 다음 두 번째 벡터 (십자 표시는 교환 가능하지 않음)를 사용하여 첫 번째 벡터 (각도를 기준으로 할 벡터)의 외적을 취합니다. 각도의 부호는 결과 벡터와 평면 법선 사이의 내적 부호와 같아야합니다.

코드에서 (C 번호 미안) - 모든 벡터는 정규화 된 것으로 가정된다 참고 :이 두 벡터의 외적은 세번째 벡터를 얻을 수 있다는 사실을 이용하여 작동

public static double AngleTo(this Vector3 source, Vector3 dest) 
{ 
    if (source == dest) { 
     return 0; 
    } 
    double dot; Vector3.Dot(ref source, ref dest, out dot); 
    return Math.Acos(dot); 
} 

public static double SignedAngleTo(this Vector3 source, Vector3 dest, Vector3 planeNormal) 
{ 
    var angle = source.AngleTo(dest); 
    Vector3 cross; Vector3.Cross(ref source, ref dest, out cross); 
    double dot; Vector3.Dot(ref cross, ref planeNormal, out dot); 
    return dot < 0 ? -angle : angle; 
} 

처음 2 개로 정의 된 평면에 수직 (수직) (따라서 본질적으로 3D 연산 임). a x b = -(b x a)이므로 벡터는 항상 평면에 수직이되지만 ab 사이의 (부호가있는) 각도에 따라 다른면에 있습니다 (right-hand rule라고하는 것이 있습니다).

따라서 교차 곱은 벡터 간의 각도가 180 °를 통과 할 때 방향을 변경하는 평면에 수직 인 부호있는 벡터를 제공합니다. 사전에을 가리키는 평면 에 수직 인 벡터를 미리 알면 해당 내적 제품의 부호를 확인하여 교차 제품이 해당 평면과 같은 방향인지 여부를 알 수 있습니다.

+0

다음 두 벡터를 V = -2x, 4y, 0z 및 U = 2x, y4, 0z로 가정 해 보겠습니다. V와 U 사이의 각도의 부호를 풀려면 (V는 시작점이고 U는 끝점입니다. 답은 시계 방향이어야 함) 두 행렬의 교차 곱을 취해야합니다. 행렬 {(x (2 * 2) - (4 * 4))의 x, y 성분이 0이되는 (-2, 4, 0) = -20이면이 벡터 (0x, 0y, -20z)와 단위 벡터 (0,0,1) = -20의 내적을 구합니다. –

+0

기호가 음수이므로 각도의 부호도 음수 여야합니까? 그렇다면 무언가 잘못 했어야합니다. (행렬을 틀린 방식으로 구성 했습니까?) –

+0

@David : [이 교차 곱 계산기] (http://www.wolframalpha.com/input/?i=cross+ 제품 + 계산기)를 사용하여 수학을 확인하십시오. 당신은 당신의 모범을 올바르게했습니다. 내적은 음수이고 최종 각도는 음수 여야합니다. 시계 방향의 각도가 양수라는 가정이 잘못 되었기 때문에 (또는 관습에 어긋납니다.) 예상했던 것과 다른 이유입니다. 단위 원을 상상해보십시오 - 각도 0에 해당하는 점이 원의 맨 오른쪽에 있고, 각도가 커지면 점이 * 시계 반대 방향으로 이동합니다. – Cameron