Quick Comparison
| 방식 | 먼저 고를 조건 | 결과를 읽는 때 | 비용의 핵심 |
|---|---|---|---|
Physics.Raycast | 소량 query, 즉시 결과 필요 | 호출 직후 | 가장 단순한 흐름 |
| NonAlloc query | Profiler에서 결과 배열 할당이 반복됨 | 호출 직후 | 버퍼 포화와 수동 순서 처리 |
RaycastCommand.ScheduleBatch | 대량 query가 main thread 병목이고 결과 소비를 늦출 수 있음 | JobHandle.Complete() 뒤 | NativeArray 수명, schedule/complete 동기화 |
RaycastCommand는 raycast를 비동기 job으로 배치합니다. 결과 버퍼는 job이 끝날 때까지 읽을 수 없습니다. 결과가 같은 frame에 꼭 필요해 곧바로 Complete()해야 한다면, scheduling 이득보다 대기·구성 비용이 큰지 Profiler로 확인합니다.
NativeArray<RaycastCommand> commands = new(count, Allocator.TempJob);
NativeArray<RaycastHit> results = new(count, Allocator.TempJob);
try
{
for (int i = 0; i < count; i++)
commands[i] = new RaycastCommand(origins[i], directions[i], QueryParameters.Default);
JobHandle handle = RaycastCommand.ScheduleBatch(commands, results, minCommandsPerJob: 1);
handle.Complete();
for (int i = 0; i < count; i++)
if (results[i].collider != null) ProcessHit(results[i]);
}
finally
{
if (commands.IsCreated) commands.Dispose();
if (results.IsCreated) results.Dispose();
}결과 버퍼와 job 수명
ScheduleBatch의 result는 command index와 maxHits에 따라 배치됩니다. command N의 결과는 N * maxHits부터 시작합니다. 한 command에 hit가 부족하면 첫 invalid result의 collider가 null이며, 그 뒤 slot은 job이 쓰지 않아 null이라고 가정하면 안 됩니다. 따라서 multi-hit를 읽을 때는 첫 null에서 멈춥니다.
const int maxHits = 2;
NativeArray<RaycastHit> results = new(commandCount * maxHits, Allocator.TempJob);
JobHandle handle = RaycastCommand.ScheduleBatch(commands, results, 1, maxHits);
handle.Complete();
for (int commandIndex = 0; commandIndex < commandCount; commandIndex++)
{
int offset = commandIndex * maxHits;
for (int hitIndex = 0; hitIndex < maxHits; hitIndex++)
{
RaycastHit hit = results[offset + hitIndex];
if (hit.collider == null) break;
ProcessHit(hit);
}
}QueryParameters로 layer mask, trigger 포함, back-face와 multi-face hit 정책을 정합니다. 일반 Raycast의 설정을 job query로 옮길 때 Default가 같은 의미라고 가정하지 말고 필요한 필터를 명시합니다. NativeArray는 job이 참조하는 동안 dispose할 수 없고, error path에서도 completion 뒤 dispose가 보장되어야 합니다.
선택 순서
GC allocation이 병목이면 먼저 NonAlloc 버퍼를 검토합니다. CPU query 시간이 병목이고 결과를 다음 system 또는 다음 frame까지 미룰 수 있으면 batch job을 검토합니다. 쿼리 수 자체가 과도하면 interval을 늘리거나 시야 후보를 spatial partitioning·layer·거리로 줄이는 설계가 먼저일 수 있습니다. job 도입은 측정 결과와 소비 시점이 뒷받침될 때만 합니다.
자주 틀리는 부분
| 증상 | 원인 | 수정 방향 |
|---|---|---|
| 결과가 간헐적으로 깨짐 | Complete 전 결과 버퍼를 읽었습니다. | dependency를 연결하고 완료 뒤에만 소비합니다. |
| 일부 multi-hit가 이상함 | 첫 invalid hit 뒤의 buffer slot을 읽었습니다. | collider == null에서 중단합니다. |
InvalidOperationException 또는 memory leak | NativeArray를 job 중 dispose했거나 dispose를 빠뜨렸습니다. | 소유 범위와 finally/dispose dependency를 명확히 합니다. |
| 도입 뒤 frame time이 줄지 않음 | 즉시 Complete하거나 scheduling 비용이 더 큽니다. | same-frame dependency와 profiler sample을 비교합니다. |
| trigger hit가 일반 ray와 다름 | QueryParameters의 filter를 명시하지 않았습니다. | layer/trigger/back-face 정책을 설정합니다. |
RaycastCommand는 속도 옵션이 아니라 비동기 데이터 수명 모델입니다. command 작성, job completion, 결과 해석, NativeArray disposal을 한 흐름으로 소유할 수 없으면 일반 query가 더 안전합니다.
참고 링크
3 sources