벡터는 위치가 아니라 화살표다
Esc

    벡터는 위치가 아니라 화살표다

    –Unity기초

    적을 향해 총알을 쏘려고 총알 속도에 적의 위치를 그대로 넣으면, 총알이 엉뚱한 쪽으로 날아갑니다. 적이 왼쪽에 있는데 오른쪽 위로 날아가기도 합니다. 속도는 1초에 어느 쪽으로 얼마나 가는지라서 방향이 들어 있습니다. enemy.position 도 Vector3 이고 총알 속도도 Vector3 라서 컴파일은 아무 문제 없이 됩니다.

    같은 Vector3 타입에 위치와 방향이라는 서로 다른 것이 담기기 때문입니다. 위치는 “어디에 있나” 이고, 방향은 “어디로 가나” 입니다.

    벡터는 시작점 없이 방향과 길이만 가진 화살표입니다. 위치는 원점에서 그 자리까지 그린 화살표로 읽은 것이고, 나에서 적으로 가는 방향은 적의 위치 − 나의 위치 로 구합니다.

    자리는 숫자 두 개로 부른다

    가로줄 하나와 세로줄 하나를 직각으로 그으면 자리를 숫자로 부를 수 있습니다. 가로줄을 x 축, 세로줄을 y 축, 둘이 만나는 기준 자리를 원점이라고 부르고 원점은 (0, 0) 으로 씁니다. 좌표 (x, y) 는 원점에서 오른쪽으로 x, 위로 y 만큼 간 자리입니다. 음수면 왼쪽, 아래쪽입니다.

    Vector3 는 숫자 세 개다

    Vector3 를 열어 보면 float 세 개뿐입니다.

    public struct Vector3
    {
        public float x;
        public float y;
        public float z;
    }

    숫자 세 개 자체에는 뜻이 없습니다. 이 셋을 어떻게 읽느냐에 따라 둘 중 하나가 됩니다.

    읽는 법 뜻 유니티에서
    점 공간의 한 자리 transform.position
    벡터 방향과 길이 transform.forward(오브젝트의 앞쪽), rb.linearVelocity(Rigidbody 의 속도)

    이 글에서는 그림을 그리기 쉽게 z 를 빼고 Vector2 처럼 (x, y) 두 개로 봅니다. 셋이 되어도 계산은 똑같습니다.

    벡터에는 시작점이 없다

    벡터 (3, 4) 는 “오른쪽으로 3, 위로 4” 라는 뜻입니다. 어디서 출발하는지는 들어 있지 않습니다. 그래서 화살표를 어디에 그려도, 방향과 길이만 같으면 같은 벡터입니다.

    화살표의 길이는 시작에서 끝까지 곧장 잰 거리입니다. 가로 3 과 세로 4 가 직각으로 만나니, 화살표는 직각삼각형(한 각이 90° 인 삼각형)의 비스듬한 변, 빗변입니다. 빗변의 길이는 피타고라스의 정리로 구합니다. “빗변을 제곱한 값 = 가로를 제곱한 값 + 세로를 제곱한 값” 입니다.

    • 제곱은 같은 수를 두 번 곱하는 것입니다. 3² = 3 × 3 = 9, 4² = 4 × 4 = 16
    • 그러면 빗변² = 9 + 16 = 25 입니다
    • 제곱근(√, 루트) 은 두 번 곱해서 그 수가 되는 수입니다. 5 × 5 = 25 라서 √25 = 5
    • 그래서 (3, 4) 의 길이는 √(3² + 4²) = √25 = 5 입니다

    유니티에서는 v.magnitude 가 이 값을 돌려줍니다. 길이는 방향이 없는 숫자 하나입니다. 거리 5, 빠르기 10 처럼 방향 없이 크기만 있는 보통 숫자를 스칼라라고 부릅니다.

    그러면 위치는 무엇인가. 위치 (5, 1) 은 “원점에서 오른쪽으로 5, 위로 1” 입니다. 원점이라는 시작점을 정해 놓고 벡터를 읽은 것이 위치입니다. 유니티의 transform.position 은 월드 원점 (0, 0, 0) 에서 그 오브젝트까지의 화살표입니다.

    나에서 적으로 가는 방향은 뺄셈이다

    나는 (5, 1) 에, 적은 (2, 5) 에 서 있습니다. 둘 다 원점에서 잰 위치입니다. 필요한 것은 나에서 출발해 적에 닿는 화살표입니다.

    같은 자리끼리 빼면 됩니다. 도착점 − 출발점입니다.

    x y
    적 (도착) 2 5
    나 (출발) 5 1
    적 − 나 2 − 5 = −3 5 − 1 = 4

    (−3, 4) 는 “왼쪽으로 3, 위로 4” 입니다. 나에서 이만큼 가면 적에 닿습니다.

    코드로 쓰면 이렇습니다.

    // 나에서 적으로 가는 화살표. 도착점 − 출발점
    Vector3 toEnemy = enemy.position - transform.position;
    
    // 적까지의 거리는 그 화살표의 길이
    float distance = toEnemy.magnitude;

    빼는 순서가 곧 방향입니다. 순서를 바꾸면 화살표가 정반대를 가리킵니다.

    식 화살표 쓰는 곳
    target - me 나 → 목표 쫓아가기, 조준, 바라보기
    me - target 목표 → 나 도망가기, 밀려나기(넉백)

    헷갈리면 “도착 − 출발” 만 기억하면 됩니다. (−3, 4) 의 길이는 √(9 + 16) = 5 라서, 나와 적 사이의 거리도 5 입니다. Vector3.Distance(a, b) 가 바로 (b - a).magnitude 입니다.

    점과 벡터로 할 수 있는 계산

    같은 Vector3 라도 점과 벡터를 섞으면 어떤 계산은 뜻이 있고 어떤 계산은 뜻이 없습니다.

    계산 결과 뜻
    점 − 점 벡터 한 자리에서 다른 자리로 가는 화살표
    점 + 벡터 점 그 자리에서 화살표만큼 간 자리. 이동
    벡터 + 벡터 벡터 두 화살표를 이어 붙인 것
    점 + 점 뜻 없음 (5, 1) + (2, 5) = (7, 6) 은 아무 자리도 아닙니다

    transform.position += velocity 가 점 + 벡터 입니다. 그래서 결과가 다시 위치가 됩니다. 총알 속도에 적의 위치를 넣는 실수는 점을 벡터 자리에 넣은 것입니다. 타입이 같아서 컴파일러는 잡아 주지 못합니다.

    그런데 왜 유니티는 둘을 한 타입에 담았을까요. 점도 벡터도 숫자 세 개이고, 더하기와 빼기를 하는 방법이 똑같기 때문입니다. 타입이 하나면 연산자와 함수를 한 벌만 두면 되고, 점 − 점 처럼 둘이 섞이는 계산도 형 변환 없이 바로 씁니다. 대신 치르는 값이 위의 실수입니다. 어느 쪽으로 읽을지는 타입이 아니라 변수 이름과 쓰는 사람이 기억해야 합니다. 그래서 targetPos, moveDir 처럼 이름에 위치인지 방향인지를 드러내 두는 편이 안전합니다.

    정리

    • Vector3 는 숫자 세 개입니다. 위치로 읽으면 점, 방향과 길이로 읽으면 벡터입니다
    • 벡터는 시작점이 없는 화살표입니다. 어디에 그려도 방향과 길이가 같으면 같은 벡터입니다
    • 위치는 원점에서 그 자리까지의 화살표입니다
    • 두 위치 사이의 방향은 도착 − 출발 입니다. 순서를 바꾸면 반대 방향입니다
    • 그 화살표의 길이가 두 위치 사이의 거리입니다
    • 점 − 점 = 벡터, 점 + 벡터 = 점. 점 + 점은 뜻이 없습니다
    • 위치를 방향 자리에 넣으면 원점 기준의 엉뚱한 방향이 됩니다. 타입이 같아서 컴파일러가 못 잡습니다
    • 길이는 피타고라스로 √(x² + y²) 이고, magnitude 가 돌려줍니다