Quick Reference
NonAlloc query는 호출자가 제공한 배열에 결과를 채우고 저장한 개수를 반환합니다. 배열이 가득 찬 경우 Unity가 자동으로 키우지 않으므로, 반환값이 버퍼 길이와 같으면 포화 가능성으로 취급합니다. 첫 count개만 이번 query의 결과입니다.
[SerializeField] private LayerMask enemyMask;
private readonly Collider[] _hits = new Collider[32];
private int FindEnemies(Vector3 center, float radius)
{
int count = Physics.OverlapSphereNonAlloc(
center,
radius,
_hits,
enemyMask,
QueryTriggerInteraction.Ignore
);
if (count == _hits.Length)
Debug.LogWarning("Overlap buffer may be saturated.");
for (int i = 0; i < count; i++)
ProcessEnemy(_hits[i]);
return count;
}| 선택 | 할당 | 반드시 관리할 것 |
|---|---|---|
일반 OverlapSphere/RaycastAll | 결과 배열 생성 가능 | 호출 빈도와 GC 측정 |
| NonAlloc overload | 호출 중 새 결과 배열 없음 | 버퍼 크기·포화·결과 순서 |
RaycastCommand | Native 결과 버퍼·job 수명 | schedule/complete/dispose와 결과 layout |
버퍼의 계약
OverlapSphereNonAlloc은 Sphere 안이나 닿은 Collider를 제공한 Collider[]에 저장하고, 저장한 개수를 반환합니다. 결과가 버퍼보다 많아도 buffer를 늘리지 않으며 buffer 길이를 반환합니다. 따라서 count == _hits.Length이면 "정확히 이만큼 hit"인지 "더 많지만 잘림"인지 구분할 수 없습니다. 게임 규칙이 모든 대상을 반드시 처리해야 한다면 fallback, 버퍼 확장, 설계상 최대치 제한 중 하나를 정해야 합니다.
버퍼는 Awake 또는 필드 초기화에서 한 번 만들고 반복 호출에서 재사용합니다. 이전 query의 참조가 배열 뒤쪽에 남아 있어도 이번 반환 count 바깥은 읽지 않습니다. 일반 query도 한 번만 실행되는 메뉴 열기, 툴, 드문 상호작용에서는 코드 단순성이 더 중요할 수 있으므로 Profiler allocation에서 실제 원인을 먼저 확인합니다.
NonAlloc 결과의 순서는 계약으로 보장되지 않습니다. 첫 결과를 가장 가까운 적으로 쓰거나 매 호출마다 같은 순서를 기대하지 마세요. 거리·우선순위가 필요하면 count 범위에서 직접 선택합니다.
Collider nearest = null;
float nearestSqrDistance = float.PositiveInfinity;
for (int i = 0; i < count; i++)
{
float sqrDistance = (_hits[i].transform.position - center).sqrMagnitude;
if (sqrDistance >= nearestSqrDistance) continue;
nearest = _hits[i];
nearestSqrDistance = sqrDistance;
}자주 틀리는 부분
| 증상 | 원인 | 수정 방향 |
|---|---|---|
| 적이 많을 때 일부만 감지 | 버퍼가 포화됐습니다. | count == buffer.Length를 telemetry로 남기고 정책을 정합니다. |
| 가끔 멀리 있는 대상을 먼저 처리 | 반환 순서에 의존했습니다. | 거리·우선순위를 직접 계산합니다. |
| 이전 frame collider를 다시 처리 | count보다 배열 전체를 순회했습니다. | 0 .. count - 1만 읽습니다. |
| GC는 줄지 않았는데 코드만 복잡 | 실제 allocation 원인이 query가 아닙니다. | Profiler에서 query·후속 LINQ·로그·GetComponent를 구분합니다. |
| trigger 감지가 씬마다 달라짐 | global trigger query 설정을 따릅니다. | QueryTriggerInteraction을 명시합니다. |
NonAlloc은 결과를 덜 만드는 API가 아니라 결과 배열 할당을 피하는 API입니다. 버퍼가 가득 찼을 때 데이터를 잃을 수 있다는 계약을 코드와 테스트에 남기지 않으면, GC 문제를 숨기는 대신 드문 전투·군중 버그를 만듭니다.
참고 링크
2 sources