use crate::prelude::*;
use bevy::{
ecs::{
entity::{EntityMapper, MapEntities},
lifecycle::HookContext,
world::DeferredWorld,
},
prelude::*,
};
#[derive(Component, Clone, Debug, PartialEq, Reflect)]
#[cfg_attr(feature = "serialize", derive(serde::Serialize, serde::Deserialize))]
#[cfg_attr(feature = "serialize", reflect(Serialize, Deserialize))]
#[reflect(Debug, Component, PartialEq)]
#[component(on_add = on_add_ray_caster)]
#[require(RayHits)]
pub struct RayCaster {
pub enabled: bool,
pub origin: Vector,
global_origin: Vector,
pub direction: Dir,
global_direction: Dir,
pub max_hits: u32,
#[doc(alias = "max_time_of_impact")]
pub max_distance: Scalar,
pub solid: bool,
pub ignore_self: bool,
pub query_filter: SpatialQueryFilter,
}
impl Default for RayCaster {
fn default() -> Self {
Self {
enabled: true,
origin: Vector::ZERO,
global_origin: Vector::ZERO,
direction: Dir::X,
global_direction: Dir::X,
max_distance: Scalar::MAX,
max_hits: u32::MAX,
solid: true,
ignore_self: true,
query_filter: SpatialQueryFilter::default(),
}
}
}
impl From<Ray> for RayCaster {
fn from(ray: Ray) -> Self {
RayCaster::from_ray(ray)
}
}
impl RayCaster {
pub fn new(origin: Vector, direction: Dir) -> Self {
Self {
origin,
direction,
..default()
}
}
pub fn from_ray(ray: Ray) -> Self {
Self {
origin: ray.origin.adjust_precision(),
direction: ray.direction,
..default()
}
}
pub fn with_origin(mut self, origin: Vector) -> Self {
self.origin = origin;
self
}
pub fn with_direction(mut self, direction: Dir) -> Self {
self.direction = direction;
self
}
pub fn with_solidness(mut self, solid: bool) -> Self {
self.solid = solid;
self
}
pub fn with_ignore_self(mut self, ignore: bool) -> Self {
self.ignore_self = ignore;
self
}
pub fn with_max_distance(mut self, max_distance: Scalar) -> Self {
self.max_distance = max_distance;
self
}
pub fn with_max_hits(mut self, max_hits: u32) -> Self {
self.max_hits = max_hits;
self
}
pub fn with_query_filter(mut self, query_filter: SpatialQueryFilter) -> Self {
self.query_filter = query_filter;
self
}
pub fn enable(&mut self) {
self.enabled = true;
}
pub fn disable(&mut self) {
self.enabled = false;
}
pub fn global_origin(&self) -> Vector {
self.global_origin
}
pub fn global_direction(&self) -> Dir {
self.global_direction
}
pub(crate) fn set_global_origin(&mut self, global_origin: Vector) {
self.global_origin = global_origin;
}
pub(crate) fn set_global_direction(&mut self, global_direction: Dir) {
self.global_direction = global_direction;
}
#[cfg(all(
feature = "default-collider",
any(feature = "parry-f32", feature = "parry-f64")
))]
pub(crate) fn cast(
&mut self,
caster_entity: Entity,
hits: &mut RayHits,
spatial_query: &SpatialQuery,
) {
if self.ignore_self {
self.query_filter.excluded_entities.insert(caster_entity);
} else {
self.query_filter.excluded_entities.remove(&caster_entity);
}
hits.clear();
if self.max_hits == 1 {
let first_hit = spatial_query.cast_ray(
self.global_origin(),
self.global_direction(),
self.max_distance,
self.solid,
&self.query_filter,
);
if let Some(hit) = first_hit {
hits.push(hit);
}
} else {
hits.extend(spatial_query.ray_hits(
self.global_origin(),
self.global_direction(),
self.max_distance,
self.max_hits,
self.solid,
&self.query_filter,
));
}
}
}
fn on_add_ray_caster(mut world: DeferredWorld, ctx: HookContext) {
let ray_caster = world.get::<RayCaster>(ctx.entity).unwrap();
let max_hits = if ray_caster.max_hits == u32::MAX {
10
} else {
ray_caster.max_hits as usize
};
world.get_mut::<RayHits>(ctx.entity).unwrap().0 = Vec::with_capacity(max_hits);
}
#[cfg_attr(feature = "2d", doc = "use avian2d::prelude::*;")]
#[cfg_attr(feature = "3d", doc = "use avian3d::prelude::*;")]
#[derive(Component, Clone, Debug, Default, Deref, DerefMut, PartialEq, Reflect)]
#[cfg_attr(feature = "serialize", derive(serde::Serialize, serde::Deserialize))]
#[cfg_attr(feature = "serialize", reflect(Serialize, Deserialize))]
#[reflect(Component, Debug, Default, PartialEq)]
pub struct RayHits(pub Vec<RayHitData>);
impl RayHits {
pub fn iter_sorted(&self) -> alloc::vec::IntoIter<RayHitData> {
let mut vector = self.as_slice().to_vec();
vector.sort_by(|a, b| a.distance.partial_cmp(&b.distance).unwrap());
vector.into_iter()
}
}
impl IntoIterator for RayHits {
type Item = RayHitData;
type IntoIter = alloc::vec::IntoIter<RayHitData>;
fn into_iter(self) -> Self::IntoIter {
self.0.into_iter()
}
}
impl<'a> IntoIterator for &'a RayHits {
type Item = &'a RayHitData;
type IntoIter = core::slice::Iter<'a, RayHitData>;
fn into_iter(self) -> Self::IntoIter {
self.0.iter()
}
}
impl<'a> IntoIterator for &'a mut RayHits {
type Item = &'a mut RayHitData;
type IntoIter = core::slice::IterMut<'a, RayHitData>;
fn into_iter(self) -> Self::IntoIter {
self.0.iter_mut()
}
}
impl MapEntities for RayHits {
fn map_entities<M: EntityMapper>(&mut self, entity_mapper: &mut M) {
for hit in self {
hit.map_entities(entity_mapper);
}
}
}
#[derive(Clone, Copy, Debug, PartialEq, Reflect)]
#[cfg_attr(feature = "serialize", derive(serde::Serialize, serde::Deserialize))]
#[cfg_attr(feature = "serialize", reflect(Serialize, Deserialize))]
#[reflect(Debug, PartialEq)]
pub struct RayHitData {
pub entity: Entity,
pub distance: Scalar,
pub normal: Vector,
}
impl MapEntities for RayHitData {
fn map_entities<M: EntityMapper>(&mut self, entity_mapper: &mut M) {
self.entity = entity_mapper.get_mapped(self.entity);
}
}