raycast method
RaycastResult<ShapeHitbox> ?
raycast(
- Ray2 ray, {
- double? maxDistance,
- bool hitboxFilter(
- ShapeHitbox candidate
- List<
ShapeHitbox> ? ignoreHitboxes, - RaycastResult<
ShapeHitbox> ? out,
override
Casts the ray and returns the nearest hit, if any.
The hitboxes are visited from the nearest to the farthest, by the point where the ray enters their bounding boxes, and the search stops as soon as the best hit so far is nearer than the next hitbox, so that most hitboxes never have their ShapeHitbox.rayIntersection called. Between hitboxes hit at exactly the same distance, the one whose box is entered first wins.
Hitboxes that rays should go through, like trigger zones, are best left
out with hitboxFilter, which skips them before any work, rather than
with a ShapeHitbox.rayIntersection that always returns null, as such
hitboxes still have to be ordered.
Implementation
@override
RaycastResult<ShapeHitbox>? raycast(
Ray2 ray, {
double? maxDistance,
bool Function(ShapeHitbox candidate)? hitboxFilter,
List<ShapeHitbox>? ignoreHitboxes,
RaycastResult<ShapeHitbox>? out,
}) {
var finalResult = out?..reset();
final limit = maxDistance ?? double.infinity;
final isNested = _isCasting;
final candidates = isNested ? _RaycastCandidates() : _candidates;
_isCasting = true;
try {
for (final item in items) {
if (!_isCandidate(item, hitboxFilter, ignoreHitboxes)) {
continue;
}
final entry = ray.entryDistanceToAabb2(item.aabb);
if (entry < 0 || entry > limit) {
continue;
}
candidates.add(entry, item);
}
candidates.order();
// Takes the hitboxes out from the nearest to the farthest. Most of the
// time the loop ends after a few, so the rest are never ordered.
while (candidates.isNotEmpty) {
// A hit can not be nearer than the entry point to its box.
if ((finalResult?.isActive ?? false) &&
finalResult!.distance! <= candidates.nearestDistance) {
break;
}
final hitbox = candidates.removeNearest();
final currentResult = hitbox.rayIntersection(
ray,
out: _temporaryRaycastResult,
);
finalResult = _nearer(finalResult, currentResult, limit);
}
} finally {
// Do not keep the hitboxes alive through the candidates, also if one of
// the callbacks throws.
candidates.clear();
if (!isNested) {
_isCasting = false;
}
}
return (finalResult?.isActive ?? false) ? finalResult : null;
}