벡터

소개

이 튜토리얼은 게임 개발에 적용되는 선형대수에 대한 짧고 실용적인 소개입니다. 선형 대수란 벡터와 벡터 사용에 대한 학문입니다. 벡터는 2D 및 3D 개발 모두에서 많은 애플리케이션을 가지고 있으며 Godot는 이를 광범위하게 사용합니다. 벡터 수학에 대한 올바른 이해는 강력한 게임 개발자가 되기 위해 필수적입니다.

주석

이 튜토리얼은 선형대수에 관한 공식 교과서가 절대 아닙니다. 우리는 단지 그것이 어떻게 게임 개발에 적용되는지 지켜볼 것입니다. 수학에 대한 자세한 내용은 https://www.khanacademy.org/math/linear-algebra을 참조하십시오

좌표계 (2차원)

2차원 공간에서, 좌표계들은 가로축(x)과 세로축(y)을 사요하며 정의됩니다. 2차원 공간에서의 특정한 위치는 (4,3)과 같이 한 쌍의 값으로 쓰여집니다.

../../_images/vector_axis1.png

주석

컴퓨터 그래픽을 처음 접하는 사람이라면 아마 수학 수업에서 배웠듯이 양의``y" 축이 위쪽이 아니라 **아래쪽**을 가리키는 것이 이상하게 보일지도 모릅니다. 그러나 대부분의 컴퓨터 그래픽 응용 프로그램에서는 일반적으로 이러한 현상이 발생합니다.

2D 평면의 모든 위치는 이러한 방식으로 한 쌍의 수들로 식별할 수 있습니다. 그러나 우리는 또한 (4, 3)의 위치를 (0, 0)으로 부터 상쇄되었다고 생각하거나 기원으로 생각할 수 있습니다. 원점에서 점까지 가리키는 화살표를 그리세요:

../../_images/vector_xy1.png

이것은 벡터입니다. 벡터는 유용한 많은 정보들을 보여줍니다. 점이 (4, 3)에 있다고 알려줄 뿐 아니라, 우리는 각도 'θ' 그리고 길이 (또는 크기) 'm' 또한 생각해낼 수 있습니다. 이러한 경우 화살표는 **위치 벡터**로, 원점에 상대적인 공간의 위치를 나타냅니다.

벡터에 대해 고려해야 할 매우 중요한 점은 벡터가 상대적인 방향과 크기만 나타낸다는 것입니다. 벡터의 위치에 대한 개념이 없습니다. 다음 두 벡터는 동일합니다:

../../_images/vector_xy2.png

두 벡터 모두 시작점으로 부터 오른쪽으로 4만큼 아랫쪽으로 3만큼 의 단위를 나타냅니다. 평면에서 벡터를 그리는 것은 중요하지 않습니다. 이것은 항상 상대적인 방향과 크기를 나타냅니다.

벡터 연산

You can use either method (x and y coordinates or angle and magnitude) to refer to a vector, but for convenience, programmers typically use the coordinate notation. For example, in Godot, the origin is the top-left corner of the screen, so to place a 2D node named Node2D 400 pixels to the right and 300 pixels down, use the following code:

$Node2D.position = Vector2(400, 300)
var node2D = (Node2D) GetNode("Node2D");
node2D.Position = new Vector2(400, 300);

Godot supports both Vector2 and Vector3 for 2D and 3D usage, respectively. The same mathematical rules discussed in this article apply to both types.

  • 멤버 접근

벡터의 각각의 구성요소는 이름으로 직접 접근할 수 있다.

# create a vector with coordinates (2, 5)
var a = Vector2(2, 5)
# create a vector and assign x and y manually
var b = Vector2()
b.x = 3
b.y = 1
// create a vector with coordinates (2, 5)
var a = new Vector2(2, 5);
// create a vector and assign x and y manually
var b = new Vector2();
b.x = 3;
b.y = 1;
  • 벡터의 합

두 벡터를 더하거나 뺼 떄, 해당하는 구성요소는 더해집니다:

var c = a + b  # (2, 5) + (3, 1) = (5, 6)
var c = a + b;  // (2, 5) + (3, 1) = (5, 6)

첫번째 벡터의 끝에 두번째 벡터를 더함으로써 우리는 시각적으로도 이것을 확인할 수 있습니다:

../../_images/vector_add1.png

즉 더하기 a+b는 b+a 와 같은 결과 값을 갖는다.

  • 스칼라 곱

주석

벡터는 방향과 크기 둘 다를 나타냅니다. 크기만 나타내고 있는 값을 **스칼라**라고 합니다.

