좌표계란?
좌표계는 오브젝트의 위치를 표현하는 기준입니다. 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);
}
}
}
주의사항
- 부모가 없을 때:
position과localPosition은 같은 값 - 부모가 있을 때:
position은 월드 좌표,localPosition은 부모 기준 상대 좌표 - 회전의 영향: 부모가 회전하면 로컬 좌표축도 회전하므로 방향이 달라짐
- 이동의 영향: 부모가 이동하면 로컬 좌표 원점도 함께 이동
실전 활용 팁
팁 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("타겟이 왼쪽에 있음");
}
}
}