A Slightly Nicer API for Unity’s NonAlloc
Physics Functions
If you’re a Unity developer, you might use the allocation-free versions of its intersection functions — Physics.RaycastNonAlloc
instead of Physics.RaycastAll
, Physics.OverlapBoxNonAlloc
instead of Physics.OverlapBox
, and so forth. And if you don’t, perhaps you ought to; they’re an easy way to reduce garbage collection overhead in your game if you call these functions every frame.
A typical implementation looks like this:
RaycastHit[] hits = new RaycastHit[10];
void Update()
{
var hitCount = Physics.RaycastNonAlloc(transform.position, transform.forward, hits);
for (var i = 0; i < hitCount; i++)
{
var hit = hits[i];
Debug.Log("Hit: " + hit.collider.name);
}
}
If you write this pattern often, a useful abstraction is to wrap the logic in a class and use ReadOnlySpan
to expose the results.
class Raycaster
{
RaycastHit[] hits = new RaycastHit[10];
ReadOnlySpan<RaycastHit> RaycastAll(Vector3 origin, Vector3 direction)
{
var hitCount = Physics.RaycastNonAlloc(origin, direction, hits);
return new ReadOnlySpan<RaycastHit>(hits, 0, hitCount);
}
}
ReadOnlySpan
is a lightweight container that allows you to peek into a slice of a managed array. With this new class, we can now iterate over our raycast hits in a foreach loop; no need to manage a results array and no danger of misusing hitCount
.
Raycaster raycaster = new();
void Update()
{
var hits = raycaster.RaycastAll(transform.position, transform.forward);
foreach (var hit in hits)
{
Debug.Log("Hit: " + hit.collider.name);
}
}
Simple as it is, I use this pattern enough that it was worthwhile to codify into a reusable package. After adding nonalloc-physics-wrapper to your game, prefix any Physics
intersection function with NonAlloc
and iterate over the return value as before.
- var hits = Physics.RaycastAll(ray);
+ var hits = NonAllocPhysics.RaycastAll(ray);
- var colliders = Physics.OverlapBox(center, halfExtents);
+ var colliders = NonAllocPhysics.OverlapBox(center, halfExtents);
... and so on.
Now instead of a new array being allocated on each call, a single one is reused across multiple calls and the garbage collector has that much less work to do. He deserves a break.