벡터는 스칼라에 의해 곱해질 수 있습니다:

var c = a * 2  # (2, 5) * 2 = (4, 10)
var d = b / 3  # (3, 6) / 3 = (1, 2)
var c = a * 2;  // (2, 5) * 2 = (4, 10)
var d = b / 3;  // (3, 6) / 3 = (1, 2)
../../_images/vector_mult1.png

주석

벡터에 스칼라를 곱하는 것은 벡터의 방향을 바꾸지는 않고, 이것의 크기만 바꿉니다. 이것이 바로 벡터의 크기를 조정하는 방법입니다.

실용적인 응용

벡터 덧셈과 뺄셈의 두 가지 일반적인 용법을 살펴보자.

  • 이동

벡터는 크기와 방향을 가진 모든 양을 나타낼 수 있습니다. 대표적인 예 : 위치, 속도, 가속도, 힘 이 그림에서 1단계의 우주선은 위치 벡터 '(1,3)' 과 속도 벡터 '(2,1)'을 가지고 있습니다. 속도 벡터는 배가 각 단계를 이동하는 정도를 나타냅니다. 현재 위치에 속도를 더함으로써 2단계의 위치를 찾을 수 있습니다.

../../_images/vector_movement1.png

참고

속도는 시간 단위당 위치의 변화를 측정합니다. 새로운 위치는 이전 위치에 속도를 더함으로써 찾을 수 있습니다.

  • 표적을 향하기

이 시나리오에서, 당신은 회전포탑을 로봇에게 겨누고자 하는 탱크를 가지고 있다. 로봇의 위치에서 탱크의 위치를 빼면 벡터가 탱크에서 로봇으로 가리킬 수 있습니다.

../../_images/vector_subtract2.png

참고

"A"에서 "B"로 향하는 벡터를 찾으려면 "B - A"를 사용합니다.

단위 벡터

크기 가 1인 벡터를 단위 벡터 라고 합니다. 또한 이러한 벡터는 방향 벡터 또는 법선벡터 라고도 합니다. 단위 벡터는 방향을 추적해야 할 때 유용합니다.

표준화

벡터를 표준화 한다는 것의 의미는 벡터의 방향은 유지하면서 벡터의 길이를 1로 줄이는 것입니다. 이것은 각 벡터를 그것의 크기로 나누면서 이루어집니다:

var a = Vector2(2, 4)
var m = sqrt(a.x*a.x + a.y*a.y)  # get magnitude "m" using the Pythagorean theorem
a.x /= m
a.y /= m
var a = new Vector2(2, 4);
var m = Mathf.Sqrt(a.x*a.x + a.y*a.y);  // get magnitude "m" using the Pythagorean theorem
a.x /= m;
a.y /= m;

이것은 일반적인 계산이기 때문에, vector2vector3 는 표준화하는 방법을 제공합니다:

a = a.normalized()
a = a.Normalized();

경고

표준화는 벡터 길이로 나누어지기 때문에 길이 "0"의 벡터를 표준화할 수 없습니다. 이렇게 시도한다면 오류가 발생할 것입니다.

반사

단위벡터의 일반적인 용도는 법선 벡터 를 나타내는 것입니다. 법선 벡터는 표면에 수직으로 정렬된 단위 벡터를 말하며, 방향을 정의합니다. 이 장치는 일반적으로 조명, 충돌 및 표면과 관련된 기타 작업에 사용됩니다.

예를 들어, 우리가 벽이나 다른 물체에서 튀기길 원하는 움직이는 공을 가지고 있다고 상상해 보세요:

../../_images/vector_reflect1.png

The surface normal has a value of (0, -1) because this is a horizontal surface. When the ball collides, we take its remaining motion (the amount left over when it hits the surface) and reflect it using the normal. In Godot, the Vector2 class has a bounce() method to handle this. Here is a GDScript example of the diagram above using a KinematicBody2D:

# object "collision" contains information about the collision
var collision = move_and_collide(velocity * delta)
if collision:
    var reflect = collision.remainder.bounce(collision.normal)
    velocity = velocity.bounce(collision.normal)
    move_and_collide(reflect)
// KinematicCollision2D contains information about the collision
KinematicCollision2D collision = MoveAndCollide(_velocity * delta);
if (collision != null)
{
    var reflect = collision.Remainder.Bounce(collision.Normal);
    _velocity = _velocity.Bounce(collision.Normal);
    MoveAndCollide(reflect);
}

내적

내적 이란 벡터 수학에서 가장 중요한 개념 중 하나이지만 종종 잘못 이해됩니다. 내적은 두 벡터를 스칼라 로 반환해주는 연산입니다. 크기와 방향 모두를 포함하는 벡터와는 다르게 스칼라 값은 오직 크기만 갖습니다.

