Unity에서 3D 오브젝트를 다루다 보면 피벗 포인트(Pivot Point) 때문에 예상치 못한 문제들이 발생할 수 있습니다. 특히 레벨 디자인이나 조준 시스템을 구현할 때 피벗 포인트의 위치는 매우 중요한 요소가 됩니다. 이번 글에서는 피벗 포인트로 인한 문제점들과 이를 해결하는 다양한 방법을 알아보겠습니다.
피벗 포인트는 Unity에서 오브젝트의 기준점입니다. Transform.position이 바로 이 피벗 포인트의 위치를 나타내며, 오브젝트의 회전, 스케일링, 이동의 중심축 역할을 합니다.
Scene 뷰 상단의 툴바에서 Center와 Pivot 사이를 전환할 수 있습니다:
가장 흔한 문제는 오브젝트 배치 시 발생하는 불편함입니다.
- Cube (Unity 기본 오브젝트): 피벗 포인트가 중심에 위치
- Barrel (외부 에셋): 피벗 포인트가 하단에 위치
Cube의 경우:
Barrel의 경우:
조준 시스템에서 Transform.position을 타겟으로 사용할 때:
// 현재 조준 시스템 (문제가 있는 코드)
aimPosition = target.transform.position;
결과:
피벗 포인트가 잘못된 위치에 있으면:
Blender 등에서 피벗 포인트 조정:
Parent GameObject (빈 오브젝트)
└── Child GameObject (실제 모델)
구현 방법:
1. 빈 게임오브젝트 생성 (부모)
2. 실제 모델을 자식으로 배치
3. 자식 오브젝트의 위치를 조정하여 부모의 피벗을 원하는 위치로 설정
4. 타겟 컴포넌트, 콜라이더 등은 부모에 설정
장점:
단점:
가장 효율적인 해결 방법은 Renderer의 bounds.center를 활용하는 것입니다.
public class PlayerAim : MonoBehaviour
{
private void UpdateAimPosition()
{
if (target != null)
{
// Renderer 컴포넌트가 있는지 확인
Renderer targetRenderer = target.GetComponent<Renderer>();
if (targetRenderer != null)
{
// 렌더러의 바운드 중심점을 조준점으로 설정
aimPosition = targetRenderer.bounds.center;
}
else
{
// Renderer가 없다면 기존 방식 사용
aimPosition = target.transform.position;
}
}
}
}
이 방법의 장점:
더 정교한 시스템을 원한다면:
public class SmartTargetSystem : MonoBehaviour
{
[System.Serializable]
public class TargetInfo
{
public Transform target;
public Vector3 customOffset = Vector3.zero;
public bool useCustomOffset = false;
}
public Vector3 GetTargetPosition(Transform target)
{
// 1. 커스텀 타겟 포인트 확인
TargetPoint customTarget = target.GetComponent<TargetPoint>();
if (customTarget != null)
{
return customTarget.GetTargetPosition();
}
// 2. Renderer bounds 중심 사용
Renderer renderer = target.GetComponent<Renderer>();
if (renderer != null)
{
return renderer.bounds.center;
}
// 3. Collider 중심 사용
Collider col = target.GetComponent<Collider>();
if (col != null)
{
return col.bounds.center;
}
// 4. 기본값: Transform 위치
return target.position;
}
}
// 특별한 타겟 포인트가 필요한 오브젝트용 컴포넌트
public class TargetPoint : MonoBehaviour
{
[SerializeField] private Vector3 localOffset = Vector3.zero;
public Vector3 GetTargetPosition()
{
return transform.position + transform.TransformDirection(localOffset);
}
private void OnDrawGizmosSelected()
{
Gizmos.color = Color.red;
Gizmos.DrawWireSphere(GetTargetPosition(), 0.1f);
}
}
[System.Serializable]
public class PropPlacementHelper : MonoBehaviour
{
[Header("Auto-Snap to Ground")]
public bool snapToGround = true;
public LayerMask groundLayer = 1;
private void Start()
{
if (snapToGround)
{
SnapToGround();
}
}
private void SnapToGround()
{
Renderer renderer = GetComponent<Renderer>();
if (renderer != null)
{
Vector3 bottomPoint = renderer.bounds.min;
if (Physics.Raycast(transform.position, Vector3.down, out RaycastHit hit, Mathf.Infinity, groundLayer))
{
float offset = transform.position.y - bottomPoint.y;
transform.position = new Vector3(transform.position.x, hit.point.y + offset, transform.position.z);
}
}
}
}
Renderer.bounds는 매 프레임 계산할 필요가 없으므로:
public class OptimizedTargetSystem : MonoBehaviour
{
private Dictionary<Transform, Vector3> cachedTargetPositions = new Dictionary<Transform, Vector3>();
public Vector3 GetCachedTargetPosition(Transform target)
{
if (!cachedTargetPositions.ContainsKey(target))
{
CacheTargetPosition(target);
}
return cachedTargetPositions[target];
}
private void CacheTargetPosition(Transform target)
{
Renderer renderer = target.GetComponent<Renderer>();
Vector3 targetPos = renderer != null ? renderer.bounds.center : target.position;
cachedTargetPositions[target] = targetPos;
}
}
피벗 포인트 문제는 Unity 개발에서 자주 마주치는 일반적인 이슈입니다. 가장 효율적인 해결책은 Renderer.bounds.center를 활용한 스크립트 기반 접근입니다. 이 방법은:
Unity 개발의 핵심은 문제 해결 능력입니다. 피벗 포인트 같은 기본적인 이슈부터 시작해서 점진적으로 복잡한 문제들을 해결하는 경험을 쌓아가시기 바랍니다. 이러한 문제 해결 스킬은 게임 개발뿐만 아니라 실생활에서도 큰 도움이 될 것입니다.