Skip to content

Commit b9b2185

Browse files
committed
Revert Collision Hooks (#610) to avoid breaking changes for 0.2.1
1 parent acae639 commit b9b2185

File tree

7 files changed

+184
-677
lines changed

7 files changed

+184
-677
lines changed
Lines changed: 78 additions & 149 deletions
Original file line numberDiff line numberDiff line change
@@ -1,35 +1,27 @@
11
//! A 2D platformer example with one-way platforms to demonstrate
2-
//! contact modification with `CollisionHooks`.
2+
//! filtering collisions with systems in the `PostProcessCollisions` schedule.
33
//!
44
//! Move with arrow keys, jump with Space and descend through
55
//! platforms by pressing Space while holding the down arrow.
66
7-
#![allow(clippy::type_complexity)]
8-
97
use avian2d::{math::*, prelude::*};
10-
use bevy::{
11-
ecs::system::{lifetimeless::Read, SystemParam},
12-
prelude::*,
13-
utils::HashSet,
14-
};
8+
use bevy::{prelude::*, utils::HashSet};
159
use examples_common_2d::ExampleCommonPlugin;
1610

1711
fn main() {
1812
App::new()
1913
.add_plugins((
2014
DefaultPlugins,
2115
ExampleCommonPlugin,
22-
PhysicsPlugins::default()
23-
// Specify a units-per-meter scaling factor, 1 meter = 20 pixels.
24-
// The unit allows the engine to tune its parameters for the scale of the world, improving stability.
25-
.with_length_unit(20.0)
26-
// Add our custom collision hooks.
27-
.with_collision_hooks::<PlatformerCollisionHooks>(),
16+
// Add physics plugins and specify a units-per-meter scaling factor, 1 meter = 20 pixels.
17+
// The unit allows the engine to tune its parameters for the scale of the world, improving stability.
18+
PhysicsPlugins::default().with_length_unit(20.0),
2819
))
2920
.insert_resource(ClearColor(Color::srgb(0.05, 0.05, 0.1)))
3021
.insert_resource(Gravity(Vector::NEG_Y * 1000.0))
3122
.add_systems(Startup, setup)
3223
.add_systems(Update, (movement, pass_through_one_way_platform))
24+
.add_systems(PostProcessCollisions, one_way_platform)
3325
.run();
3426
}
3527

@@ -42,10 +34,7 @@ struct MovementSpeed(Scalar);
4234
#[derive(Component)]
4335
struct JumpImpulse(Scalar);
4436

45-
// Enable contact modification for one-way platforms with the `ActiveCollisionHooks` component.
46-
// Here we use required components, but you could also add it manually.
4737
#[derive(Clone, Eq, PartialEq, Debug, Default, Component)]
48-
#[require(ActiveCollisionHooks(|| ActiveCollisionHooks::MODIFY_CONTACTS))]
4938
pub struct OneWayPlatform(HashSet<Entity>);
5039

5140
/// A component to control how an actor interacts with a one-way platform.
@@ -183,70 +172,70 @@ fn pass_through_one_way_platform(
183172
}
184173
}
185174

