Expand description
§Physics Integration Layer for bevy-tnua
Crates that implement a physics layer integration for Tnua (like bevy-tnua-rapier or bevy-tnua-avian) should depend on this crate and not on the main bevy-tnua crate. This crate should update less often - only when there are changes in the integration layer (which is not supposed to change as much) or when Bevy itself updates.
To integrate a Bevy physics engine with Tnua, one should create a plugin named
Tnua<physics-engine-name>Plugin, which:
-
Configures
TnuaSystemsto not run when the physics engine is paused. -
Add systems, to the
TnuaPipelineSystems::Sensorsstage, that update:TnuaRigidBodyTrackerwith the objects current kinematic status (position, rotation, velocity, angular velocity) as well as the gravity currently applied to it.TnuaProximitySensorwith the first tangible collider within range, andTnuaGhostSensorwith all the ghost colliders found before that tangible collider.- A tangible collider is a non-ghost collider that physically interacts with the character’s collider.
- A ghost collider is a collider marked with the
TnuaGhostPlatformcomponent. It may or may not physically interact with the character’s collider - as long as it has the component it is considered a ghost collider. - The sensor should ignore the owner entity’s collider.
- If the sensor has the
TnuaSubservientSensorcomponent, the “owner entity” is defined as theowner_entityfield from that component and not the entity the sensor component is attached to. - The detection should be done with a ray cast, unless the sensor is configured to cast a
shape instead. Such configuration is done with component, defined by the integration
crate, that specifies the shape to cast in a way the integration crate can pass on to the
physics engine. The name of that component should be
Tnua<physics-engine-name>SensorShape. - The detection should skip entities marked with the
TnuaNotPlatformcomponent.
TnuaObstacleRadarwith all the entities within proximity (defined byradiusandheightfields on the radar component). The system must first call thepre_marking_updatemethod, and only then callmark_seenon each entity still within range in the current frame.
The integration crate may update all these components in one system or multiple systems as it sees fit.
-
Add a system, to the
TnuaPipelineSystems::Motorsstage, that applies all the impulses and accelerations fromTnuaMotorcomponents.Here, too, if it makes sense to split this work into multiple systems the integration crate may do so at its own discretion.
-
Ensure that
TnuaSystemsruns before the integration backend’s systems. -
Handle
TnuaGravityin whatever way the integration crate sees fit. This usually entails:- Feeding the gravity set in the
TnuaGravityinto theTnuaRigidBodyTrackerinstead of the regular global gravity. - Nullifying the regular global gravity. This can be done either by applying an opposing
force or by applying an opposing force or by using some facility offered by the physics
backend (e.g. both Rapier and Avian have a
GravityScalecomponent which can be used for that purpose) - Applying the gravity from
TnuaGravityas a force.
If the physics backend has its own way to do per-rigid-body gravity, the integration crate may prefer to sync
TnuaGravityinto that mechanism instead (and should support that mechanism as the input source forTnuaRigidBodyTracker::gravitywhen it is used, even ifTnuaGravityis not used) - Feeding the gravity set in the
-
Define a type that implements
TnuaSpatialExt. That type will typically be aSystemParamthat contains queries and resources for getting the required information from the physics backend.
The integration backend’s systems must run with the same timing as the physics backend. If the
physics backend supports running in a different schedule, the integration plugin should also
support it by adding a ::new() method that accepts a schedule and registers all the systems
there. It should also implement Default to make it run in the default schedule (usually
Update)
Note that a physics backend may run its systems under PostUpdate instead of Update (both
Rapier and Avian do this by default). In this case, it’s still okay for the integration backend
to run under Update, because they use the same timing.
If the integration crate needs the character entity to have more components from the physics
engine crate, that one would not naturally add to it, it should define a bundle named
Tnua<physics-engine-name>IOBundle that adds these components. One would naturally add a rigid
body and a collider, so they should not go in that bundle, but if the crate needs things users
rarely think about - for example, bevy_rapier’s ReadMassProperties - then these components
should go in that bundle.
Modules§
Structs§
- Tnua
Systems - Umbrella system set for
TnuaPipelineSystems.
Enums§
- Tnua
Pipeline Systems - The various stages of the Tnua pipeline.