Quick answer: Size your RaycastHit[] buffer to the peak hit count plus a small margin. Sort by distance after the call — Unity doesn’t guarantee hit order. Always check the return count.

A piercing bullet ray is supposed to damage every enemy along the line. Five enemies in line; only the first three are hit. Buffer was sized 3.

The Symptom

RaycastNonAlloc/SphereCastNonAlloc returns hits but skips some. The omitted ones are not always the farthest — PhysX returns hits in arbitrary order.

The Fix

private readonly RaycastHit[] _hits = new RaycastHit[16];

void FirePiercingBullet(Ray ray, float dist)
{
    int count = Physics.RaycastNonAlloc(ray, _hits, dist, _hitMask, QueryTriggerInteraction.Ignore);

    // Sort by distance to hit nearest first
    Array.Sort(_hits, 0, count, _byDistance);

    for (int i = 0; i < count; i++)
        DamageHit(_hits[i]);

    if (count == _hits.Length)
        Debug.LogWarning($"Hit buffer full ({count}); some hits possibly truncated");
}

private static readonly IComparer<RaycastHit> _byDistance = Comparer<RaycastHit>.Create((a, b) => a.distance.CompareTo(b.distance));

Buffer of 16 covers most cases. Sort once. Detect the “buffer full” case and warn or auto-grow.

Auto-Grow Pattern

For systems where peak hit count is unpredictable:

RaycastHit[] _hits = new RaycastHit[16];

int count;
while (true)
{
    count = Physics.RaycastNonAlloc(ray, _hits, dist, _hitMask);
    if (count < _hits.Length) break;
    _hits = new RaycastHit[_hits.Length * 2];
}

Doubles on saturation. Stable size after a few frames.

Verifying

Place 20 enemies on a line. Fire. Count damaged. With sized buffer + sort, all are hit. Without, hits truncate at the buffer length.

“Size for peak. Sort. Detect saturation. Hits arrive ordered.”

Related Issues

For Physics2D OverlapCircle stale, see overlap stale. For collider not detecting, see collider detection.

Bigger buffer. Sort. No truncation.