Skip to content

Commit 538eb1c

Browse files
committed
add IgnoredCollisions component
1 parent b47c30c commit 538eb1c

File tree

6 files changed

+92
-2
lines changed

6 files changed

+92
-2
lines changed

src/collision/broad_phase.rs

Lines changed: 23 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -190,6 +190,7 @@ fn collect_collision_pairs(
190190
intervals: ResMut<AabbIntervals>,
191191
mut broad_collision_pairs: ResMut<BroadCollisionPairs>,
192192
mut aabb_intersection_query: Query<&mut AabbIntersections>,
193+
ignored_collisions: Query<&IgnoredCollisions>,
193194
) {
194195
for mut intersections in &mut aabb_intersection_query {
195196
intersections.clear();
@@ -199,6 +200,7 @@ fn collect_collision_pairs(
199200
intervals,
200201
&mut broad_collision_pairs.0,
201202
&mut aabb_intersection_query,
203+
ignored_collisions,
202204
);
203205
}
204206

@@ -209,6 +211,7 @@ fn sweep_and_prune(
209211
mut intervals: ResMut<AabbIntervals>,
210212
broad_collision_pairs: &mut Vec<(Entity, Entity)>,
211213
aabb_intersection_query: &mut Query<&mut AabbIntersections>,
214+
ignored_collisions: Query<&IgnoredCollisions>,
212215
) {
213216
// Sort bodies along the x-axis using insertion sort, a sorting algorithm great for sorting nearly sorted lists.
214217
insertion_sort(&mut intervals.0, |a, b| a.2.min.x > b.2.min.x);
@@ -220,9 +223,29 @@ fn sweep_and_prune(
220223
for (i, (ent1, parent1, aabb1, layers1, store_intersections1, inactive1)) in
221224
intervals.0.iter().enumerate()
222225
{
226+
let ent1_ignored_collisions = ignored_collisions.get(*ent1).ok();
223227
for (ent2, parent2, aabb2, layers2, store_intersections2, inactive2) in
224228
intervals.0.iter().skip(i + 1)
225229
{
230+
// Check ignored collisions of `ent1`
231+
if ent1_ignored_collisions
232+
.as_ref()
233+
.map(|i| i.contains(ent2))
234+
.unwrap_or_default()
235+
{
236+
continue;
237+
}
238+
239+
// Check ignored collisions of `ent2`
240+
let ent2_ignored_collisions = ignored_collisions.get(*ent2).ok();
241+
if ent2_ignored_collisions
242+
.as_ref()
243+
.map(|i| i.contains(ent1))
244+
.unwrap_or_default()
245+
{
246+
continue;
247+
}
248+
226249
// x doesn't intersect; check this first so we can discard as soon as possible
227250
if aabb2.min.x > aabb1.max.x {
228251
break;

src/collision/collider/parry/mod.rs

Lines changed: 2 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -263,7 +263,7 @@ impl From<TrimeshFlags> for parry::shape::TriMeshFlags {
263263
/// ```
264264
///
265265
/// Colliders can be further configured using various components like [`Friction`], [`Restitution`],
266-
/// [`Sensor`], [`CollisionLayers`], [`CollisionMargin`], and [`ColliderDensity`].
266+
/// [`Sensor`], [`IgnoredCollisions`], [`CollisionLayers`], [`CollisionMargin`], and [`ColliderDensity`].
267267
///
268268
/// If you need to specify the shape of the collider statically, use [`ColliderConstructor`] and build your collider
269269
/// with the [`Collider::try_from_constructor`] method.
@@ -337,6 +337,7 @@ impl From<TrimeshFlags> for parry::shape::TriMeshFlags {
337337
/// - [Rigid bodies](RigidBody)
338338
/// - [Density](ColliderDensity)
339339
/// - [Friction] and [restitution](Restitution) (bounciness)
340+
/// - [Ignoring collisions](IgnoredCollisions)
340341
/// - [Collision layers](CollisionLayers)
341342
/// - [Sensors](Sensor)
342343
/// - [Collision margins for adding extra thickness to colliders](CollisionMargin)

src/collision/layers.rs

Lines changed: 2 additions & 0 deletions
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::dynamics::rigid_body::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

Lines changed: 6 additions & 0 deletions
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

Lines changed: 58 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -20,7 +20,7 @@ pub(crate) use forces::FloatZero;
2020
pub(crate) use forces::Torque;
2121

2222
use crate::prelude::*;
23-
use bevy::prelude::*;
23+
use bevy::{prelude::*, utils::HashSet};
2424
use derive_more::From;
2525

2626
/// A non-deformable body used for the simulation of most physics objects.
@@ -684,3 +684,60 @@ pub struct AngularDamping(pub Scalar);
684684
#[cfg_attr(feature = "serialize", reflect(Serialize, Deserialize))]
685685
#[reflect(Debug, Component, Default, PartialEq)]
686686
pub struct Dominance(pub i8);
687+
688+
/// A component containing a set of entities for which any collisions with the
689+
/// owning entity will be ignored.
690+
///
691+
/// ## Example
692+
///
693+
/// ```
694+
/// use bevy::prelude::*;
695+
/// # #[cfg(feature = "2d")]
696+
/// # use avian2d::prelude::*;
697+
/// # #[cfg(feature = "3d")]
698+
/// use avian3d::prelude::*;
699+
///
700+
/// fn setup(mut commands: Commands) {
701+
/// // Spawn an entity with a collider
702+
#[cfg_attr(
703+
feature = "2d",
704+
doc = " let ent1 = commands",
705+
doc = " .spawn((RigidBody::Dynamic, Collider::circle(0.5)))",
706+
doc = " .id();"
707+
)]
708+
#[cfg_attr(
709+
feature = "3d",
710+
doc = " let ent1 = commands",
711+
doc = " .spawn((RigidBody::Dynamic, Collider::sphere(0.5)))",
712+
doc = " .id();"
713+
)]
714+
///
715+
/// // Spawn another entity with a collider and configure it to avoid collisions with the first entity.
716+
#[cfg_attr(
717+
feature = "2d",
718+
doc = " let ent1 = commands.spawn((",
719+
doc = " RigidBody::Dynamic,",
720+
doc = " Collider::circle(0.5),",
721+
doc = " IgnoredCollisions::from_iter([ent1]),",
722+
doc = "));"
723+
)]
724+
#[cfg_attr(
725+
feature = "3d",
726+
doc = " let ent1 = commands.spawn((",
727+
doc = " RigidBody::Dynamic,",
728+
doc = " Collider::sphere(0.5),",
729+
doc = " IgnoredCollisions::from_iter([ent1]),",
730+
doc = " ));"
731+
)]
732+
/// }
733+
/// ```
734+
///
735+
/// See also [`CollisionLayers`].
736+
#[derive(Component, Clone, Debug, Default, Deref, DerefMut)]
737+
pub struct IgnoredCollisions(pub HashSet<Entity>);
738+
739+
impl FromIterator<Entity> for IgnoredCollisions {
740+
fn from_iter<T: IntoIterator<Item = Entity>>(iter: T) -> Self {
741+
Self(HashSet::from_iter(iter))
742+
}
743+
}

src/lib.rs

Lines changed: 1 addition & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -142,6 +142,7 @@
142142
//! - [Creation](Collider#creation)
143143
//! - [Density](ColliderDensity)
144144
//! - [Friction] and [restitution](Restitution) (bounciness)
145+
//! - [Ignoring collisions](IgnoredCollisions)
145146
//! - [Collision layers](CollisionLayers)
146147
//! - [Sensors](Sensor)
147148
#![cfg_attr(

0 commit comments

Comments
 (0)