핵심 정리
private readonly Collider[] results = new Collider[16];
int hitCount = Physics.OverlapSphereNonAlloc(transform.position, radius, results);구조 이해
GC 할당 문제와 NonAlloc 접근
OverlapSphere, OverlapBox처럼 여러 결과를 돌려주는 물리 쿼리 API는 매 호출마다 결과 배열을 새로 생성합니다. 이 방식은 코드가 단순하지만, AI 감지나 탐지 시스템처럼 매 프레임 반복 호출되는 상황에서는 GC 부담이 빠르게 쌓입니다. Unity 공식 물리 성능 가이드는 이런 패턴에서 NonAlloc 버전을 우선 고려하라고 설명합니다.
NonAlloc API 동작 방식
NonAlloc API는 결과 버퍼를 호출자가 직접 제공하므로 추가 할당 없이 재사용할 수 있습니다. 반환값은 실제로 채워진 결과 개수이며, 버퍼 크기보다 많은 충돌이 있어도 버퍼 크기까지만 채워집니다. 따라서 results 배열은 예상 최대 충돌 수보다 충분히 크게 잡아야 합니다.
[SerializeField] private float detectRadius = 3f;
private readonly Collider[] hits = new Collider[32];
private void Update()
{
int count = Physics.OverlapSphereNonAlloc(transform.position, detectRadius, hits);
for (int i = 0; i < count; i++)
{
if (hits[i].CompareTag("Enemy"))
{
Debug.Log(hits[i].name);
}
}
}2D 물리와 버퍼 크기 선택
2D 물리에서는 NonAlloc 이름 대신 배열이나 List<T>를 받는 overload를 쓰는 방식으로 같은 목적을 달성합니다. 버퍼 크기 선택은 성능과 정확도의 균형 문제입니다. 너무 작으면 일부 충돌 대상을 놓치고, 너무 크면 메모리를 낭비합니다. 실제 장면에서 최대 충돌 개수를 측정한 뒤 적절한 여유분을 더한 크기로 설정하는 편이 좋습니다.
체크포인트
| 상황 | 적합한 선택 |
|---|---|
| AI 탐지·범위 감지처럼 매 프레임 반복 쿼리 | OverlapSphereNonAlloc + 재사용 버퍼 |
| 결과 배열이 매 프레임 새로 생성되어 GC 튐 | NonAlloc 버전으로 교체 |
| 버퍼 크기 결정 | 실제 장면 최대 충돌 수 측정 후 여유분 추가 |
| 한 번만 호출되는 쿼리 | 일반 API 그대로 사용해도 무방 |
주의할 점
NonAlloc으로 바꿔도 버퍼가 너무 작으면 충돌 대상을 놓칩니다. 버퍼 크기는 실제 장면에서 검증해야 합니다.
// ❌ 매 프레임 결과 배열 새로 생성 — GC 할당 반복
private void Update()
{
Collider[] hits = Physics.OverlapSphere(transform.position, radius);
// 매 프레임 new Collider[] 발생 → GC 누적
}
// ✅ 버퍼 재사용 — 할당 없음
private readonly Collider[] _hits = new Collider[32]; // 한 번만 할당
private void Update()
{
int count = Physics.OverlapSphereNonAlloc(transform.position, radius, _hits);
// 버퍼 크기 32 미만이면 일부 결과 누락 가능 → 실측으로 크기 결정
for (int i = 0; i < count; i++)
{
ProcessHit(_hits[i]);
}
}참고 링크
2 sources