벡터 더하기는 화살표를 이어 붙이는 것이다
Esc

    벡터 더하기는 화살표를 이어 붙이는 것이다

    –Unity기초

    캐릭터가 오른쪽으로 걷고 있는데 위쪽에서 바람이 불고, 그 순간 적에게 맞아 뒤로 밀려나기까지 합니다. 움직임이 셋인데 캐릭터의 위치는 하나입니다. 셋을 어떻게 합쳐야 할까요.

    각 움직임을 벡터 하나로 두고 더하면 됩니다. 벡터 더하기는 x 는 x 끼리, y 는 y 끼리 더하는 것이고, 그림으로는 화살표를 끝에 이어 붙이는 것입니다. 움직임을 빠르게 하거나 느리게 하는 것은 벡터에 숫자를 곱해서 합니다. 방향은 그대로 두고 길이만 바뀝니다.

    벡터 + 벡터는 같은 자리끼리 더하기이고, 화살표를 이어 붙인 끝이 결과입니다. 벡터 × 숫자는 같은 방향으로 길이만 그 배가 됩니다. 음수를 곱하면 방향이 뒤집힙니다. 유니티의 이동 코드는 거의 전부 이 두 가지로 되어 있습니다.

    더하기는 같은 자리끼리

    벡터는 방향과 길이만 가진 화살표입니다. (3, 1) 은 “오른쪽으로 3, 위로 1” 입니다. 괄호 안의 숫자 하나하나를 성분이라고 부릅니다. (3, 1) 의 x 성분은 3, y 성분은 1 입니다. 두 벡터 a = (3, 1), b = (1, 2) 를 더하면 이렇게 됩니다. x 성분은 x 성분끼리, y 성분은 y 성분끼리 더합니다.

    x y
    a 3 1
    b 1 2
    a + b 3 + 1 = 4 1 + 2 = 3

    그림으로 보면 a 를 먼저 가고, a 가 끝난 자리에서 b 를 이어서 간 것입니다.

    왜 이어 붙이는 것과 같은 자리끼리 더하는 것이 같은지는 가로와 세로를 따로 보면 보입니다. a 로 가로 3 을 가고 b 로 가로 1 을 더 갔으니 가로로는 모두 4 를 간 것입니다. 세로도 1 과 2 를 갔으니 3 입니다. 화살표를 이어 붙이는 것은 가로로 간 양과 세로로 간 양을 각각 모으는 일입니다.

    이동은 위치에 벡터를 계속 더하는 것

    위치는 원점(좌표의 기준 자리 (0, 0))에서 그 자리까지의 화살표로 볼 수 있습니다. 그래서 위치에 벡터를 더하면 화살표만큼 간 새 위치가 나옵니다. 캐릭터를 움직이는 코드는 매 프레임 이 더하기를 합니다.

    걷기가 프레임마다 (1, 0), 바람이 프레임마다 (0, 0.5) 이고, (0, 0) 에서 출발해 세 프레임을 더합니다. (실제 코드는 프레임 시간까지 곱하지만, 여기서는 더하기만 보려고 한 프레임에 한 번씩 그대로 더합니다.)

    위치 + 벡터 = 새 위치라는 것과 더하는 순서는 상관없다는 것, 이 두 가지가 합쳐진 결과입니다. 넉백이 들어오면 그 벡터를 하나 더 더하면 됩니다. 움직임마다 코드를 따로 고칠 필요가 없습니다.

    몇 배 하기는 길이만 바꾼다

    벡터에 숫자 하나를 곱하면 x 와 y 에 똑같이 곱합니다. 이것을 스칼라 곱이라고 부릅니다. 스칼라는 방향 없이 크기만 있는 보통 숫자라는 뜻입니다.

    곱한 수 (2, 1) × 수 화살표
    2 (4, 2) 같은 방향, 길이 두 배
    0.5 (1, 0.5) 같은 방향, 길이 절반
    −1 (−2, −1) 길이 그대로, 방향 정반대
    0 (0, 0) 길이 0. 방향이 사라짐

    방향이 그대로인 이유는 x 와 y 의 비율에 있습니다. (2, 1) 은 가로 2 칸에 세로 1 칸, 곧 가로로 2 갈 때마다 위로 1 오르는 기울기입니다. (4, 2) 도 가로 4 에 세로 2 라서 기울기가 같습니다. 두 성분에 같은 수를 곱하니 비율이 깨지지 않습니다. 길이가 두 배인 것은 더하기로 확인됩니다. (2, 1) × 2 는 (2, 1) + (2, 1) 이고, 같은 화살표를 두 번 이어 붙인 것이니 길이도 두 배입니다.

    그래서 이동 코드는 방향과 빠르기를 나눠서 들고 있다가 곱합니다.

    Vector3 direction = Vector3.right;   // 어느 쪽으로
    float speed = 4f;                     // 얼마나 빨리
    
    Vector3 velocity = direction * speed; // (4, 0, 0)

    방향을 바꾸고 싶으면 direction 만, 빠르기를 바꾸고 싶으면 speed 만 고치면 됩니다. 뒤로 밀려나는 넉백은 바라보는 방향에 음수를 곱해 만듭니다. transform.forward * -3f 는 뒤쪽으로 3 입니다.

    빼기는 몇 배 하기와 더하기를 합친 것입니다. a − b 는 b 에 −1 을 곱해 뒤집은 다음 더한 것, 곧 a + (−1)b 입니다. 그래서 더하기와 달리 빼는 순서를 바꾸면 결과가 정반대가 됩니다.

    a = (3, 1), b = (1, 2) 면 a − b = (3 − 1, 1 − 2) = (2, −1) 입니다. b 의 자리 (1, 2) 에서 (2, −1) 을 이어 붙이면 (3, 1), 곧 a 에 닿습니다. a − b 는 b 에서 a 로 가는 화살표입니다. 두 위치 사이의 방향을 구할 때 “도착 − 출발” 로 빼는 이유가 이것입니다.

    두 축을 섞어서 방향을 만든다

    캐릭터가 돌아 서 있으면 “캐릭터 기준 앞” 과 “캐릭터 기준 오른쪽” 은 월드의 x, z 축(바닥의 가로와 세로)과 다릅니다. 유니티는 캐릭터 기준의 축을 transform.right(캐릭터의 오른쪽) 와 transform.forward(캐릭터의 앞) 로 줍니다. 둘 다 길이가 1 인 벡터라서, 몇 배를 하면 그 숫자가 그대로 길이가 됩니다.

    키 입력이 오른쪽 1, 앞 2 라면 오른쪽 축을 1배, 앞 축을 2배 해서 더한 것이 이동 방향입니다. 캐릭터가 돌아 있어서 오른쪽이 (0.8, −0.6), 앞이 (0.6, 0.8) 이라고 합시다. 위에서 내려다본 그림이라 가로가 x, 세로가 z 입니다.

    x z
    1 × right 1 × 0.8 = 0.8 1 × (−0.6) = −0.6
    2 × forward 2 × 0.6 = 1.2 2 × 0.8 = 1.6
    더하면 0.8 + 1.2 = 2.0 −0.6 + 1.6 = 1.0

    코드로 쓰면 한 줄입니다.

    float h = Input.GetAxis("Horizontal");   // 오른쪽 입력
    float v = Input.GetAxis("Vertical");     // 앞 입력
    
    // 캐릭터 기준 축 두 개를 입력만큼 섞기
    Vector3 move = h * transform.right + v * transform.forward;

    이렇게 벡터 몇 개에 숫자를 곱해서 더한 것을 수학에서는 선형결합이라고 부릅니다. 1 × right + 2 × forward 가 선형결합 하나입니다. 서로 다른 쪽을 가리키는 축 두 개만 있으면, 곱하는 두 숫자를 바꿔서 평면 위의 어느 방향이든 만들 수 있습니다. 월드의 (2, 1) 도 사실 2 × (1, 0) + 1 × (0, 1), 곧 x 축과 y 축의 선형결합입니다.

    유니티에서 쓰는 벡터 연산

    코드 결과 뜻
    a + b 벡터 같은 자리끼리 더하기. 이어 붙이기
    a - b 벡터 b 에서 a 로 가는 화살표
    a * 2f 또는 2f * a 벡터 길이 두 배
    a / 2f 벡터 길이 절반
    -a 벡터 반대 방향
    a + 1f 컴파일 에러 벡터에 숫자 하나를 더하는 연산은 없습니다
    Vector3 끼리 a * b 컴파일 에러 Vector3 에는 벡터끼리 곱하는 * 가 없습니다
    Vector3.Scale(a, b) 벡터 같은 자리끼리 곱하기. (2, 3) 과 (4, 5) 면 (8, 15)
    Vector2 끼리 a * b 벡터 Vector2 는 * 가 같은 자리끼리 곱하기입니다. Scale 과 같습니다

    Vector3 에 벡터끼리 * 가 없는 이유는 “벡터끼리 곱한다” 가 한 가지로 정해지지 않기 때문입니다. 같은 자리끼리 곱하는 것(Scale), 곱해서 숫자 하나를 만드는 것(내적, Vector3.Dot), 수직인 벡터를 만드는 것(외적, Vector3.Cross)이 모두 쓰입니다. 그래서 Vector3 는 연산자 하나로 정하지 않고 이름을 붙인 함수로 나눠 두었습니다. Vector2 의 * 는 그중 같은 자리끼리 곱하기를 고른 것이라, 헷갈리지 않게 Scale 을 쓰는 편이 읽기 쉽습니다.

    조심할 것

    • 섞으면 길이가 변합니다. 앞 입력 1, 오른쪽 입력 1 을 섞으면 right + forward 의 길이는 1 이 아니라 √2 ≈ 1.41 입니다. right 와 forward 는 서로 수직이고 길이가 1 이라, 이어 붙이면 두 변이 1 과 1 인 직각삼각형(한 각이 90° 인 삼각형)이 됩니다. 직각삼각형의 비스듬한 변(빗변) 길이는 피타고라스의 정리로 √(가로² + 세로²) 입니다. ² 는 같은 수를 두 번 곱하기, √ 는 두 번 곱해 그 수가 되는 수입니다. 그래서 √(1 × 1 + 1 × 1) = √2 ≈ 1.41 입니다(1.41 × 1.41 ≈ 2). 대각선으로 갈 때만 41% 빨라지는 이유가 이것이고, 길이를 1 로 되돌리는 정규화(normalized)로 막습니다
    • 0 을 곱하면 방향이 사라집니다. 영벡터 (0, 0) 에서는 “어느 쪽” 을 되찾을 수 없습니다. 입력이 없을 때 이동 방향으로 캐릭터를 돌리는 코드는 영벡터인지 먼저 확인해야 합니다
    • 위치 + 위치는 뜻이 없습니다. 더해서 의미가 있는 것은 위치 + 벡터, 벡터 + 벡터입니다

    정리

    • 벡터 더하기는 x 는 x 끼리, y 는 y 끼리 더하는 것이고, 그림으로는 화살표를 끝에 이어 붙이는 것입니다
    • 더하는 순서는 상관없습니다. 빼는 순서는 결과를 뒤집습니다
    • 이동은 위치에 벡터를 계속 더하는 것입니다. 움직임이 여러 개면 먼저 더해서 한 번에 더해도 같습니다
    • 숫자를 곱하면 방향은 두고 길이만 그 배가 됩니다. 음수는 방향을 뒤집고, 0 은 영벡터를 만듭니다
    • 이동 코드는 방향(길이 1 인 벡터)과 빠르기(숫자)를 따로 들고 곱합니다
    • h * transform.right + v * transform.forward 처럼 축을 몇 배씩 섞는 것을 선형결합이라고 부릅니다
    • Vector3 끼리는 * 가 없습니다. 같은 자리끼리 곱하기는 Vector3.Scale 입니다