Skip to content

Commit 3fd6b17

Browse files
committed
add IgnoredCollisions component
1 parent f1a98d9 commit 3fd6b17

File tree

6 files changed

+92
-2
lines changed

6 files changed

+92
-2
lines changed

src/collision/broad_phase.rs

+23
Original file line numberDiff line numberDiff line change
@@ -182,6 +182,7 @@ fn collect_collision_pairs(
182182
intervals: ResMut<AabbIntervals>,
183183
mut broad_collision_pairs: ResMut<BroadCollisionPairs>,
184184
mut aabb_intersection_query: Query<&mut AabbIntersections>,
185+
ignored_collisions: Query<&IgnoredCollisions>,
185186
) {
186187
for mut intersections in &mut aabb_intersection_query {
187188
intersections.clear();
@@ -191,6 +192,7 @@ fn collect_collision_pairs(
191192
intervals,
192193
&mut broad_collision_pairs.0,
193194
&mut aabb_intersection_query,
195+
ignored_collisions,
194196
);
195197
}
196198

@@ -201,6 +203,7 @@ fn sweep_and_prune(
201203
mut intervals: ResMut<AabbIntervals>,
202204
broad_collision_pairs: &mut Vec<(Entity, Entity)>,
203205
aabb_intersection_query: &mut Query<&mut AabbIntersections>,
206+
ignored_collisions: Query<&IgnoredCollisions>,
204207
) {
205208
// Sort bodies along the x-axis using insertion sort, a sorting algorithm great for sorting nearly sorted lists.
206209
insertion_sort(&mut intervals.0, |a, b| a.2.min.x > b.2.min.x);
@@ -212,9 +215,29 @@ fn sweep_and_prune(
212215
for (i, (ent1, parent1, aabb1, layers1, store_intersections1, inactive1)) in
213216
intervals.0.iter().enumerate()
214217
{
218+
let ent1_ignored_collisions = ignored_collisions.get(*ent1).ok();
215219
for (ent2, parent2, aabb2, layers2, store_intersections2, inactive2) in
216220
intervals.0.iter().skip(i + 1)
217221
{
222+
// Check ignored collisions of `ent1`
223+
if ent1_ignored_collisions
224+
.as_ref()
225+
.map(|i| i.contains(ent2))
226+
.unwrap_or_default()
227+
{
228+
continue;
229+
}
230+
231+
// Check ignored collisions of `ent2`
232+
let ent2_ignored_collisions = ignored_collisions.get(*ent2).ok();
233+
if ent2_ignored_collisions
234+
.as_ref()
235+
.map(|i| i.contains(ent1))
236+
.unwrap_or_default()
237+
{
238+
continue;
239+
}
240+
218241
// x doesn't intersect; check this first so we can discard as soon as possible
219242
if aabb2.min.x > aabb1.max.x {
220243
break;

src/collision/collider/parry/mod.rs

+2-1
Original file line numberDiff line numberDiff line change
@@ -262,7 +262,7 @@ impl From<TrimeshFlags> for parry::shape::TriMeshFlags {
262262
/// ```
263263
///
264264
/// Colliders can be further configured using various components like [`Friction`], [`Restitution`],
265-
/// [`Sensor`], [`CollisionLayers`], and [`CollisionMargin`].
265+
/// [`Sensor`], [`IgnoredCollisions`], [`CollisionLayers`], and [`CollisionMargin`].
266266
///
267267
/// In addition, Avian automatically adds some other components for colliders, like the following:
268268
///
@@ -344,6 +344,7 @@ impl From<TrimeshFlags> for parry::shape::TriMeshFlags {
344344
/// - [Rigid bodies](RigidBody)
345345
/// - [Density](ColliderDensity)
346346
/// - [Friction] and [restitution](Restitution) (bounciness)
347+
/// - [Ignoring collisions](IgnoredCollisions)
347348
/// - [Collision layers](CollisionLayers)
348349
/// - [Sensors](Sensor)
349350
/// - [Collision margins for adding extra thickness to colliders](CollisionMargin)

src/collision/layers.rs

+2
Original file line numberDiff line numberDiff line change
@@ -257,6 +257,8 @@ impl Not for LayerMask {
257257
///
258258
/// [bitmasks]: https://en.wikipedia.org/wiki/Mask_(computing)
259259
///
260+
/// See also [`IgnoredCollisions`](crate::components::IgnoredCollisions).
261+
///
260262
/// ## Creation
261263
///
262264
/// Collision layers store memberships and filters using [`LayerMask`]s. A [`LayerMask`] can be created using

src/collision/mod.rs

+6
Original file line numberDiff line numberDiff line change
@@ -68,6 +68,12 @@ use indexmap::IndexMap;
6868
/// The collisions can be accessed at any time, but modifications to contacts should be performed
6969
/// in the [`PostProcessCollisions`] schedule. Otherwise, the physics solver will use the old contact data.
7070
///
71+
/// ### Ignoring collisions
72+
///
73+
/// You can attach an [`IgnoredCollisions`] component to an entity with a
74+
/// [`Collider`] to completely avoid collision detection between the entity and
75+
/// the entities contained within the [`IgnoredCollisions`] component.
76+
///
7177
/// ### Filtering and removing collisions
7278
///
7379
/// The following methods can be used for filtering or removing existing collisions:

src/dynamics/rigid_body/mod.rs

+58-1
Original file line numberDiff line numberDiff line change
@@ -16,7 +16,7 @@ pub(crate) use forces::FloatZero;
1616
pub(crate) use forces::Torque;
1717

1818
use crate::prelude::*;
19-
use bevy::prelude::*;
19+
use bevy::{prelude::*, utils::HashSet};
2020
use derive_more::From;
2121

2222
/// A non-deformable body used for the simulation of most physics objects.
@@ -826,6 +826,63 @@ pub struct AngularDamping(pub Scalar);
826826
#[reflect(Debug, Component, Default, PartialEq)]
827827
pub struct Dominance(pub i8);
828828

829+
/// A component containing a set of entities for which any collisions with the
830+
/// owning entity will be ignored.
831+
///
832+
/// ## Example
833+
///
834+
/// ```
835+
/// use bevy::prelude::*;
836+
/// # #[cfg(feature = "2d")]
837+
/// # use bevy_xpbd_2d::prelude::*;
838+
/// # #[cfg(feature = "3d")]
839+
/// use bevy_xpbd_3d::prelude::*;
840+
///
841+
/// fn setup(mut commands: Commands) {
842+
/// // Spawn an entity with a collider
843+
#[cfg_attr(
844+
feature = "2d",
845+
doc = " let ent1 = commands",
846+
doc = " .spawn((RigidBody::Dynamic, Collider::circle(0.5)))",
847+
doc = " .id();"
848+
)]
849+
#[cfg_attr(
850+
feature = "3d",
851+
doc = " let ent1 = commands",
852+
doc = " .spawn((RigidBody::Dynamic, Collider::sphere(0.5)))",
853+
doc = " .id();"
854+
)]
855+
///
856+
/// // Spawn another entity with a collider and configure it to avoid collisions with the first entity.
857+
#[cfg_attr(
858+
feature = "2d",
859+
doc = " let ent1 = commands.spawn((",
860+
doc = " RigidBody::Dynamic,",
861+
doc = " Collider::circle(0.5),",
862+
doc = " IgnoredCollisions::from_iter([ent1]),",
863+
doc = "));"
864+
)]
865+
#[cfg_attr(
866+
feature = "3d",
867+
doc = " let ent1 = commands.spawn((",
868+
doc = " RigidBody::Dynamic,",
869+
doc = " Collider::sphere(0.5),",
870+
doc = " IgnoredCollisions::from_iter([ent1]),",
871+
doc = " ));"
872+
)]
873+
/// }
874+
/// ```
875+
///
876+
/// See also [`CollisionLayers`].
877+
#[derive(Component, Clone, Debug, Default, Deref, DerefMut)]
878+
pub struct IgnoredCollisions(pub HashSet<Entity>);
879+
880+
impl FromIterator<Entity> for IgnoredCollisions {
881+
fn from_iter<T: IntoIterator<Item = Entity>>(iter: T) -> Self {
882+
Self(HashSet::from_iter(iter))
883+
}
884+
}
885+
829886
#[cfg(test)]
830887
mod tests {
831888
use crate::prelude::*;

src/lib.rs

+1
Original file line numberDiff line numberDiff line change
@@ -140,6 +140,7 @@
140140
//! - [Creation](Collider#creation)
141141
//! - [Density](ColliderDensity)
142142
//! - [Friction] and [restitution](Restitution) (bounciness)
143+
//! - [Ignoring collisions](IgnoredCollisions)
143144
//! - [Collision layers](CollisionLayers)
144145
//! - [Sensors](Sensor)
145146
#![cfg_attr(

0 commit comments

Comments
 (0)