유니티(Unity) 물리 엔진 성능 최적화: FixedUpdate 주기 조절과 물리 레이어(Layer Collision Matrix) 설정법
모바일 3D 게임에서 다수의 몬스터와 투사체, 파티클 충돌체가 화면을 가득 채울 때 발생하는 심각한 프레임 드랍과 발열의 주원인은 그래픽 렌더링뿐만 아니라 CPU의 물리 연산 과부하(Physics.Simulate)에 있습니다. 유니티의 내장 물리 엔진(PhysX)은 현실적인 충돌과 역학을 계산하지만, 모바일 기기의 제한된 CPU 코어에서 수백 번의 충돌 판정이 매 프레임 반복되면 메인 렌더링 루프까지 함께 멈칫거리는 병목 현상이 발생합니다. 특히 렌더링 프레임(Update)과 물리 시뮬레이션 주기(FixedUpdate)의 작동 메커니즘을 제대로 제어하지 못하면 '물리 연산의 나선형 죽음(Physics Spiral of Death)'에 빠지기 쉽습니다. 본 포스팅에서는 Fixed Timestep의 수학적 튜닝 기법부터, 불필요한 충돌 검사를 80% 이상 쳐내는 레이어 충돌 매트릭스(Layer Collision Matrix) 설계, 그리고 가비지 컬렉션(GC)을 0으로 만드는 Non-Alloc 물리 API 활용법까지 완벽히 정리합니다.
1. FixedUpdate와 타임스텝(Timestep)의 내부 동작 메커니즘
유니티의 Update()는 하드웨어 성능과 렌더링 부하에 따라 실행 간격이 유동적으로 변하는 가변 프레임(Variable Frame)인 반면, FixedUpdate()는 물리 시뮬레이션의 일관된 재현성을 위해 고정된 시간 간격(Fixed Timestep)마다 호출됩니다.
1.1 Fixed Timestep 최적 수치 튜닝
유니티의 기본 Fixed Timestep은 0.02초 (초당 50회)로 설정되어 있습니다. 게임의 장르와 타깃 프레임레이트에 맞춰 이 수치를 조정하는 것만으로도 CPU 물리 부하를 대폭 줄일 수 있습니다.
| 게임 장르 및 환경 | 권장 Fixed Timestep | 초당 연산 횟수 | 성능 및 물리 정밀도 효과 |
|---|---|---|---|
| 캐주얼 / 방치형 / 턴제 RPG | 0.0333초 ~ 0.04초 | 25 ~ 30 Hz | 물리 연산량 40~50% 절감. 모바일 배터리 절약 극대화 |
| 기본 표준값 | 0.02초 | 50 Hz | 일반적인 3D 액션 및 플랫폼 게임의 균형점 |
| 정밀 레이싱 / 물리 퍼즐 | 0.0166초 | 60 Hz | 고속 주행 터널링 방지, 높은 물리 정밀도 (CPU 부담 증가) |
1.2 Maximum Allowed Timestep: 물리 연산의 나선형 죽음 방어
렌더링 프레임이 일시적으로 떨어져 1초에 10프레임(100ms)만 그려지는 순간, 유니티는 밀린 0.02초 단위의 물리 연산을 따라잡기 위해 단일 렌더 프레임 동안 5번의 FixedUpdate를 연속 실행합니다. 이로 인해 CPU가 더욱 과열되고 프레임은 더 떨어지는 악순환이 발생하는데, 이를 'Physics Spiral of Death'라고 부릅니다.
해결책: Project Settings > Time에서 Maximum Allowed Timestep을 0.05초~0.1초 수준으로 제한하여, 최악의 프레임 드랍 상황에서도 한 프레임당 물리 시뮬레이션 실행 횟수를 2~3회 이내로 강제 캡핑해야 합니다.
2. 레이어 충돌 매트릭스(Layer Collision Matrix) 최적화
기본적으로 유니티 프로젝트는 모든 레이어가 서로 충돌할 수 있도록 매트릭스의 모든 체크박스가 켜져 있습니다. 씬에 콜라이더가 N개 있을 때 충돌 판정 검사 비용은 $O(N^2)$으로 치솟습니다.
2.1 실무 충돌 매트릭스 정리 원칙
- 적 투사체(EnemyBullet) vs 적(Enemy): 적이 쏜 총알이 적 동료를 맞출 필요가 없다면 충돌 체크를 해제합니다.
- 아군 투사체(PlayerBullet) vs 플레이어(Player): 플레이어 총알이 자신과 충돌 검사하는 것을 원천 차단합니다.
- 드롭 아이템(Item) vs 몬스터/투사체: 아이템은 오직 바닥(Environment)과 플레이어(Player)하고만 충돌하면 충분합니다.
- 장식용 프랍(Debris/VFX) vs 캐릭터: 시각적 잔해물은 물리 연산 없이 무시하거나 바닥 충돌만 허용합니다.
- 효과: 물리 엔진의 1단계 검사인 브로드페이즈(Broadphase)에서 비활성화된 레이어 쌍을 즉각 제외(Early Out)하므로, 씬 내 수백 개의 콜라이더가 존재해도 연산량이 70~80% 감소합니다.
3. 가비지 컬렉션(GC)을 없애는 Non-Alloc 물리 API 정석
매 프레임 시야각이나 범위 내 적을 감지하기 위해 Physics.RaycastAll이나 Physics.OverlapSphere를 호출하는 것은 모바일 메모리에 치명적입니다. 매번 RaycastHit[] 또는 Collider[] 배열 객체를 힙(Heap) 메모리에 새로 할당하여 심각한 GC Alloc 스파이크를 일으키기 때문입니다.
3.1 Non-Alloc API C# 실전 구현 패턴
using UnityEngine;
public class OptimizedSensor : MonoBehaviour
{
[Header("Detection Settings")]
[SerializeField] private float detectRadius = 10f;
[SerializeField] private LayerMask targetLayer;
// 1. 메모리 힙 할당을 방지하기 위한 정적 버퍼 사전 할당
private static readonly Collider[] HitCollidersBuffer = new Collider[20];
public void DetectSurroundingTargets()
{
// 2. OverlapSphereNonAlloc 호출: 가비지(GC) 발생 0 Byte
int hitCount = Physics.OverlapSphereNonAlloc(
transform.position,
detectRadius,
HitCollidersBuffer,
targetLayer
);
// 3. 반환된 개수(hitCount)만큼만 순회 처리
for (int i = 0; i < hitCount; i++)
{
Collider col = HitCollidersBuffer[i];
Debug.Log($"감지된 대상: {col.gameObject.name}");
// 처리 후 버퍼 참조 정리 (메모리 누수 방지)
HitCollidersBuffer[i] = null;
}
}
}
4. 콜라이더 및 리지드바디 실무 최적화 4대 수칙
- 메시 콜라이더(Mesh Collider) 지양: 폴리곤 면마다 충돌을 계산하는 복잡한 메시 콜라이더는 모바일의 재앙입니다. 기본 도형(Box, Sphere, Capsule) 여러 개를 조합한 복합 콜라이더(Compound Collider)로 단순화해야 합니다.
- 정적 콜라이더(Static Collider) 이동 금지:
Rigidbody가 없는 게임오브젝트의transform.position을 스크립트로 이동시키면, PhysX는 전체 월드의 BVH(Bounding Volume Hierarchy) 트리를 매 프레임 재구성하는 극심한 부하를 유발합니다. 이동하는 모든 콜라이더에는 반드시Rigidbody(isKinematic = true)를 부착해야 합니다. - 슬립 모드(Sleeping) 적극 활용: 정지해 있는 리지드바디는
sleepThreshold에 도달하면 물리 연산을 중단하는 수면 상태로 전환됩니다. 스크립트에서 불필요하게rigidbody.velocity를 매 프레임 0으로 덮어써서 깨우지 않도록 주의합니다. - 보간(Interpolate) 선별 적용:
FixedUpdate주기와 렌더링Update주기의 차이로 캐릭터가 덜덜 떨리는 현상은 Rigidbody의 Interpolate 옵션으로 해결할 수 있습니다. 단, 연산 비용이 있으므로 주인공과 핵심 카메라 타깃에만 한정 적용합니다.
5. 결론 및 모바일 물리 최적화 실무 체크리스트
물리 엔진 최적화는 단순히 시뮬레이션을 끄는 것이 아니라, 엔진의 시간 모델과 충돌 검사 알고리즘을 효율화하여 안정적인 60FPS와 디바이스 쿨링을 달성하는 엔지니어링입니다. 프로젝트 배포 전 아래 5가지 체크리스트를 확인하시기 바랍니다.
- Fixed Timestep 설정: 프로젝트 장르에 맞춰 0.02초(50Hz) 또는 0.0333초(30Hz)로 최적화되었는가?
- Maximum Allowed Timestep 제한: 급격한 프레임 드랍 시 물리 폭주를 막기 위해 0.05~0.1초 이하로 캡핑되었는가?
- 충돌 매트릭스 다이어트:
Project Settings > Physics에서 불필요한 충돌 레이어 간의 교차 체크박스가 모두 해제되었는가? - Non-Alloc API 100% 전환: 런타임 반복 검사에서
RaycastAll대신RaycastNonAlloc/OverlapSphereNonAlloc을 사용하고 있는가? - 이동체 Kinematic Rigidbody 부착: 씬에서 스크립트나 애니메이션으로 움직이는 모든 장애물/플랫폼에
isKinematicRigidbody가 빠짐없이 부착되었는가?
댓글 쓰기