로컬 좌표와 월드 좌표
Esc

    로컬 좌표와 월드 좌표

    –Unity기초

    캐릭터 오른쪽 앞에 무기를 붙이려고 weapon.position = new Vector3(1, 0, 1) 이라고 썼더니, 무기가 캐릭터 옆이 아니라 맵 원점 근처에 생깁니다. 원점은 좌표를 재기 시작하는 자리로, 월드에서는 (0, 0, 0) 입니다.

    어느 축으로 잰 숫자인가의 문제입니다. 축은 숫자를 재는 방향으로, 오른쪽 · 위 · 앞을 가리키는 길이 1 인 화살표 세 개입니다. 월드 원점과 월드 축으로 잰 숫자가 월드 좌표이고, 오브젝트의 위치를 원점으로, 오브젝트의 오른쪽 · 위 · 앞을 축으로 잰 숫자가 로컬 좌표입니다. 같은 (1, 0, 1) 이라도 읽는 기준에 따라 다른 자리입니다.

    로컬 (a, b) 의 월드 자리는 오브젝트 위치 + a × 오른쪽 축 + b × 위 축 입니다. TransformPoint 가 이 계산입니다. 거꾸로 월드 자리의 로컬 숫자는, 위치를 뺀 뒤 각 축과 내적해서 구합니다. InverseTransformPoint 가 이 계산입니다.

    좌표 숫자는 축을 몇 개씩 섞을지다

    벡터 (2, 1) 은 “x 축 방향으로 2, y 축 방향으로 1” 입니다. 곧 2 × x 축 + 1 × y 축 입니다. 월드 축은 Vector3.right (1, 0, 0), Vector3.up (0, 1, 0), Vector3.forward (0, 0, 1) 로 늘 같습니다.

    오브젝트도 자기 축을 들고 있습니다. transform.right, transform.up, transform.forward 입니다. 오브젝트가 돌면 이 세 축도 같이 돕니다. 셋 다 길이가 1 이고 서로 수직, 곧 직각(90°)으로 만납니다. 원점 하나와 이렇게 만나는 축 세트를 묶은 것을 좌표계, 그 축 세트를 기저라고 부릅니다. 이 세 축이 오브젝트의 기저입니다.

    원점 축 오브젝트가 돌면
    월드 (0, 0, 0) Vector3.right · up · forward 그대로
    로컬 오브젝트의 위치 transform.right · up · forward 같이 돈다

    로컬에서 월드로: 축을 섞고 위치를 더한다

    2D 로 줄인 예입니다. 오브젝트가 (3, 1) 에 있고, 조금 돌아 서 있어서 오른쪽 축이 (0.8, 0.6), 위 축이 (−0.6, 0.8) 입니다.

    길이는 가로와 세로를 각각 제곱해 더한 뒤 √ 를 씌워 잽니다(피타고라스). (0.8, 0.6) 은 √(0.64 + 0.36) = √1 = 1 이라 길이 1 이고, (−0.6, 0.8) 도 같습니다. 두 축이 수직인지는 내적, 곧 x 끼리, y 끼리 곱해서 더한 숫자로 확인합니다. 수직인 두 방향은 내적이 0 입니다. 0.8 × (−0.6) + 0.6 × 0.8 = 0 이라 서로 수직입니다.

    이 오브젝트 기준의 로컬 점 (2, 1) 은 월드의 어디일까요.

    단계 계산 결과
    1. 오른쪽 축 2 개 2 × (0.8, 0.6) (1.6, 1.2)
    2. 위 축 1 개 1 × (−0.6, 0.8) (−0.6, 0.8)
    3. 둘을 더함 (1.6 − 0.6, 1.2 + 0.8) (1.0, 2.0)
    4. 위치를 더함 (3 + 1.0, 1 + 2.0) (4, 3)

    이 계산은 로컬 숫자만큼 오브젝트의 축을 섞고, 오브젝트 위치를 더한 것입니다. transform.TransformPoint(local) 이 그대로 이 일을 합니다. 오브젝트의 Scale 이 1 이 아니면 로컬 숫자에 Scale 을 먼저 곱합니다. Scale 이 (2, 2) 면 로컬 (2, 1) 은 오른쪽 축 4 칸, 위 축 2 칸입니다.

    무기를 캐릭터 오른쪽 앞에 붙이려던 코드는 이렇게 고칩니다.

    // 캐릭터 기준 오른쪽 1, 앞 1
    Vector3 local = new Vector3(1f, 0f, 1f);
    
    weapon.position = character.TransformPoint(local);

    월드에서 로컬로: 위치를 빼고 축과 내적한다

    반대 방향도 자주 필요합니다. “이 적이 내 기준으로 앞에 있나, 오른쪽에 있나” 를 숫자로 알고 싶을 때입니다. 앞의 월드 (4, 3) 을 오브젝트 기준 숫자로 되돌리면 다시 (2, 1) 이 나와야 합니다.

    길이 1 인 축과의 내적은 그 축 위에 내린 그림자의 길이입니다. 축이 서로 수직이라서, 각 축과의 내적이 곧 그 축으로 몇 칸인지가 됩니다. (1, 2) 는 2 × right + 1 × up 입니다. 이것을 right 와 내적하면 2 × (right · right) + 1 × (up · right) = 2 × 1 + 1 × 0 = 2 입니다. 길이 1 인 축은 자기와의 내적이 1 이고, 서로 수직인 축끼리의 내적은 0 이라 다른 축의 몫이 지워지고 그 축의 몫만 남습니다.

    단계 계산 결과
    1. 위치를 뺌 (4, 3) − (3, 1) (1, 2)
    2. 오른쪽 축과 내적 1 × 0.8 + 2 × 0.6 2
    3. 위 축과 내적 1 × (−0.6) + 2 × 0.8 1

    transform.InverseTransformPoint(world) 가 이 계산입니다. Scale 이 1 이 아니면 내적한 값을 마지막에 Scale 로 나눕니다.

    Vector3 local = transform.InverseTransformPoint(enemy.position);
    
    // local.x > 0 이면 내 오른쪽, local.z > 0 이면 내 앞
    bool isFrontRight = local.x > 0f && local.z > 0f;

    로컬 좌표로 바꾸면 앞뒤(z), 좌우(x), 위아래(y)가 부호로 바로 보입니다. 축 하나하나와 따로 내적해서 판정하던 것을 한 번에 하는 셈입니다.

    점 · 방향 · 벡터를 옮기는 함수가 셋인 이유

    로컬에서 월드로 바꾸는 함수는 셋입니다. 위치를 더하는지, 크기를 곱하는지가 다릅니다.

    함수 위치를 더함 크기를 곱함 넣는 것
    TransformPoint 예 예 자리. 무기를 붙일 곳
    TransformVector 아니요 예 크기를 따라야 하는 길이
    TransformDirection 아니요 아니요 방향. 총알이 날아갈 쪽

    방향에는 시작점이 없으니 위치를 더하지 않고, 길이 1 인 방향이 커지면 안 되니 크기도 곱하지 않습니다. 셋 다 회전은 적용합니다. (1, 1) 에 있고 돌지 않았으며 Scale 이 2 인 오브젝트에 로컬 (1, 1) 을 넣으면 셋의 결과가 다릅니다. TransformPoint 는 (3, 3), TransformVector 는 (2, 2), TransformDirection 은 (1, 1) 입니다.

    되돌리는 쪽도 InverseTransformPoint, InverseTransformVector, InverseTransformDirection 셋이 짝을 이룹니다.

    position 과 localPosition, Translate 와 +=

    같은 생각이 Transform 의 다른 곳에도 그대로 있습니다. 아래에 나오는 부모와 자식은 Hierarchy 관계입니다. 다른 오브젝트 아래에 놓인 오브젝트가 자식, 그 위의 것이 부모이고, 부모가 움직이면 자식이 따라 움직입니다.

    코드 기준
    transform.position 월드
    transform.localPosition 부모 기준 로컬. 부모가 없으면 position 과 같음
    transform.forward 월드로 나타낸 내 앞 방향
    Vector3.forward 월드의 앞 (0, 0, 1). 내가 어디를 보든 같음
    transform.Translate(v) 기본이 나 기준(Space.Self). v 를 내 축으로 섞어서 감
    transform.Translate(v, Space.World) 월드 기준
    transform.position += v 월드 기준

    그래서 transform.Translate(Vector3.forward) 는 내가 보는 쪽으로 갑니다. transform.position += Vector3.forward 는 내가 어디를 보든 늘 월드 앞쪽으로 갑니다. transform.position += transform.forward 라고 쓰면 Translate 와 같은 결과입니다. transform.forward 가 이미 내 앞을 월드 숫자로 바꿔 둔 값이기 때문입니다.

    localPosition 의 기준은 자기 자신이 아니라 부모라는 점도 헷갈리기 쉽습니다. 자식의 localPosition (1, 0, 0) 은 “부모의 오른쪽 축으로 1” 이고, 부모의 Scale 이 2 면 월드로는 2 칸 떨어집니다.

    정리

    • 좌표 숫자는 축을 몇 개씩 섞을지입니다. 월드는 월드 축으로, 로컬은 오브젝트의 축(기저)으로 잽니다
    • 로컬 → 월드는 위치 + x × right + y × up + z × forward 입니다(크기가 있으면 먼저 곱함). TransformPoint
    • 월드 → 로컬은 위치를 빼고 각 축과 내적합니다. 축이 서로 수직이고 길이 1 이라 내적이 곧 칸 수입니다. InverseTransformPoint
    • 로컬 좌표로 바꾸면 앞뒤 · 좌우 · 위아래가 z · x · y 의 부호로 보입니다
    • 점은 TransformPoint, 크기를 따르는 길이는 TransformVector, 방향은 TransformDirection 으로 옮깁니다
    • Translate 는 기본이 나 기준, position += 는 월드 기준입니다. localPosition 은 부모 기준입니다