186-
// Define a custom `SystemParam` for our collision hooks.
187-
// It can have read-only access to queries, resources, and other system parameters.
188-
#[derive(SystemParam)]
189-
struct PlatformerCollisionHooks<'w, 's> {
190-
one_way_platforms_query: Query<'w, 's, Read<OneWayPlatform>>,
191-
// NOTE: This precludes a `OneWayPlatform` passing through a `OneWayPlatform`.
175+
/// Allows entities to pass through [`OneWayPlatform`] entities.
176+
///
177+
/// Passing through is achieved by removing the collisions between the [`OneWayPlatform`]
178+
/// and the other entity if the entity should pass through.
179+
/// If a [`PassThroughOneWayPlatform`] is present on the non-platform entity,
180+
/// the value of the component dictates the pass-through behaviour.
181+
///
182+
/// Entities known to be passing through each [`OneWayPlatform`] are stored in the
183+
/// [`OneWayPlatform`]. If an entity is known to be passing through a [`OneWayPlatform`],
184+
/// it is allowed to continue to do so, even if [`PassThroughOneWayPlatform`] has been
185+
/// set to disallow passing through.
186+
///
187+
/// > Note that this is a very simplistic implementation of one-way
188+
/// > platforms to demonstrate filtering collisions via [`PostProcessCollisions`].
189+
/// > You will probably want something more robust to implement one-way
190+
/// > platforms properly, or may elect to use a sensor collider for your entities instead,
191+
/// > which means you won't need to filter collisions at all.
192+
///
193+
/// #### When an entity is known to already be passing through the [`OneWayPlatform`]
194+
///
195+
/// Any time an entity begins passing through a [`OneWayPlatform`], it is added to the
196+
/// [`OneWayPlatform`]'s set of currently active penetrations, and will be allowed to
197+
/// continue to pass through the platform until it is no longer penetrating the platform.
198+
///
199+
/// The entity is allowed to continue to pass through the platform as long as at least
200+
/// one contact is penetrating.
201+
///
202+
/// Once all of the contacts are no longer penetrating the [`OneWayPlatform`], or all contacts
203+
/// have stopped, the entity is forgotten about and the logic falls through to the next part.
204+
///
205+
/// #### When an entity is NOT known to be passing through the [`OneWayPlatform`]
206+
///
207+
/// Depending on the setting of [`PassThroughOneWayPlatform`], the entity may be allowed to
208+
/// pass through.
209+
///
210+
/// If no [`PassThroughOneWayPlatform`] is present, [`PassThroughOneWayPlatform::ByNormal`] is used.
211+
///
212+
/// [`PassThroughOneWayPlatform`] may be in one of three states:
213+
/// 1. [`PassThroughOneWayPlatform::ByNormal`]
214+
/// - This is the default state
215+
/// - The entity may be allowed to pass through the [`OneWayPlatform`] depending on the contact normal
216+
/// - If all contact normals are in line with the [`OneWayPlatform`]'s local-space up vector,
217+
/// the entity is allowed to pass through
218+
/// 2. [`PassThroughOneWayPlatform::Always`]
219+
/// - The entity will always pass through the [`OneWayPlatform`], regardless of contact normal
220+
/// - This is useful for allowing an entity to jump down through a platform
221+
/// 3. [`PassThroughOneWayPlatform::Never`]
222+
/// - The entity will never pass through the [`OneWayPlatform`], meaning the platform will act
223+
/// as normal hard collision for this entity
224+
///
225+
/// Even if an entity is changed to [`PassThroughOneWayPlatform::Never`], it will be allowed to pass
226+
/// through a [`OneWayPlatform`] if it is already penetrating the platform. Once it exits the platform,
227+
/// it will no longer be allowed to pass through.
228+
fn one_way_platform(
229+
mut one_way_platforms_query: Query<&mut OneWayPlatform>,
192230
other_colliders_query: Query<
193-
'w,
194-
's,
195-
Option<Read<PassThroughOneWayPlatform>>,
196-
(With<Collider>, Without<OneWayPlatform>),
231+
Option<&PassThroughOneWayPlatform>,
232+
(With<Collider>, Without<OneWayPlatform>), // NOTE: This precludes OneWayPlatform passing through a OneWayPlatform
197233
>,
198-
}
199-
200-
// Implement the `CollisionHooks` trait for our custom system parameter.
201-
impl CollisionHooks for PlatformerCollisionHooks<'_, '_> {
202-
// Below is a description of the logic used for one-way platforms.
203-
204-
/// Allows entities to pass through [`OneWayPlatform`] entities.
205-
///
206-
/// Passing through is achieved by removing the collisions between the [`OneWayPlatform`]
207-
/// and the other entity if the entity should pass through.
208-
/// If a [`PassThroughOneWayPlatform`] is present on the non-platform entity,
209-
/// the value of the component dictates the pass-through behaviour.
210-
///
211-
/// Entities known to be passing through each [`OneWayPlatform`] are stored in the
212-
/// [`OneWayPlatform`]. If an entity is known to be passing through a [`OneWayPlatform`],
213-
/// it is allowed to continue to do so, even if [`PassThroughOneWayPlatform`] has been
214-
/// set to disallow passing through.
215-
///
216-
/// #### When an entity is known to already be passing through the [`OneWayPlatform`]
217-
///
218-
/// When an entity begins passing through a [`OneWayPlatform`], it is added to the
219-
/// [`OneWayPlatform`]'s set of active penetrations, and will be allowed to continue
220-
/// to pass through until it is no longer penetrating the platform.
221-
///
222-
/// #### When an entity is *not* known to be passing through the [`OneWayPlatform`]
223-
///
224-
/// Depending on the setting of [`PassThroughOneWayPlatform`], the entity may be allowed to
225-
/// pass through.
226-
///
227-
/// If no [`PassThroughOneWayPlatform`] is present, [`PassThroughOneWayPlatform::ByNormal`] is used.
228-
///
229-
/// [`PassThroughOneWayPlatform`] may be in one of three states:
230-
/// 1. [`PassThroughOneWayPlatform::ByNormal`]
231-
/// - This is the default state
232-
/// - The entity may be allowed to pass through the [`OneWayPlatform`] depending on the contact normal
233-
/// - If all contact normals are in line with the [`OneWayPlatform`]'s local-space up vector,
234-
/// the entity is allowed to pass through
235-
/// 2. [`PassThroughOneWayPlatform::Always`]
236-
/// - The entity will always pass through the [`OneWayPlatform`], regardless of contact normal
237-
/// - This is useful for allowing an entity to jump down through a platform
238-
/// 3. [`PassThroughOneWayPlatform::Never`]
239-
/// - The entity will never pass through the [`OneWayPlatform`], meaning the platform will act
240-
/// as normal hard collision for this entity
241-
///
242-
/// Even if an entity is changed to [`PassThroughOneWayPlatform::Never`], it will be allowed to pass
243-
/// through a [`OneWayPlatform`] if it is already penetrating the platform. Once it exits the platform,
244-
/// it will no longer be allowed to pass through.
245-
fn modify_contacts(&self, contacts: &mut Contacts, commands: &mut Commands) -> bool {
246-
// This is the contact modification hook, called after collision detection,
247-
// but before constraints are created for the solver. Mutable access to the ECS
248-
// is not allowed, but we can queue commands to perform deferred changes.
249-
234+
mut collisions: ResMut<Collisions>,
235+
) {
236+
// This assumes that Collisions contains empty entries for entities
237+
// that were once colliding but no longer are.
238+
collisions.retain(|contacts| {
250239
// Differentiate between which normal of the manifold we should use
251240
enum RelevantNormal {
252241
Normal1,
@@ -255,59 +244,40 @@ impl CollisionHooks for PlatformerCollisionHooks<'_, '_> {
255244

256245
// First, figure out which entity is the one-way platform, and which is the other.
257246
// Choose the appropriate normal for pass-through depending on which is which.
258-
let (platform_entity, one_way_platform, other_entity, relevant_normal) =
259-
if let Ok(one_way_platform) = self.one_way_platforms_query.get(contacts.entity1) {
260-
(
261-
contacts.entity1,
262-
one_way_platform,
263-
contacts.entity2,
264-
RelevantNormal::Normal1,
265-
)
266-
} else if let Ok(one_way_platform) = self.one_way_platforms_query.get(contacts.entity2)
267-
{
268-
(
269-
contacts.entity2,
270-
one_way_platform,
271-
contacts.entity1,
272-
RelevantNormal::Normal2,
273-
)
247+
let (mut one_way_platform, other_entity, relevant_normal) =
248+
if let Ok(one_way_platform) = one_way_platforms_query.get_mut(contacts.entity1) {
249+
(one_way_platform, contacts.entity2, RelevantNormal::Normal1)
250+
} else if let Ok(one_way_platform) = one_way_platforms_query.get_mut(contacts.entity2) {
251+
(one_way_platform, contacts.entity1, RelevantNormal::Normal2)
274252
} else {
275253
// Neither is a one-way-platform, so accept the collision:
276254
// we're done here.
277255
return true;
278256
};
279-
280257
if one_way_platform.0.contains(&other_entity) {
281258
let any_penetrating = contacts.manifolds.iter().any(|manifold| {
282259
manifold
283260
.contacts
284261
.iter()
285262
.any(|contact| contact.penetration > 0.0)
286263
});
287-
288264
if any_penetrating {
289265
// If we were already allowing a collision for a particular entity,
290266
// and if it is penetrating us still, continue to allow it to do so.
291267
return false;
292268
} else {
293269
// If it's no longer penetrating us, forget it.
294-
commands.queue(OneWayPlatformCommand::Remove {
295-
platform_entity,
296-
entity: other_entity,
297-
});
270+
one_way_platform.0.remove(&other_entity);
298271
}
299272
}
300273

301-
match self.other_colliders_query.get(other_entity) {
274+
match other_colliders_query.get(other_entity) {
302275
// Pass-through is set to never, so accept the collision.
303276
Ok(Some(PassThroughOneWayPlatform::Never)) => true,
304277
// Pass-through is set to always, so always ignore this collision
305278
// and register it as an entity that's currently penetrating.
306279
Ok(Some(PassThroughOneWayPlatform::Always)) => {
307-
commands.queue(OneWayPlatformCommand::Add {
308-
platform_entity,
309-
entity: other_entity,
310-
});
280+
one_way_platform.0.insert(other_entity);
311281
false
312282
}
313283
// Default behaviour is "by normal".
@@ -319,57 +289,16 @@ impl CollisionHooks for PlatformerCollisionHooks<'_, '_> {
319289
RelevantNormal::Normal1 => manifold.normal1,
320290
RelevantNormal::Normal2 => manifold.normal2,
321291
};
322-
323292
normal.length() > Scalar::EPSILON && normal.dot(Vector::Y) >= 0.5
324293
}) {
325294
true
326295
} else {
327296
// Otherwise, ignore the collision and register
328297
// the other entity as one that's currently penetrating.
329-
commands.queue(OneWayPlatformCommand::Add {
330-
platform_entity,
331-
entity: other_entity,
332-
});
298+
one_way_platform.0.insert(other_entity);
333299
false
334300
}
335301
}
336302
}
337-
}
338-
}
339-
340-
/// A command to add/remove entities to/from the set of entities
341-
/// that are currently in contact with a one-way platform.
342-
enum OneWayPlatformCommand {
343-
Add {
344-
platform_entity: Entity,
345-
entity: Entity,
346-
},
347-
Remove {
348-
platform_entity: Entity,
349-
entity: Entity,
350-
},
351-
}
352-
353-
impl Command for OneWayPlatformCommand {
354-
fn apply(self, world: &mut World) {
355-
match self {
356-
OneWayPlatformCommand::Add {
357-
platform_entity,
358-
entity,
359-
} => {
360-
if let Some(mut platform) = world.get_mut::<OneWayPlatform>(platform_entity) {
361-
platform.0.insert(entity);
362-
}
363-
}
364-
365-
OneWayPlatformCommand::Remove {
366-
platform_entity,
367-
entity,
368-
} => {
369-
if let Some(mut platform) = world.get_mut::<OneWayPlatform>(platform_entity) {
370-
platform.0.remove(&entity);
371-
}
372-
}
373-
}
374-
}
303+
});
375304
}

0 commit comments

Comments
 (0)