벡터는 방향과 크기를 설명하는 데 사용되는 기본적인 수학적 개념입니다. 게임과 앱에서 벡터는 캐릭터의 위치, 무언가의 이동 속도, 두 오브젝트 간의 거리 등과 같은 여러 기본적인 프로퍼티를 설명하기 위해 자주 사용됩니다.
벡터 연산은 그래픽스, 물리, 애니메이션 등과 같은 컴퓨터 프로그래밍의 많은 요소에 있어 핵심적이며, Unity를 최대한 활용하기 위해서는 벡터 연산을 깊이 이해하는 것이 좋습니다.
벡터는 여러 차원으로 표현할 수 있으며, Unity는 2D, 3D 및 4D 벡터 작업을 위한 Vector2, Vector3 및 Vector4 클래스를 제공합니다. 이 세 가지 타입의 Vector 클래스는 모두 많은 동일한 함수(예: 크기)를 공유하므로, 달리 명시하지 않는 한 이 페이지에 있는 대부분의 정보는 세 가지 타입의 벡터 모두에 적용됩니다.
이 페이지에서는 Vector 클래스에 대한 개요와 스크립팅 시 일반적인 용도에 대해 설명합니다. Vector 클래스의 모든 멤버에 대한 전체 레퍼런스는 Vector2, Vector3 및 Vector4에 대한 스크립트 레퍼런스 페이지를 참조하십시오.
두 벡터를 더하면 그 결과는 오리지널 벡터를 “단계”대로 하나씩 차례대로 취한 것과 동일합니다. 두 파라미터의 순서가 바뀌어도 결과가 같으므로 순서는 상관이 없습니다.
첫 번째 벡터가 공간 내 하나의 점일 경우, 두 번째 벡터는 그 포지션의 오프셋 또는 그 포지션에서 “점프”한 것으로 해석할 수 있습니다. 예를 들어, 땅 위 어떤 위치로부터 5유닛 위에 있는 위치의 지점을 찾으려면 다음 연산을 사용하면 됩니다.-
var pointInAir = pointOnGround + new Vector2(0, 5);
벡터가 힘을 나타내는 단위일 때 벡터를 힘의 크기인 강도와 방향으로 간주하면 더욱 직관적으로 이해할 수 있습니다. 두 가지 힘의 벡터를 더하면 두 힘을 합한 값과 동일한 새로운 벡터가 생성됩니다. 이 개념은 동시에 다수의 서로 다른 컴포넌트에 힘을 가할 때 유용합니다. 예를 들어, 로켓이 추진력에 의해 앞으로 가고 있지만 역풍으로 인해 영향을 받는 경우를 구현하기 유용합니다.
여기 예제에는 2D 벡터가 나와 있지만, 3D 및 4D 벡터에도 동일한 개념이 적용됩니다.
벡터의 뺄셈이 가장 많이 사용되는 경우는 한 오브젝트에서 다른 오브젝트까지의 거리와 방향을 구하고자 할 때입니다. 주의할 점은 벡터의 뺄셈에서는 두 파라미터의 순서가 매우 중요하다는 점입니다.
// The vector d has the same magnitude as c but points in the opposite direction.
var c = b - a;
var d = a - b;
숫자의 경우 음의 벡터를 더하는 것은 양의 벡터를 빼는 것과 동일합니다.
// These both give the same result.
var c = a - b;
var c = a + -b;
음의 벡터는 오리지널 벡터와 크기가 같고 같은 선상에서 정반대 방향을 가리키는 벡터를 말합니다.
공백 내 한 포인트의 값을 다른 포인트의 값에서 빼면 그 결과는 한 오브젝트에서 다른 오브젝트를 “가리키는” 벡터 입니다.
// Gets a vector that points from the player's position to the target's.
var heading = target.position - player.position;
이 벡터는 타겟 오브젝트의 방향을 가리키고, 이 벡터의 크기는 두 포지션 사이의 거리와 같습니다. 예를 들어 고정 거리로 발사체를 유도하기 위해 “정규화된” 벡터가 필요할 수 있습니다. 이 경우 벡터를 자체 크기로 나누어 정규화할 수 있습니다.
var distance = heading.magnitude;
var direction = heading / distance; // This is now the normalized direction.
방법은 크기와 정규화 된 프로퍼티를 모두 별도로 사용하는 것보다 바람직합니다. 둘 다 CPU를 많이 소모하기 때문입니다(모두 제곱근 연산을 수반함).
거리를 비교 용도로만 사용해야 하는 경우(예: 근접 검사) 크기 계산 자체를 피할 수 있습니다. sqrMagnitude 프로퍼티는 크기 값을 제곱을 제공하고 크기처럼 계산되지만 시간 소모적인 제곱근 연산이 불필요합니다. 크기를 알려진 거리에 비교하지 않고 크기의 제곱을 거리의 제곱에 비교할 수 있습니다.
if (heading.sqrMagnitude < maxRange * maxRange) {
// Target is within range.
}
그러면 실제 크기를 비교에 사용하는 것보다 훨씬 더 효율적입니다.
때로는 3D 작업 시 타겟을 향하는 “지상 방향”이 필요할 수 있습니다. 예를 들어 땅 위에 서있는 플레이어가 공중에 떠있는 타겟에 접근해야 하는 경우를 상상해 보아야 합니다. 플레이어의 포지션을 타겟의 포지션에서 빼면 결과 벡터가 타겟 방향으로 위쪽을 향합니다. 이 벡터는 플레이어의 트랜스폼 방향을 지정하는 데 적합하지 않습니다. 이 역시 위쪽을 향하기 때문입니다. 실제로 필요한 것은 플레이어의 포지션에서 타겟 바로 밑에 있는 지상 포지션까지의 벡터입니다. 이 벡터는 뺄셈 결과를 사용하고 Y 좌표를 0으로 설정하여 쉽게 얻을 수 있습니다.
var heading = target.position - player.position;
heading.y = 0; // This is the overground heading.
벡터를 논할 때, 일반적으로 보통의 수(예: 플로트 값)를 스칼라라고 부릅니다. 스칼라라는 말의 의미는 “스케일” 또는 크기만을 가진다는 의미이고 한편 벡터는 크기와 방향을 모두 가집니다.
벡터에 스칼라를 곱하면 그 결과는 오리지널과 동일한 방향을 가리키는 벡터가 됩니다. 그러나 새 벡터의 크기는 오리지널 벡터의 크기에 해당 스칼라 값을 곱한 값과 동일합니다.
마찬가지로 스칼라 나눗셈은 오리지널 벡터의 크기를 스칼라로 나누는 것입니다.
이러한 연산은 벡터가 이동 오프셋 또는 힘을 나타낼 때 유용합니다. 이 방식을 통해 방향에는 영향을 주지 않으면서 어떤 벡터의 크기를 변경할 수 있습니다.
어떤 벡터를 자신의 크기로 나누면 그 결과는 크기가 1인 벡터가 되며, 이 벡터를 노멀라이즈 벡터라 합니다. 노멀라이즈 벡터에 스칼라를 곱하면 결과 벡터의 크기는 해당 스칼라 값과 같아집니다. 힘의 방향은 일정하지만 강도를 제어할 수 있는 경우에 유용합니다(예: 자동차 바퀴의 힘은 항상 앞 방향으로 차를 움직이지만 그 동력은 운전자가 제어합니다).
내적 연산은 두 벡터를 받아 스칼라를 반환합니다. 이 스칼라는 두 벡터의 크기를 곱하고 그 결과에 두 벡터 사이의 각에 대한 코사인을 곱한 것과 같습니다. 두 벡터 모두 노멀라이즈 벡터일 경우 코사인은 근본적으로 첫 번째 벡터가 두 번째 벡터의 방향으로 얼마나 기울었는지를 나타냅니다(또는 그 반대도 성립합니다. 즉 파라미터의 순서는 상관이 없습니다).
아래에서 레퍼런스 벡터와 비교한 다양한 각도의 벡터가 1과 –1 사이의 내적 값을 반환하는 방식을 확인할 수 있습니다.
내적은 코사인 계산보다 더 간단한 수학 연산으로, 일부 상황에서는 Mathf.Cos 함수 또는 벡터 크기 연산을 대신하여 사용할 수 있습니다(정확히 동일한 일을 하지는 않지만 때로는 얻을 수 있는 효과가 동일합니다). 그러나 내적 함수 연산은 CPU 시간이 훨씬 적게 걸리며 따라서 유용한 최적화 방법이 될 수 있습니다.
내적은 다른 벡터의 방향에 있는 한 벡터의 크기를 계산하려는 경우에 유용합니다.
예를 들어 자동차 속도계는 일반적으로 바퀴의 회전 속도를 측정하여 표시합니다. 간혹 자동차는 전진하지 않을 수도 있는데(옆으로 미끄러질 수도 있으므로), 이 경우 해당 운동의 일부는 자동차가 향하는 방향이 아니므로 속도계가 측정할 수 없습니다. 오브젝트의 rigidbody.velocity 벡터의 크기는 전체 운동의 해당 방향에 대한 속도를 알려주므로 전진 속도만을 알고 싶은 경우 벡터 내적을 사용해야 합니다.
var fwdSpeed = Vector3.Dot(rigidbody.velocity, transform.forward);
물론 방향은 어떤 방향이든 무관하지만, 이 연산을 하려면 방향 벡터를 정규화해야 합니다. 결과가 속도 크기를 더 정확하게 측정할 수 있을 뿐만 아니라, 크기를 계산하는 동안 느린 제곱근 연산을 피할 수 있도록 하기 때문입니다.
외적은 3D 벡터에만 해당됩니다. 두 개의 3D 벡터를 입력으로 사용하고, 그 결과로 다른 3D 벡터를 반환합니다.
결과 벡터는 두 입력 벡터에 수직입니다. 입력 벡터의 배치에 따라 나오는 결과 벡터의 방향을 기억하기 위해 “왼손 법칙”을 사용할 수 있습니다. 첫 번째 파라미터가 왼손 엄지에 해당하고 두 번째 파라미터가 검지에 해당할 때, 결과 벡터는 중지 방향을 향합니다. 파라미터의 순서가 바뀌면 결과 벡터는 같은 크기이면서 정반대 방향을 가리키게 됩니다.
결과 벡터의 크기는 입력 벡터들의 크기를 곱하고 그 결과에 두 벡터 사이각의 사인을 곱한 것과 같습니다. 사인 함수에서 자주 사용되는 값은 아래와 같습니다.
외적은 결과 값에 여러 유용한 정보를 합쳐 놓은 것이므로 복잡해 보일 수도 있습니다. 그러나 내적과 마찬가지로 외적은 수학적으로 매우 효율적이며, 느린 초월 함수(예: 사인 및 코사인)를 써야 했을 상황에서 코드 최적화를 할 때 유용하게 사용할 수 있습니다.
“노멀” 벡터(즉 평면에 수직인 벡터)는 메시 생성 중에 자주 필요하며 경로 추적 및 다른 상황에서도 유용합니다. 평면의 세 점(예: 메시 삼각형의 코너 점)이 주어질 경우 다음과 같은 방식으로 노멀을 찾을 수 있습니다. - 세 점 중 아무 점이나 하나를 선택합니다. - 이 점을 다른 두 점에서 각각 뺍니다. 그러면 두 개의 새로운 벡터(“Side 1”과 “Side 2”)가 생성됩니다. - 벡터 “Side 1”과 “Side 2”의 외적을 계산합니다. - 외적의 결과는 세 개의 원래 점이 있는 평면에 수직인 새로운 벡터, 즉 “노멀”입니다.
Vector3 a;
Vector3 b;
Vector3 c;
Vector3 side1 = b - a;
Vector3 side2 = c - a;
Vector3 normal = Vector3.Cross(side1, side2);
“왼손 규칙”은 두 벡터를 외적 함수에 전달해야 하는 순서를 결정하는 데 사용할 수 있습니다. 표면의 윗면을 내려다볼 때(즉 노멀이 가리키며 뻗어나가는 쪽에서 평면 방향을 보면) 첫 번째 벡터는 시계 방향으로 두 번째 벡터로 돌아야 합니다.
입력 벡터의 순서가 바뀌면 결과는 정확히 반대 방향을 가리킵니다.
메시의 경우 노멀 벡터도 정규화되어야 합니다. 정규화된 프로퍼티를 사용하여 이 작업을 수행할 수 있지만 때때로 유용한 또 다른 트릭이 있습니다. 직각 벡터를 자체 크기로 나눠 정규화할 수도 있습니다.
float perpLength = perp.magnitude;
perp /= perpLength;
삼각형의 영역은 perpLength / 2와 동일하다는 점에 유의하십시오. 메시의 전체 표면 영역을 알고 싶을 때, 또는 상대 영역을 기반으로 삼각형을 임의의 확률로 선택하려는 경우에 유용합니다.