내적의 공식은 두가지 형식을 취한다:

A \cdot B = \left \| A \right \|\left \| B \right \|\cos \Theta

그리고

A·B = Ax Bx + Ay By

그러나, 대부분의 경우에서 기본 방법을 사용하는 것이 가장 쉽습니다. 두 벡터의 순서는 중요하지 않습니다:

var c = a.dot(b)
var d = b.dot(a)  # these are equivalent
float c = a.Dot(b);
float d = b.Dot(a);  // these are equivalent

내적은 첫 번째 공식을 cosθ로 줄이면서 단위벡터를 사용할 때 가장 유용합니다. 즉, 단위벡터를 사용하여 두 벡터 간의 각도에 대해 설명할 수 있습니다:

../../_images/vector_dot3.png

단위벡터를 사용할때, 결과는 항상 -1(180°)에서 1(0°) 사이일 것이다.

직면

우리는 이 사실을 어떤 물체가 다른 물체를 향하고 있는지 감지하는데에 이용할 수 있습니다. 아래 그림에서 P 선수는 좀비 AB 를 피하려고 합니다. 좀비의 시야가 180° 라고 가정하면 플레이어를 볼 수 있을까요?

../../_images/vector_facing2.png

녹색 화살표 fAfB 는 좀비들이 마주보는 방향을 나타내는 단위 벡터 이고 파란 반원형은 그것의 시야를 나타냅니다. 좀비 A 의 경우 방향 벡터인 AP``가 ``P - A 를 사용하여 선수를 가리키는 것을 발견해 법선벡터를 만듭니다. 이 벡터와 마주보는 벡터 사이의 각도가 90° 미만일 경우 좀비는 플레이어를 볼 수 있습니다.

코드에서는 이렇게 보일 것이다:

var AP = (P - A).normalized()
if AP.dot(fA) > 0:
    print("A sees P!")
var AP = (P - A).Normalized();
if (AP.Dot(fA) > 0)
{
    GD.Print("A sees P!");
}

외적

Like the dot product, the cross product is an operation on two vectors. However, the result of the cross product is a vector with a direction that is perpendicular to both. Its magnitude depends on their relative angle. If two vectors are parallel, the result of their cross product will be a null vector.

||a X b|| = ||a|| ||b|| |sin(a,b)|

../../_images/tutovec16.png

외적은 이렇게 계산된다:

var c = Vector3()
c.x = (a.y * b.z) - (a.z * b.y)
c.y = (a.z * b.x) - (a.x * b.z)
c.z = (a.x * b.y) - (a.y * b.x)
var c = new Vector3();
c.x = (a.y * b.z) - (a.z * b.y);
c.y = (a.z * b.x) - (a.x * b.z);
c.z = (a.x * b.y) - (a.y * b.x);

고도에서는, 기본 방법을 사용할 수 있습니다:

var c = a.cross(b)
var c = a.Cross(b);

주석

외적에서는, 순서가 중요합니다. "a.cross(b)"는 "b.cross(a)"와 같은 결가가 나오지 않습니다. 결과 벡터는 반대쪽 방향을 가리킵니다.

법선 계산하기

외적의 일반적인 용도는 3D 공간에서 표면 또는 평명의 법선 표면을 찾는 것입니다. 삼각형 "ABC"가 있다면 벡터 뺄셈을 사용하여 "AB"와 "AC"의 두 모서리를 찾을 수 있습니다. 외적을 이용하여 "AB x AC"는 두 가지 모두에 수직인 벡터를 생성합니다: 표면에 수직방향.

이것은 삼각형에 수직인 평면을 계산하는 함수이다:

func get_triangle_normal(a, b, c):
    # find the surface normal given 3 vertices
    var side1 = b - a
    var side2 = c - a
    var normal = side1.cross(side2)
    return normal
Vector3 GetTriangleNormal(Vector3 a, Vector3 b, Vector3 c)
{
    // find the surface normal given 3 vertices
    var side1 = b - a;
    var side2 = c - a;
    var normal = side1.Cross(side2);
    return normal;
}

대상 가리키기

In the dot product section above, we saw how it could be used to find the angle between two vectors. However, in 3D, this is not enough information. We also need to know what axis to rotate around. We can find that by calculating the cross product of the current facing direction and the target direction. The resulting perpendicular vector is the axis of rotation.

더 많은 정보

고도에서 벡터 수학을 이용하는 것에 대해 더 많은 정보를 원한다면, 다음 글들을 봐주십시오: