로컬 좌표계와 월드 좌표계

Unity에서 로컬 좌표계와 월드 좌표계의 차이를 학습합니다. transform.position, transform.localPosition, 좌표 변환 등을 다룹니다.

좌표계란?

좌표계는 오브젝트의 위치를 표현하는 기준입니다. Unity에서는 월드 좌표계로컬 좌표계 두 가지를 사용합니다. 이 둘의 차이를 이해하면 오브젝트를 정확하게 이동시키고 배치할 수 있습니다.

기본 개념

  • 월드 좌표계 (World Space): 씬 전체에서 절대적인 위치 (모든 오브젝트가 공유하는 기준)
  • 로컬 좌표계 (Local Space): 부모 오브젝트를 기준으로 한 상대적인 위치 (부모의 회전/이동에 영향받음)
  • 좌표 변환: 로컬 좌표 ↔ 월드 좌표 변환

월드 좌표계 (World Space)

transform.position

using UnityEngine;

public class WorldPosition : MonoBehaviour
{
    void Start()
    {
        // 월드 좌표계에서의 위치 (절대 위치)
        Vector3 worldPos = transform.position;
        
        Debug.Log($"월드 위치: {worldPos}");
        
        // 월드 좌표로 직접 설정
        transform.position = new Vector3(5, 0, 3);
    }
}

특징:

  • 씬 전체에서 절대적인 위치
  • 부모 오브젝트의 위치/회전과 무관
  • 모든 오브젝트가 같은 기준을 사용

월드 좌표계 예시

using UnityEngine;

public class WorldSpaceExample : MonoBehaviour
{
    public Transform target;
    
    void Update()
    {
        // 월드 좌표로 이동
        transform.position = new Vector3(0, 0, 0);  // 씬의 원점
        
        // 다른 오브젝트와의 월드 좌표 거리 계산
        if (target != null)
        {
            float distance = Vector3.Distance(transform.position, target.position);
            Debug.Log($"월드 좌표 거리: {distance}");
        }
    }
}

로컬 좌표계 (Local Space)

transform.localPosition

using UnityEngine;

public class LocalPosition : MonoBehaviour
{
    void Start()
    {
        // 로컬 좌표계에서의 위치 (부모 기준 상대 위치)
        Vector3 localPos = transform.localPosition;
        
        Debug.Log($"로컬 위치: {localPos}");
        
        // 로컬 좌표로 직접 설정
        transform.localPosition = new Vector3(1, 0, 0);
    }
}

특징:

  • 부모 오브젝트를 기준으로 한 상대적인 위치
  • 부모가 회전하면 로컬 좌표축도 함께 회전
  • 부모가 이동하면 로컬 좌표 원점도 함께 이동

로컬 좌표계 예시

using UnityEngine;

public class LocalSpaceExample : MonoBehaviour
{
    void Start()
    {
        // 부모를 기준으로 오른쪽으로 2 단위 이동
        transform.localPosition = new Vector3(2, 0, 0);
        
        // 부모가 회전하면 로컬 좌표축도 회전
        // 부모가 이동하면 로컬 좌표 원점도 이동
    }
}

월드 좌표 vs 로컬 좌표 비교

기본 비교

using UnityEngine;

public class CoordinateComparison : MonoBehaviour
{
    void Start()
    {
        // 월드 좌표 (절대 위치)
        Vector3 worldPos = transform.position;
        Debug.Log($"월드 좌표: {worldPos}");
        
        // 로컬 좌표 (부모 기준 상대 위치)
        Vector3 localPos = transform.localPosition;
        Debug.Log($"로컬 좌표: {localPos}");
        
        // 부모가 있으면 두 값이 다를 수 있음
        // 부모가 없으면 두 값이 같음
    }
}

부모가 있을 때의 차이

using UnityEngine;

public class ParentChildExample : MonoBehaviour
{
    public Transform parentObject;
    
    void Start()
    {
        // 자식 오브젝트 생성
        GameObject child = new GameObject("Child");
        child.transform.SetParent(parentObject);
        
        // 로컬 좌표로 설정 (부모 기준)
        child.transform.localPosition = new Vector3(1, 0, 0);
        
        // 부모가 (5, 0, 0)에 있으면
        // 자식의 월드 좌표는 (6, 0, 0)이 됨
        
        Debug.Log($"자식 로컬 좌표: {child.transform.localPosition}");
        Debug.Log($"자식 월드 좌표: {child.transform.position}");
    }
}

좌표 변환

로컬 → 월드 변환

using UnityEngine;

