diff --git a/src/components/rotation.rs b/src/components/rotation.rs index 4d6e6f9f..f836d64a 100644 --- a/src/components/rotation.rs +++ b/src/components/rotation.rs @@ -217,6 +217,13 @@ impl From for Scalar { } } +#[cfg(feature = "2d")] +impl From for Rotation { + fn from(radians: Scalar) -> Self { + Self::from_radians(radians) + } +} + #[cfg(feature = "2d")] impl From for Quaternion { fn from(rot: Rotation) -> Self { diff --git a/src/plugins/debug/configuration.rs b/src/plugins/debug/configuration.rs index f29c3a71..4b22bbc2 100644 --- a/src/plugins/debug/configuration.rs +++ b/src/plugins/debug/configuration.rs @@ -24,6 +24,20 @@ pub struct PhysicsDebugConfig { pub joint_anchor_color: Option, /// The color of the lines drawn between joint anchors, indicating the separation. pub joint_separation_color: Option, + /// The color used for the rays in [raycasts](spatial_query#ray-casting). + pub raycast_color: Option, + /// The color used for the hit points in [raycasts](spatial_query#ray-casting). + pub raycast_point_color: Option, + /// The color used for the hit normals in [raycasts](spatial_query#ray-casting). + pub raycast_normal_color: Option, + /// The color used for the ray in [shapecasts](spatial_query#shape-casting). + pub shapecast_color: Option, + /// The color used for the shape in [shapecasts](spatial_query#shape-casting). + pub shapecast_shape_color: Option, + /// The color used for the hit points in [shapecasts](spatial_query#shape-casting). + pub shapecast_point_color: Option, + /// The color used for the hit normals in [shapecasts](spatial_query#shape-casting). + pub shapecast_normal_color: Option, /// Determines if the visibility of entities with [colliders](Collider) should be set to `Visibility::Hidden`, /// which will only show the debug renders. pub hide_meshes: bool, @@ -43,6 +57,13 @@ impl Default for PhysicsDebugConfig { contact_color: None, joint_anchor_color: Some(Color::PINK), joint_separation_color: Some(Color::RED), + raycast_color: Some(Color::RED), + raycast_point_color: Some(Color::YELLOW), + raycast_normal_color: Some(Color::PINK), + shapecast_color: Some(Color::RED), + shapecast_shape_color: Some(Color::rgb(0.4, 0.6, 1.0)), + shapecast_point_color: Some(Color::YELLOW), + shapecast_normal_color: Some(Color::PINK), hide_meshes: false, } } @@ -63,6 +84,13 @@ impl PhysicsDebugConfig { contact_color: Some(Color::CYAN), joint_anchor_color: Some(Color::PINK), joint_separation_color: Some(Color::RED), + raycast_color: Some(Color::RED), + raycast_point_color: Some(Color::YELLOW), + raycast_normal_color: Some(Color::PINK), + shapecast_color: Some(Color::RED), + shapecast_shape_color: Some(Color::rgb(0.4, 0.6, 1.0)), + shapecast_point_color: Some(Color::YELLOW), + shapecast_normal_color: Some(Color::PINK), hide_meshes: true, } } @@ -80,6 +108,13 @@ impl PhysicsDebugConfig { contact_color: None, joint_anchor_color: None, joint_separation_color: None, + raycast_color: None, + raycast_point_color: None, + raycast_normal_color: None, + shapecast_color: None, + shapecast_shape_color: None, + shapecast_point_color: None, + shapecast_normal_color: None, hide_meshes: false, } } @@ -122,10 +157,10 @@ impl PhysicsDebugConfig { /// Creates a [`PhysicsDebugConfig`] configuration with given colors for /// joint anchors and separation distances. Other debug rendering options will be disabled. - pub fn joints(anchor_color: Color, separation_color: Color) -> Self { + pub fn joints(anchor_color: Option, separation_color: Option) -> Self { Self { - joint_anchor_color: Some(anchor_color), - joint_separation_color: Some(separation_color), + joint_anchor_color: anchor_color, + joint_separation_color: separation_color, ..Self::none() } } @@ -160,6 +195,43 @@ impl PhysicsDebugConfig { self } + /// Sets the colors used for debug rendering joints. + pub fn with_joint_colors(anchor_color: Option, separation_color: Option) -> Self { + Self { + joint_anchor_color: anchor_color, + joint_separation_color: separation_color, + ..Self::none() + } + } + + /// Sets the colors used for debug rendering raycasts. + pub fn with_raycast_colors( + mut self, + ray: Option, + hit_point: Option, + hit_normal: Option, + ) -> Self { + self.raycast_color = ray; + self.raycast_point_color = hit_point; + self.raycast_normal_color = hit_normal; + self + } + + /// Sets the colors used for debug rendering shapecasts. + pub fn with_shapecast_colors( + mut self, + ray: Option, + shape: Option, + hit_point: Option, + hit_normal: Option, + ) -> Self { + self.shapecast_color = ray; + self.shapecast_shape_color = shape; + self.shapecast_point_color = hit_point; + self.shapecast_normal_color = hit_normal; + self + } + /// Sets the visibility of the entity's visual mesh. pub fn with_mesh_visibility(mut self, is_visible: bool) -> Self { self.hide_meshes = !is_visible; @@ -196,6 +268,23 @@ impl PhysicsDebugConfig { self.joint_separation_color = None; self } + + /// Disables raycast debug rendering. + pub fn without_raycasts(mut self) -> Self { + self.raycast_color = None; + self.raycast_point_color = None; + self.raycast_normal_color = None; + self + } + + /// Disables shapecast debug rendering. + pub fn without_shapecasts(mut self) -> Self { + self.shapecast_color = None; + self.shapecast_shape_color = None; + self.shapecast_point_color = None; + self.shapecast_normal_color = None; + self + } } /// A component for the debug render configuration of an entity. diff --git a/src/plugins/debug/mod.rs b/src/plugins/debug/mod.rs index 6e55e815..5d94999d 100644 --- a/src/plugins/debug/mod.rs +++ b/src/plugins/debug/mod.rs @@ -21,6 +21,8 @@ use bevy::{ecs::query::Has, prelude::*}; /// - Use different colors for [sleeping](Sleeping) bodies /// - [Contacts] /// - [Joints](joints) +/// - [`RayCaster`] +/// - [`ShapeCaster`] /// - Changing the visibility of entities to only show debug rendering /// /// By default, only axes, colliders and joints are debug rendered. You can use the [`PhysicsDebugConfig`] @@ -72,6 +74,8 @@ impl Plugin for PhysicsDebugPlugin { debug_render_joints::, debug_render_joints::, debug_render_joints::, + debug_render_raycasts, + debug_render_shapecasts, change_mesh_visibility, ) .after(PhysicsSet::StepSimulation) @@ -307,6 +311,70 @@ fn debug_render_joints( } } +fn debug_render_raycasts( + query: Query<(&RayCaster, &RayHits)>, + mut debug_renderer: PhysicsDebugRenderer, + config: Res, +) { + for (ray, hits) in &query { + let ray_color = config + .raycast_color + .unwrap_or(Color::rgba(0.0, 0.0, 0.0, 0.0)); + let point_color = config + .raycast_point_color + .unwrap_or(Color::rgba(0.0, 0.0, 0.0, 0.0)); + let normal_color = config + .raycast_normal_color + .unwrap_or(Color::rgba(0.0, 0.0, 0.0, 0.0)); + + debug_renderer.draw_raycast( + ray.global_origin(), + ray.global_direction(), + // f32::MAX renders nothing, but this number seems to be fine :P + ray.max_time_of_impact.min(1_000_000_000_000_000_000.0), + hits.as_slice(), + ray_color, + point_color, + normal_color, + ); + } +} + +fn debug_render_shapecasts( + query: Query<(&ShapeCaster, &ShapeHits)>, + mut debug_renderer: PhysicsDebugRenderer, + config: Res, +) { + for (shape_caster, hits) in &query { + let ray_color = config + .shapecast_color + .unwrap_or(Color::rgba(0.0, 0.0, 0.0, 0.0)); + let shape_color = config + .shapecast_shape_color + .unwrap_or(Color::rgba(0.0, 0.0, 0.0, 0.0)); + let point_color = config + .shapecast_point_color + .unwrap_or(Color::rgba(0.0, 0.0, 0.0, 0.0)); + let normal_color = config + .shapecast_normal_color + .unwrap_or(Color::rgba(0.0, 0.0, 0.0, 0.0)); + + debug_renderer.draw_shapecast( + &shape_caster.shape, + shape_caster.global_origin(), + shape_caster.global_shape_rotation(), + shape_caster.global_direction(), + // f32::MAX renders nothing, but this number seems to be fine :P + shape_caster.max_time_of_impact.min(1_000_000_000_000_000.0), + hits.as_slice(), + ray_color, + shape_color, + point_color, + normal_color, + ); + } +} + type MeshVisibilityQueryFilter = ( Or<(With, With)>, Or<(Changed, Without)>, diff --git a/src/plugins/debug/renderer.rs b/src/plugins/debug/renderer.rs index ac7bca29..d4ce9fc7 100644 --- a/src/plugins/debug/renderer.rs +++ b/src/plugins/debug/renderer.rs @@ -66,6 +66,47 @@ impl<'w, 's> PhysicsDebugRenderer<'w, 's> { } } + /// Draws an arrow from `a` to `b` with an arrowhead that has a length of `head_length` + /// and a width of `head_width`. + pub fn draw_arrow( + &mut self, + a: Vector, + b: Vector, + head_length: Scalar, + head_width: Scalar, + color: Color, + ) { + self.draw_line(a, b, color); + + let dir = (b - a).normalize_or_zero(); + + #[cfg(feature = "2d")] + { + let v = head_width * 0.5 * Vector::new(-dir.y, dir.x); + self.draw_line(b, b - head_length * dir + v, color); + self.draw_line(b, b - head_length * dir - v, color); + } + + #[cfg(feature = "3d")] + { + let back = Vector::NEG_Z; + let up = dir.try_normalize().unwrap_or(Vector::Y); + let right = up + .cross(back) + .try_normalize() + .unwrap_or_else(|| up.any_orthonormal_vector()); + let up = back.cross(right); + let q = Quaternion::from_mat3(&Matrix3::from_cols(right, up, back)); + + self.draw_collider( + &Collider::cone(head_length, head_width * 0.5), + &Position(b - dir * head_length * 0.5), + &Rotation(q), + color, + ); + } + } + /// Draws a collider shape with a given position and rotation. #[allow(clippy::unnecessary_cast)] pub fn draw_collider( @@ -340,4 +381,119 @@ impl<'w, 's> PhysicsDebugRenderer<'w, 's> { TypedShape::Custom(_) => (), } } + + /// Draws the results of a [raycast](SpatialQuery#ray-casting). + #[allow(clippy::too_many_arguments)] + pub fn draw_raycast( + &mut self, + origin: Vector, + direction: Vector, + max_time_of_impact: Scalar, + hits: &[RayHitData], + ray_color: Color, + point_color: Color, + normal_color: Color, + ) { + let max_toi = hits + .iter() + .max_by(|a, b| a.time_of_impact.total_cmp(&b.time_of_impact)) + .map_or(max_time_of_impact, |hit| hit.time_of_impact); + + // Draw ray as arrow + #[cfg(feature = "2d")] + self.draw_arrow(origin, origin + direction * max_toi, 8.0, 8.0, ray_color); + #[cfg(feature = "3d")] + self.draw_arrow(origin, origin + direction * max_toi, 0.1, 0.1, ray_color); + + // Draw all hit points and normals + for hit in hits { + let point = origin + direction * hit.time_of_impact; + + // Draw hit point + #[cfg(feature = "2d")] + self.gizmos + .circle_2d(point.adjust_precision(), 3.0, point_color); + #[cfg(feature = "3d")] + self.gizmos + .sphere(point.adjust_precision(), default(), 0.025, point_color); + + // Draw hit normal as arrow + #[cfg(feature = "2d")] + self.draw_arrow(point, point + hit.normal * 30.0, 8.0, 8.0, normal_color); + #[cfg(feature = "3d")] + self.draw_arrow(point, point + hit.normal * 0.5, 0.1, 0.1, normal_color); + } + } + + /// Draws the results of a [shapecast](SpatialQuery#shape-casting). + #[allow(clippy::too_many_arguments)] + pub fn draw_shapecast( + &mut self, + shape: &Collider, + origin: Vector, + shape_rotation: impl Into, + direction: Vector, + max_time_of_impact: Scalar, + hits: &[ShapeHitData], + ray_color: Color, + shape_color: Color, + point_color: Color, + normal_color: Color, + ) { + let shape_rotation = shape_rotation.into(); + #[cfg(feature = "3d")] + let shape_rotation = Rotation(shape_rotation.normalize()); + + let max_toi = hits + .iter() + .max_by(|a, b| a.time_of_impact.total_cmp(&b.time_of_impact)) + .map_or(max_time_of_impact, |hit| hit.time_of_impact); + + // Draw collider at origin + self.draw_collider(shape, &Position(origin), &shape_rotation, shape_color); + + // Draw arrow from origin to position of shape at final hit + // TODO: We could render the swept collider outline instead + #[cfg(feature = "2d")] + self.draw_arrow(origin, origin + max_toi * direction, 8.0, 8.0, ray_color); + #[cfg(feature = "3d")] + self.draw_arrow(origin, origin + max_toi * direction, 0.1, 0.1, ray_color); + + // Draw all hit points, normals and the shape at the hit points + for hit in hits { + // Draw hit point + #[cfg(feature = "2d")] + self.gizmos + .circle_2d(hit.point1.adjust_precision(), 3.0, point_color); + #[cfg(feature = "3d")] + self.gizmos + .sphere(hit.point1.adjust_precision(), default(), 0.025, point_color); + + // Draw hit normal as arrow + #[cfg(feature = "2d")] + self.draw_arrow( + hit.point1, + hit.point1 + hit.normal1 * 30.0, + 8.0, + 8.0, + normal_color, + ); + #[cfg(feature = "3d")] + self.draw_arrow( + hit.point1, + hit.point1 + hit.normal1 * 0.5, + 0.1, + 0.1, + normal_color, + ); + + // Draw collider at hit point + self.draw_collider( + shape, + &Position(origin + hit.time_of_impact * direction), + &shape_rotation, + Color::rgba(shape_color.r(), shape_color.g(), shape_color.b(), 0.3), + ); + } + } } diff --git a/src/plugins/spatial_query/mod.rs b/src/plugins/spatial_query/mod.rs index fc09b30c..b4fa6cd3 100644 --- a/src/plugins/spatial_query/mod.rs +++ b/src/plugins/spatial_query/mod.rs @@ -1,7 +1,7 @@ //! **Spatial queries** are a way to get information about the environment. They perform geometric queries //! on [colliders](Collider) and retrieve data about intersections. //! -//! There are four types of spatial queries: [ray casts](#ray-casting), [shape casts](#shape-casting), +//! There are four types of spatial queries: [raycasts](#ray-casting), [shapecasts](#shape-casting), //! [point projection](#point-projection) and [intersection tests](#intersection-tests). //! All spatial queries can be done using the various methods provided by the [`SpatialQuery`] system parameter. //! @@ -19,9 +19,9 @@ //! The time of impact refers to how long the ray travelled, which is essentially the distance from the ray origin to //! the point of intersection. //! -//! There are two ways to perform ray casts. +//! There are two ways to perform raycasts. //! -//! 1. For simple ray casts, use the [`RayCaster`] component. It returns the results of the ray cast +//! 1. For simple raycasts, use the [`RayCaster`] component. It returns the results of the raycast //! in the [`RayHits`] component every frame. It uses local coordinates, so it will automatically follow the entity //! it's attached to or its parent. //! 2. When you need more control or don't want to cast every frame, use the ray casting methods provided by @@ -74,9 +74,9 @@ //! normals will be stored in [`ShapeHitData`]. The time of impact refers to how long the shape travelled before the initial //! hit, which is essentially the distance from the shape origin to the global point of intersection. //! -//! There are two ways to perform shape casts. +//! There are two ways to perform shapecasts. //! -//! 1. For simple shape casts, use the [`ShapeCaster`] component. It returns the results of the shape cast +//! 1. For simple shapecasts, use the [`ShapeCaster`] component. It returns the results of the shapecast //! in the [`ShapeHits`] component every frame. It uses local coordinates, so it will automatically follow the entity //! it's attached to or its parent. //! 2. When you need more control or don't want to cast every frame, use the shape casting methods provided by diff --git a/src/plugins/spatial_query/pipeline.rs b/src/plugins/spatial_query/pipeline.rs index c2c354ed..3456cacd 100644 --- a/src/plugins/spatial_query/pipeline.rs +++ b/src/plugins/spatial_query/pipeline.rs @@ -213,7 +213,7 @@ impl SpatialQueryPipeline { } /// Casts a [ray](spatial_query#ray-casting) and computes all [hits](RayHitData), calling the given `callback` - /// for each hit. The ray cast stops when `callback` returns false or all hits have been found. + /// for each hit. The raycast stops when `callback` returns false or all hits have been found. /// /// Note that the order of the results is not guaranteed. /// @@ -381,7 +381,7 @@ impl SpatialQueryPipeline { } /// Casts a [shape](spatial_query#shape-casting) with a given rotation and computes computes all [hits](ShapeHitData) - /// in the order of the time of impact, calling the given `callback` for each hit. The shape cast stops when + /// in the order of the time of impact, calling the given `callback` for each hit. The shapecast stops when /// `callback` returns false or all hits have been found. /// /// ## Arguments diff --git a/src/plugins/spatial_query/ray_caster.rs b/src/plugins/spatial_query/ray_caster.rs index a3b129a7..668ea074 100644 --- a/src/plugins/spatial_query/ray_caster.rs +++ b/src/plugins/spatial_query/ray_caster.rs @@ -16,12 +16,12 @@ use parry::query::{ /// and add them to the [`RayHits`] component. Each hit has a `time_of_impact` property /// which refers to how long the ray travelled, i.e. the distance between the `origin` and the point of intersection. /// -/// The [`RayCaster`] is the easiest way to handle simple ray casts. If you want more control and don't want to -/// perform ray casts every frame, consider using the [`SpatialQuery`] system parameter. +/// The [`RayCaster`] is the easiest way to handle simple raycasts. If you want more control and don't want to +/// perform raycasts every frame, consider using the [`SpatialQuery`] system parameter. /// /// ## Hit count and order /// -/// The results of a ray cast are in an arbitrary order by default. You can iterate over them in the order of +/// The results of a raycast are in an arbitrary order by default. You can iterate over them in the order of /// time of impact with the [`RayHits::iter_sorted`](RayHits#method.iter_sorted) method. /// /// You can configure the maximum amount of hits for a ray using `max_hits`. By default this is unbounded, @@ -158,7 +158,7 @@ impl RayCaster { } /// Sets the ray caster's [query filter](SpatialQueryFilter) that controls which colliders - /// should be included or excluded by ray casts. + /// should be included or excluded by raycasts. pub fn with_query_filter(mut self, query_filter: SpatialQueryFilter) -> Self { self.query_filter = query_filter; self @@ -356,7 +356,7 @@ impl MapEntities for RayHits { } } -/// Data related to a hit during a [ray cast](spatial_query#ray-casting). +/// Data related to a hit during a [raycast](spatial_query#ray-casting). #[derive(Clone, Copy, Debug, PartialEq)] pub struct RayHitData { /// The entity of the collider that was hit by the ray. diff --git a/src/plugins/spatial_query/shape_caster.rs b/src/plugins/spatial_query/shape_caster.rs index 619b9ef7..3fb936e8 100644 --- a/src/plugins/spatial_query/shape_caster.rs +++ b/src/plugins/spatial_query/shape_caster.rs @@ -11,7 +11,7 @@ use parry::query::details::TOICompositeShapeShapeBestFirstVisitor; /// line and computes hits with colliders. This is often used to determine how far an object can move /// in a direction before it hits something. /// -/// Each shape cast is defined by a `shape` (a [`Collider`]), its local `shape_rotation`, a local `origin` and +/// Each shapecast is defined by a `shape` (a [`Collider`]), its local `shape_rotation`, a local `origin` and /// a local `direction`. The [`ShapeCaster`] will find each hit and add them to the [`ShapeHits`] component in /// the order of the time of impact. /// @@ -19,7 +19,7 @@ use parry::query::details::TOICompositeShapeShapeBestFirstVisitor; /// is one by default. This can be configured through the `max_hits` property. /// /// The [`ShapeCaster`] is the easiest way to handle simple shape casting. If you want more control and don't want -/// to perform shape casts on every frame, consider using the [`SpatialQuery`] system parameter. +/// to perform shapecasts on every frame, consider using the [`SpatialQuery`] system parameter. /// /// ## Example /// @@ -80,23 +80,23 @@ pub struct ShapeCaster { /// The global rotation of the shape. #[cfg(feature = "3d")] global_shape_rotation: Quaternion, - /// The local direction of the shape cast relative to the [`Rotation`] of the shape caster entity or its parent. + /// The local direction of the shapecast relative to the [`Rotation`] of the shape caster entity or its parent. /// /// To get the global direction, use the `global_direction` method. pub direction: Vector, - /// The global direction of the shape cast. + /// The global direction of the shapecast. global_direction: Vector, /// The maximum distance the shape can travel. By default this is infinite, so the shape will travel /// until a hit is found. pub max_time_of_impact: Scalar, /// The maximum number of hits allowed. By default this is one and only the first hit is returned. pub max_hits: u32, - /// Controls how the shape cast behaves when the shape is already penetrating a [collider](Collider) + /// Controls how the shapecast behaves when the shape is already penetrating a [collider](Collider) /// at the shape origin. /// /// If set to true **and** the shape is being cast in a direction where it will eventually stop penetrating, - /// the shape cast will not stop immediately, and will instead continue until another hit.\ - /// If set to false, the shape cast will stop immediately and return the hit. This is the default. + /// the shapecast will not stop immediately, and will instead continue until another hit.\ + /// If set to false, the shapecast will stop immediately and return the hit. This is the default. pub ignore_origin_penetration: bool, /// Rules that determine which colliders are taken into account in the query. pub query_filter: SpatialQueryFilter, @@ -168,12 +168,12 @@ impl ShapeCaster { self } - /// Controls how the shape cast behaves when the shape is already penetrating a [collider](Collider) + /// Controls how the shapecast behaves when the shape is already penetrating a [collider](Collider) /// at the shape origin. /// /// If set to true **and** the shape is being cast in a direction where it will eventually stop penetrating, - /// the shape cast will not stop immediately, and will instead continue until another hit.\ - /// If set to false, the shape cast will stop immediately and return the hit. This is the default. + /// the shapecast will not stop immediately, and will instead continue until another hit.\ + /// If set to false, the shapecast will stop immediately and return the hit. This is the default. pub fn with_ignore_origin_penetration(mut self, ignore: bool) -> Self { self.ignore_origin_penetration = ignore; self @@ -192,7 +192,7 @@ impl ShapeCaster { } /// Sets the shape caster's [query filter](SpatialQueryFilter) that controls which colliders - /// should be included or excluded by shape casts. + /// should be included or excluded by shapecasts. pub fn with_query_filter(mut self, query_filter: SpatialQueryFilter) -> Self { self.query_filter = query_filter; self @@ -338,7 +338,7 @@ pub struct ShapeHits { } impl ShapeHits { - /// Returns a slice over the shape cast hits. + /// Returns a slice over the shapecast hits. pub fn as_slice(&self) -> &[ShapeHitData] { &self.vector[0..self.count as usize] } @@ -374,7 +374,7 @@ impl MapEntities for ShapeHits { } } -/// Data related to a hit during a [shape cast](spatial_query#shape-casting). +/// Data related to a hit during a [shapecast](spatial_query#shape-casting). #[derive(Clone, Copy, Debug, PartialEq)] pub struct ShapeHitData { /// The entity of the collider that was hit by the shape. diff --git a/src/plugins/spatial_query/system_param.rs b/src/plugins/spatial_query/system_param.rs index 78c20460..275495b0 100644 --- a/src/plugins/spatial_query/system_param.rs +++ b/src/plugins/spatial_query/system_param.rs @@ -18,7 +18,7 @@ use bevy::{ecs::system::SystemParam, prelude::*}; /// - Shape intersections: [`shape_intersections`](SpatialQuery#method.shape_intersections) /// [`shape_intersections_callback`](SpatialQuery#method.shape_intersections_callback) /// -/// For simple ray casts and shape casts, consider using the [`RayCaster`] and [`ShapeCaster`] components that +/// For simple raycasts and shapecasts, consider using the [`RayCaster`] and [`ShapeCaster`] components that /// provide a more ECS-based approach and perform casts on every frame. /// /// ## Ray casting example @@ -195,7 +195,7 @@ impl<'w, 's> SpatialQuery<'w, 's> { } /// Casts a [ray](spatial_query#ray-casting) and computes all [hits](RayHitData), calling the given `callback` - /// for each hit. The ray cast stops when `callback` returns false or all hits have been found. + /// for each hit. The raycast stops when `callback` returns false or all hits have been found. /// /// Note that the order of the results is not guaranteed. /// @@ -395,7 +395,7 @@ impl<'w, 's> SpatialQuery<'w, 's> { } /// Casts a [shape](spatial_query#shape-casting) with a given rotation and computes computes all [hits](ShapeHitData) - /// in the order of the time of impact, calling the given `callback` for each hit. The shape cast stops when + /// in the order of the time of impact, calling the given `callback` for each hit. The shapecast stops when /// `callback` returns false or all hits have been found. /// /// ## Arguments