bevy_tnua_physics_integration_layer/lib.rs
1//! # Physics Integration Layer for bevy-tnua
2//!
3//! Crates that implement a physics layer integration for Tnua (like bevy-tnua-rapier or
4//! bevy-tnua-avian) should depend on this crate and not on the main bevy-tnua crate. This crate
5//! should update less often - only when there are changes in the integration layer (which is not
6//! supposed to change as much) or when Bevy itself updates.
7//!
8//! To integrate a Bevy physics engine with Tnua, one should create a plugin named
9//! `Tnua<physics-engine-name>Plugin`, which:
10//!
11//! * Configures [`TnuaSystems`] to not run when the physics engine is paused.
12//! * Add systems, to the [`TnuaPipelineSystems::Sensors`] stage, that update:
13//! * [`TnuaRigidBodyTracker`](data_for_backends::TnuaRigidBodyTracker) with the objects current
14//! kinematic status (position, rotation, velocity, angular velocity) as well as the gravity
15//! currently applied to it.
16//! * [`TnuaProximitySensor`](data_for_backends::TnuaProximitySensor) with the _first_ tangible
17//! collider within range, and [`TnuaGhostSensor`](data_for_backends::TnuaGhostSensor) with
18//! _all_ the ghost colliders found before that tangible collider.
19//! * The positions inside the sensors are relative to the transform of its owner entity, via
20//! the [`TnuaSensorOf`](data_for_backends::TnuaSensorOf) relationship.
21//! * A tangible collider is a **non-ghost** collider that physically interacts with the
22//! character's collider.
23//! * A ghost collider is a collider marked with the
24//! [`TnuaGhostPlatform`](data_for_backends::TnuaGhostPlatform) component. It may or may not
25//! physically interact with the character's collider - as long as it has the component it is
26//! considered a ghost collider.
27//! * The sensor should ignore the owner entity's collider.
28//! * The detection should be done with a ray cast, unless the sensor is configured to cast a
29//! shape instead. Such configuration is done with component, defined by the integration
30//! crate, that specifies the shape to cast in a way the integration crate can pass on to the
31//! physics engine. The name of that component should be
32//! `Tnua<physics-engine-name>SensorShape`.
33//! * The detection should skip entities marked with the
34//! [`TnuaNotPlatform`](data_for_backends::TnuaNotPlatform) component.
35//! * [`TnuaObstacleRadar`](data_for_backends::TnuaObstacleRadar) with all the entities within
36//! proximity (defined by `radius` and `height` fields on the radar component). The system must
37//! first call the
38//! [`pre_marking_update`](data_for_backends::TnuaObstacleRadar::pre_marking_update) method,
39//! and only then call [`mark_seen`](data_for_backends::TnuaObstacleRadar::mark_seen) on each
40//! entity still within range in the current frame.
41//!
42//! The integration crate may update all these components in one system or multiple systems as it
43//! sees fit.
44//!
45//! * Add a system, to the [`TnuaPipelineSystems::Motors`] stage, that applies all the impulses and
46//! accelerations from [`TnuaMotor`](data_for_backends::TnuaMotor) components.
47//!
48//! Here, too, if it makes sense to split this work into multiple systems the integration crate
49//! may do so at its own discretion.
50//!
51//! * Ensure that [`TnuaSystems`] runs before the integration backend's systems.
52//!
53//! * Handle [`TnuaGravity`](data_for_backends::TnuaGravity) in whatever way the integration crate
54//! sees fit. This usually entails:
55//! * Feeding the gravity set in the `TnuaGravity` into the `TnuaRigidBodyTracker` instead of
56//! the regular global gravity.
57//! * Nullifying the regular global gravity. This can be done either by applying an opposing
58//! force or by applying an opposing force or by using some facility offered by the physics
59//! backend (e.g. both Rapier and Avian have a `GravityScale` component which can be used for
60//! that purpose)
61//! * Applying the gravity from `TnuaGravity` as a force.
62//!
63//! If the physics backend has its own way to do per-rigid-body gravity, the integration crate
64//! may prefer to sync `TnuaGravity` into that mechanism instead (and should support that
65//! mechanism as the input source for `TnuaRigidBodyTracker::gravity` when it is used, even
66//! if `TnuaGravity` is not used)
67//!
68//! * Define a type that implements [`TnuaSpatialExt`](spatial_ext::TnuaSpatialExt). That type will
69//! typically be a [`SystemParam`](bevy::ecs::system::SystemParam) that contains queries and
70//! resources for getting the required information from the physics backend.
71//!
72//! The integration backend's systems must run with the same timing as the physics backend. If the
73//! physics backend supports running in a different schedule, the integration plugin should also
74//! support it by adding a `::new()` method that accepts a schedule and registers all the systems
75//! there. It should also implement `Default` to make it run in the default schedule (usually
76//! `Update`)
77//!
78//! Note that a physics backend may run its systems under `PostUpdate` instead of `Update` (both
79//! Rapier and Avian do this by default). In this case, it's still okay for the integration backend
80//! to run under `Update`, because they use the same timing.
81//!
82//! If the integration crate needs the character entity to have more components from the physics
83//! engine crate, that one would not naturally add to it, it should define a bundle named
84//! `Tnua<physics-engine-name>IOBundle` that adds these components. One would naturally add a rigid
85//! body and a collider, so they should not go in that bundle, but if the crate needs things users
86//! rarely think about - for example, bevy_rapier's `ReadMassProperties` - then these components
87//! should go in that bundle.
88use bevy::prelude::*;
89
90pub mod data_for_backends;
91pub mod math;
92pub mod obstacle_radar;
93pub mod spatial_ext;
94
95/// Umbrella system set for [`TnuaPipelineSystems`].
96///
97/// The physics backends' plugins are responsible for preventing this entire system set from
98/// running when the physics backend itself is paused.
99#[derive(SystemSet, Clone, PartialEq, Eq, Debug, Hash)]
100pub struct TnuaSystems;
101
102/// The various stages of the Tnua pipeline.
103#[derive(SystemSet, Clone, PartialEq, Eq, Debug, Hash)]
104pub enum TnuaPipelineSystems {
105 /// Data is read from the physics backend.
106 Sensors,
107 /// Tnua decieds how the entity should be manipulated.
108 Logic,
109 /// Forces are applied in the physics backend.
110 Motors,
111}