public class LocalToWorld : MonoBehaviour
{
    void Start()
    {
        // 로컬 좌표
        Vector3 localPos = new Vector3(1, 0, 0);
        
        // 로컬 좌표를 월드 좌표로 변환
        Vector3 worldPos = transform.TransformPoint(localPos);
        
        Debug.Log($"로컬 좌표: {localPos}");
        Debug.Log($"월드 좌표: {worldPos}");
    }
}

월드 → 로컬 변환

using UnityEngine;

public class WorldToLocal : MonoBehaviour
{
    void Start()
    {
        // 월드 좌표
        Vector3 worldPos = new Vector3(5, 0, 3);
        
        // 월드 좌표를 로컬 좌표로 변환
        Vector3 localPos = transform.InverseTransformPoint(worldPos);
        
        Debug.Log($"월드 좌표: {worldPos}");
        Debug.Log($"로컬 좌표: {localPos}");
    }
}

TransformPoint() vs InverseTransformPoint()

using UnityEngine;

public class TransformPointExample : MonoBehaviour
{
    void Start()
    {
        Vector3 localPos = new Vector3(1, 0, 0);
        
        // 로컬 → 월드 변환
        Vector3 worldPos = transform.TransformPoint(localPos);
        
        // 월드 → 로컬 변환 (역변환)
        Vector3 backToLocal = transform.InverseTransformPoint(worldPos);
        
        // 원래 값과 같아야 함
        Debug.Log($"원래 로컬: {localPos}");
        Debug.Log($"변환된 월드: {worldPos}");
        Debug.Log($"다시 로컬: {backToLocal}");
    }
}

방향 벡터 변환

TransformDirection() - 방향 벡터 변환

using UnityEngine;

public class TransformDirection : MonoBehaviour
{
    void Start()
    {
        // 로컬 방향 벡터 (위치가 아닌 방향)
        Vector3 localDirection = Vector3.forward;  // 로컬 Z축 방향
        
        // 로컬 방향을 월드 방향으로 변환
        Vector3 worldDirection = transform.TransformDirection(localDirection);
        
        Debug.Log($"로컬 방향: {localDirection}");
        Debug.Log($"월드 방향: {worldDirection}");
    }
}

InverseTransformDirection() - 역방향 변환

using UnityEngine;

public class InverseTransformDirection : MonoBehaviour
{
    void Start()
    {
        // 월드 방향 벡터
        Vector3 worldDirection = Vector3.forward;
        
        // 월드 방향을 로컬 방향으로 변환
        Vector3 localDirection = transform.InverseTransformDirection(worldDirection);
        
        Debug.Log($"월드 방향: {worldDirection}");
        Debug.Log($"로컬 방향: {localDirection}");
    }
}

실전 활용 예시

예시 1: 부모 기준으로 자식 배치

using UnityEngine;

public class ChildPlacement : MonoBehaviour
{
    public GameObject childPrefab;
    
    void Start()
    {
        // 자식 오브젝트 생성
        GameObject child = Instantiate(childPrefab);
        child.transform.SetParent(transform);
        
        // 부모를 기준으로 오른쪽으로 2 단위 배치 (로컬 좌표)
        child.transform.localPosition = new Vector3(2, 0, 0);
        
        // 부모가 회전/이동해도 자식은 부모 기준으로 유지됨
    }
}

예시 2: 월드 좌표로 절대 위치 설정

using UnityEngine;

public class AbsolutePosition : MonoBehaviour
{
    void Start()
    {
        // 씬의 특정 위치에 배치 (월드 좌표)
        transform.position = new Vector3(10, 0, 5);
        
        // 부모가 있어도 월드 좌표는 절대 위치
        // 부모가 이동해도 이 오브젝트는 (10, 0, 5)에 유지됨
    }
}

예시 3: 부모 회전에 따른 로컬 좌표 영향

using UnityEngine;

public class RotationEffect : MonoBehaviour
{
    public Transform parent;
    public Transform child;
    
    void Start()
    {
        // 자식을 부모의 자식으로 설정
        child.SetParent(parent);
        
        // 로컬 좌표로 배치 (부모 기준 오른쪽)
        child.localPosition = new Vector3(2, 0, 0);
        
        // 부모를 90도 회전
        parent.Rotate(0, 90, 0);
        
        // 자식의 로컬 좌표는 변하지 않지만
        // 월드 좌표는 부모의 회전에 따라 변경됨
        Debug.Log($"자식 로컬 좌표: {child.localPosition}");
        Debug.Log($"자식 월드 좌표: {child.position}");
    }
}

예시 4: 카메라를 플레이어 기준으로 배치

using UnityEngine;

