Skip to content
New issue

Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.

By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.

Already on GitHub? Sign in to your account

Use masks instead of collision layers for SpatialQueryFilter #61

Merged
merged 2 commits into from
Jul 8, 2023
Merged
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
54 changes: 33 additions & 21 deletions src/plugins/spatial_query/query_filter.rs
Original file line number Diff line number Diff line change
@@ -1,6 +1,6 @@
use bevy::{prelude::*, utils::HashSet};

use crate::prelude::CollisionLayers;
use crate::prelude::*;

/// Rules that determine which colliders are taken into account in [spatial queries](crate::spatial_query).
///
Expand All @@ -16,34 +16,53 @@ use crate::prelude::CollisionLayers;
/// fn setup(mut commands: Commands) {
/// let object = commands.spawn(Collider::ball(0.5)).id();
///
/// // A query filter that only includes one layer and excludes the `object` entity
/// // A query filter that has three collision masks and excludes the `object` entity
/// let query_filter = SpatialQueryFilter::new()
/// .with_layers(CollisionLayers::from_bits(0b1111, 0b0001))
/// .with_masks_from_bits(0b1011)
/// .without_entities([object]);
///
/// // Spawn a ray caster with the query filter
/// commands.spawn(RayCaster::default().with_query_filter(query_filter));
/// }
/// ```
#[derive(Clone, Default)]
#[derive(Clone)]
pub struct SpatialQueryFilter {
/// If set, only the colliers with compatible [collision layers](CollisionLayers) will be taken into account
/// in [spatial queries](crate::spatial_query).
pub layers: Option<CollisionLayers>,
/// Specifies which [collision groups](CollisionLayers) will be included in a [spatial query](crate::spatial_query).
pub masks: u32,
/// Entities that will not be included in [spatial queries](crate::spatial_query).
pub excluded_entities: HashSet<Entity>,
}

impl Default for SpatialQueryFilter {
fn default() -> Self {
Self {
masks: 0xffff_ffff,
excluded_entities: default(),
}
}
}

impl SpatialQueryFilter {
/// Creates a new [`SpatialQueryFilter`] that doesn't exclude any colliders.
pub fn new() -> Self {
Self::default()
}

/// Sets the layers that will be included in [spatial queries](crate::spatial_query).
/// Colliders that have incompatible [collision layers](CollisionLayers) will be excluded.
pub fn with_layers(mut self, layers: CollisionLayers) -> Self {
self.layers = Some(layers);
/// Sets the masks of the filter configuration using a bitmask. Colliders with the corresponding
/// [collision group](CollisionLayers) will be included in the [spatial query](crate::spatial_query).
pub fn with_masks_from_bits(mut self, masks: u32) -> Self {
self.masks = masks;
self
}

/// Sets the masks of the filter configuration using a list of [layers](PhysicsLayer).
/// Colliders with the corresponding [collision groups](CollisionLayers) will be included
/// in the [spatial query](crate::spatial_query).
pub fn with_masks(mut self, masks: impl IntoIterator<Item = impl PhysicsLayer>) -> Self {
self.masks = 0;
for mask in masks.into_iter().map(|l| l.to_bits()) {
self.masks |= mask;
}
self
}

Expand All @@ -58,15 +77,8 @@ impl SpatialQueryFilter {
/// filter configuration.
pub fn test(&self, entity: Entity, layers: CollisionLayers) -> bool {
!self.excluded_entities.contains(&entity)
&& (self.layers.is_none() || self.layers.is_some_and(|l| l.interacts_with(layers)))
}
}

impl From<CollisionLayers> for SpatialQueryFilter {
fn from(value: CollisionLayers) -> Self {
Self {
layers: Some(value),
..default()
}
&& CollisionLayers::from_bits(0xffff_ffff, self.masks).interacts_with(
CollisionLayers::from_bits(layers.groups_bits(), 0xffff_ffff),
)
}
}