raycast method

  1. @override
RaycastResult<ShapeHitbox>? raycast(
  1. Ray2 ray, {
  2. double? maxDistance,
  3. bool hitboxFilter(
    1. ShapeHitbox candidate
    )?,
  4. List<ShapeHitbox>? ignoreHitboxes,
  5. RaycastResult<ShapeHitbox>? out,
})
override

Returns the first hitbox that the given ray hits and the associated intersection information; or null if the ray doesn't hit any hitbox.

maxDistance can be provided to limit the raycast to only return hits within this distance from the ray origin.

You can provide a hitboxFilter callback to define which hitboxes to consider and which to ignore. This callback will be called with every prospective hitbox, and only if the callback returns true will the hitbox be considered. Otherwise, the ray will go straight through it. One common use case is ignoring the component that is shooting the ray.

If you have a list of hitboxes to ignore in advance, you can provide them via the ignoreHitboxes argument.

If out is provided that object will be modified and returned with the result.

Implementation

@override
RaycastResult<ShapeHitbox>? raycast(
  Ray2 ray, {
  double? maxDistance,
  bool Function(ShapeHitbox candidate)? hitboxFilter,
  List<ShapeHitbox>? ignoreHitboxes,
  RaycastResult<ShapeHitbox>? out,
}) {
  var finalResult = out?..reset();
  _updateRayAabb(ray, maxDistance);
  for (final item in items) {
    if (ignoreHitboxes?.contains(item) ?? false) {
      continue;
    }
    if (hitboxFilter != null) {
      if (!hitboxFilter(item)) {
        continue;
      }
    }
    if (!item.aabb.intersectsWithAabb2(_temporaryRayAabb)) {
      continue;
    }
    final currentResult =
        item.rayIntersection(ray, out: _temporaryRaycastResult);
    final possiblyFirstResult = !(finalResult?.isActive ?? false);
    if (currentResult != null &&
        (possiblyFirstResult ||
            currentResult.distance! < finalResult!.distance!) &&
        currentResult.distance! <= (maxDistance ?? double.infinity)) {
      if (finalResult == null) {
        finalResult = currentResult.clone();
      } else {
        finalResult.setFrom(currentResult);
      }
    }
  }
  return (finalResult?.isActive ?? false) ? finalResult : null;
}