public class CameraFollow : MonoBehaviour
{
    public Transform player;
    public Vector3 offset = new Vector3(0, 5, -10);
    
    void LateUpdate()
    {
        // 플레이어의 월드 좌표를 기준으로 카메라 배치
        transform.position = player.position + offset;
        
        // 또는 로컬 좌표 사용 (카메라를 플레이어의 자식으로 설정)
        // transform.localPosition = offset;
    }
}

예시 5: 월드 좌표로 거리 계산

using UnityEngine;

public class DistanceCalculation : MonoBehaviour
{
    public Transform target1;
    public Transform target2;
    
    void Update()
    {
        // 월드 좌표로 거리 계산
        float distance = Vector3.Distance(target1.position, target2.position);
        
        Debug.Log($"두 오브젝트 사이의 거리: {distance}");
        
        // 로컬 좌표로는 거리 계산 불가 (기준이 다를 수 있음)
    }
}

transform.Translate()와 좌표계

Translate()의 좌표계 옵션

using UnityEngine;

public class TranslateSpace : MonoBehaviour
{
    void Update()
    {
        // Space.World: 월드 좌표계 기준 이동
        transform.Translate(1, 0, 0, Space.World);
        
        // Space.Self: 로컬 좌표계 기준 이동 (기본값)
        transform.Translate(1, 0, 0, Space.Self);
        // 또는
        transform.Translate(1, 0, 0);  // 기본값은 Space.Self
    }
}

월드 좌표계 이동 vs 로컬 좌표계 이동

using UnityEngine;

public class TranslateComparison : MonoBehaviour
{
    void Update()
    {
        // 방법 1: 월드 좌표계 기준 (회전과 무관)
        if (Input.GetKey(KeyCode.W))
        {
            transform.Translate(0, 0, 1 * Time.deltaTime, Space.World);
        }
        
        // 방법 2: 로컬 좌표계 기준 (회전에 따라 방향 변경)
        if (Input.GetKey(KeyCode.S))
        {
            transform.Translate(0, 0, -1 * Time.deltaTime, Space.Self);
        }
    }
}

주의사항

  1. 부모가 없을 때: positionlocalPosition은 같은 값
  2. 부모가 있을 때: position은 월드 좌표, localPosition은 부모 기준 상대 좌표
  3. 회전의 영향: 부모가 회전하면 로컬 좌표축도 회전하므로 방향이 달라짐
  4. 이동의 영향: 부모가 이동하면 로컬 좌표 원점도 함께 이동

실전 활용 팁

팁 1: 언제 로컬 좌표를 사용할까?

using UnityEngine;

public class WhenToUseLocal : MonoBehaviour
{
    // 로컬 좌표를 사용하는 경우:
    // 1. 부모를 기준으로 자식을 배치할 때
    // 2. 부모와 함께 이동/회전해야 할 때
    // 3. UI 요소를 부모 패널 기준으로 배치할 때
    
    void SetupUI()
    {
        GameObject button = new GameObject("Button");
        button.transform.SetParent(transform);
        
        // 부모 패널 기준으로 배치 (로컬 좌표)
        button.transform.localPosition = new Vector3(100, -50, 0);
    }
}

팁 2: 언제 월드 좌표를 사용할까?

using UnityEngine;

public class WhenToUseWorld : MonoBehaviour
{
    // 월드 좌표를 사용하는 경우:
    // 1. 씬의 절대 위치에 배치할 때
    // 2. 다른 오브젝트와의 거리를 계산할 때
    // 3. 부모와 무관하게 위치를 유지해야 할 때
    
    void SpawnEnemy()
    {
        GameObject enemy = new GameObject("Enemy");
        
        // 씬의 특정 위치에 배치 (월드 좌표)
        enemy.transform.position = new Vector3(10, 0, 5);
        
        // 플레이어와의 거리 계산
        float distance = Vector3.Distance(
            enemy.transform.position,
            transform.position
        );
    }
}

팁 3: 좌표 변환 활용

using UnityEngine;

public class CoordinateConversion : MonoBehaviour
{
    public Transform target;
    
    void Update()
    {
        // 타겟의 월드 좌표를 내 로컬 좌표로 변환
        Vector3 localTargetPos = transform.InverseTransformPoint(target.position);
        
        // 내 로컬 좌표계에서 타겟이 어느 방향에 있는지 확인
        if (localTargetPos.x > 0)
        {
            Debug.Log("타겟이 오른쪽에 있음");
        }
        else if (localTargetPos.x < 0)
        {
            Debug.Log("타겟이 왼쪽에 있음");
        }
    }
}

← 목차로 돌아가기