터널링 현상(Tunneling)은 빠르게 움직이는 객체가 충돌을 감지하지 못하고 다른 객체를 통과하는 문제를 의미합니다.
FixedUpdate 이벤트 메서드 활용
Update() 대신 일정한 시간 간격으로 호출되는 FixedUpdate()를 사용하여 물리 연산의 정확도를 높일 수 있습니다.
public class MovementController : MonoBehaviour
{
public float moveSpeed = 5f;
private Vector3 direction;
void FixedUpdate()
{
// 일정한 시간 간격으로 이동 처리
transform.position += direction * moveSpeed * Time.fixedDeltaTime;
}
}
Time.fixedDeltaTime 조절
물리 연산의 정확도를 높이기 위해 FixedUpdate의 호출 간격을 조절할 수 있습니다.
- Time.fixedDeltaTime 값을 너무 작게 설정하면 전체적인 게임 성능에 영향을 줄 수 있습니다.
public class PhysicsTimeController : MonoBehaviour
{
void Start()
{
// 기본값(0.02초)보다 더 작은 간격으로 설정
Time.fixedDeltaTime = 0.01f;
}
}
Rigidbody의 Collision Detection 모드 설정
Rigidbody 컴포넌트의 충돌 감지 모드를 상황에 맞게 설정합니다.
충돌 감지 모드의 종류
- Discrete (기본값)
- 가장 기본적인 충돌 감지 방식
- 각 물리 업데이트 단계에서 현재 위치만을 기준으로 충돌 검사
- 성능이 좋지만, 빠른 움직임에서 터널링 현상 발생 가능
- 일반적인 움직임의 오브젝트에 적합
- Continuous
- 이동 경로를 고려한 연속적인 충돌 검사
- 정적(Static) 콜라이더와의 충돌만 연속적으로 감지
- Discrete보다 더 많은 CPU 리소스 사용
- 투사체나 빠르게 움직이는 물체에 적합
- Continuous Dynamic
- 가장 정확한 충돌 감지 방식
- 동적(Dynamic) 콜라이더를 포함한 모든 충돌을 연속적으로 감지
- 가장 많은 CPU 리소스 사용
- 매우 빠른 속도로 움직이는 물체들 간의 상호작용에 적합
public class CollisionController : MonoBehaviour
{
void Start()
{
Rigidbody rb = GetComponent<Rigidbody>();
// 상황에 따른 충돌 감지 모드 설정
switch(objectType)
{
case ObjectType.Normal:
rb.collisionDetectionMode = CollisionDetectionMode.Discrete;
break;
case ObjectType.FastMoving:
rb.collisionDetectionMode = CollisionDetectionMode.Continuous;
break;
case ObjectType.VeryFastMoving:
rb.collisionDetectionMode = CollisionDetectionMode.ContinuousDynamic;
break;
}
}
}
Rigidbody Interpolation 설정
물리 연산과 렌더링 사이의 불일치를 보완하기 위한 보간 설정입니다.
Interpolation 모드의 종류
- None (기본값)
- 보간을 사용하지 않음
- 물리 업데이트 시점의 위치를 그대로 사용
- CPU 부하가 가장 적음
- 움직임이 다소 부자연스러울 수 있음
- Interpolate
- 이전 프레임과 현재 프레임의 위치를 보간
- 부드러운 움직임 제공
- 약간의 지연이 발생할 수 있음
- 대부분의 일반적인 상황에 적합
- Extrapolate
- 현재와 이전 프레임을 기반으로 다음 위치를 예측
- 지연이 적고 반응성이 좋음
- 예측이 빗나가면 움직임이 불안정해질 수 있음
- 빠른 반응성이 필요한 물체에 적합
public class InterpolationController : MonoBehaviour
{
public enum MovementType
{
Regular,
Smooth,
Responsive
}
public MovementType movementType = MovementType.Regular;
void Start()
{
Rigidbody rb = GetComponent<Rigidbody>();
// 움직임 타입에 따른 보간 설정
switch(movementType)
{
case MovementType.Regular:
rb.interpolation = RigidbodyInterpolation.None;
break;
case MovementType.Smooth:
rb.interpolation = RigidbodyInterpolation.Interpolate;
break;
case MovementType.Responsive:
rb.interpolation = RigidbodyInterpolation.Extrapolate;
break;
}
}
}
Rigidbody의 조합 예시
- 일반 오브젝트
- rb.collisionDetectionMode = CollisionDetectionMode.Discrete;
- rb.interpolation = RigidbodyInterpolation.None;
- 플레이어 캐릭터
- rb.collisionDetectionMode = CollisionDetectionMode.Continuous;
- rb.interpolation = RigidbodyInterpolation.Interpolate;
- 고속 투사체
- rb.collisionDetectionMode = CollisionDetectionMode.ContinuousDynamic;
- rb.interpolation = RigidbodyInterpolation.Extrapolate;
Raycast를 활용한 충돌 감지
이동 경로상의 충돌을 미리 감지하여 터널링을 방지합니다.
- Raycast를 사용할 때는 레이어 마스크를 적절히 설정하여 불필요한 충돌 검사를 피하는 것이 좋습니다.
public class RaycastController : MonoBehaviour
{
public float moveSpeed = 5f;
public LayerMask collisionLayer;
private Vector3 direction;
void Update()
{
Vector3 nextPosition = transform.position + direction * moveSpeed * Time.deltaTime;
// 이동 전에 Raycast로 충돌 체크
RaycastHit hit;
if (Physics.Raycast(transform.position, direction, out hit,
moveSpeed * Time.deltaTime, collisionLayer))
{
// 충돌이 예상되는 경우 처리
direction = Vector3.zero;
}
else
{
// 안전한 경우 이동
transform.position = nextPosition;
}
}
}