Jondolf / avian

ECS-driven 2D and 3D physics engine for the Bevy game engine.
https://crates.io/crates/avian3d
Apache License 2.0
1.56k stars 120 forks source link

Support for use within Bevy's FixedUpdate schedule? #263

Closed Architector4 closed 3 months ago

Architector4 commented 1 year ago

I'm doing a bunch of logic before, within, and after the FixedUpdate schedule in Bevy, on the fixed timestep. I'd like to run XPBD physics within the FixedUpdate schedule, to ensure that I can record all entity transforms how they are before the physics step, and record them after they have been adjusted by the physics step after it, so that I can catch the difference and do my own logic with that.

Unfortunately, that doesn't seem to be doable. The Time<Physics> resource could be adjusted to use TimestepMode::FixedOnce right before the physics step, but there doesn't appear to be a way to ensure the physics engine is notified of removals of various components it tracks.

ComponentRemovals<T> only gives info about removals that happened in the same or previous app update, and so they are frequently missed in FixedUpdate if the fixed timestep is slow enough. As such, it'd be nice to be able to run XPBD's systems that use that data in a schedule I designate that I run every update, but it's not possible because it's hardcoded.

In particular, the detection only happens in PhysicsSchedule, which is used to step the entire simulation forward:

https://github.com/Jondolf/bevy_xpbd/blob/088facfe3761235f0b297bc1493927a12a6fe21b/src/plugins/prepare.rs#L96-L97

These systems themselves are private, and so they can't be run individually outside of the crate without stepping the simulation. The resource that handle_collider_storage_removals system uses, ColliderStorageMap, is also private, so it can't be notified of removals manually either.

I assume there may be other roadblocks that prevent my use case from being possible, but this is the one that I noticed.

Jondolf commented 12 months ago

So if I understood correctly, the main issue is that some systems currently use RemovedComponents<T>, but if the engine is configured to run in FixedUpdate with a slow enough timestep, it frequently misses component removals.

I think one way to fix this could be to have a RemovedColliders resource that is cleared at the end of the PhysicsSchedule. To avoid removals being missed, the removed colliders would be added to the list in Last. This way, the collider removals would be detected correctly and only cleared after they have been handled in PhysicsSchedule. Would that be enough to address your issue?

Note that I believe all the collider removal workarounds like ColliderStorageMap should become redundant once Bevy has component lifecycle hooks (bevyengine/bevy#10756), since the component data would be accessible when responding to the removals.

Architector4 commented 12 months ago

So if I understood correctly, the main issue is that some systems currently use RemovedComponents<T>, but if the engine is configured to run in FixedUpdate with a slow enough timestep, it frequently misses component removals.

Yes, basically.

To avoid removals being missed, the removed colliders would be added to the list in Last.

In my particular case, some removals happen in a custom schedule I designate that runs every single frame right before FixedUpdate, and I'd like the physics engine run in FixedUpdate to be able to respond to them immediately, if the FixedUpdate schedule runs right after that custom schedule in the same frame. This is the case that is handled perfectly by the engine right now, as it trivially detects the remove.

That doesn't sound like it would be handled by using a list that is only updated in Last, but I guess this could work if the current and proposed removal detections are used together.

...But then that might introduce difficulties, as both the current detection will respond to removals immediately, and they would also be added to the proposed removed colliders list and handled again on the next frame, which might wreck havoc in case some game logic adds the collider back to an entity in the very next frame, which would then be added and erroneously removed.

I assume that would be fixed by first handling the removals from the list, which would also handle the duplicate removals as a no-op, and only then add colliders.

That's suboptimal, but it might be a good workaround until the component lifecycle hooks are added.

Edit: or maybe not, the above logic could break the more general case of when an entity has had a collider added and then immediately removed, in the same frame, both right before the physics engine is run. Or maybe if a collider was added, then removed, then added back, or removed then added then removed. I'll be completely honest, I'm unsure how bevy_xpbd handles this even now, but I felt like this is also something worth noting.

Sorry, this has turned into a bit of a mind dump lol

janhohenheim commented 4 months ago

Probably blocked by Take note of https://github.com/bevyengine/bevy/issues/7836