bevy_tnua/
lib.rs

1//! # Tnua - A Character Controller for Bevy.
2//!
3//! Tnua ("motion" in Hebrew) is a floating character controller, which means that instead of
4//! constantly touching the ground the character floats above it, which makes many aspects of the
5//! motion control simpler.
6//!
7//! Tnua can use [Rapier](https://rapier.rs/) or [Avian](https://github.com/Jondolf/avian), and
8//! supports both the 2D and 3D versions of both with integration crates:
9//!
10//! * For Rapier 2D, add the [bevy-tnua-rapier2d](https://crates.io/crates/bevy-tnua-rapier2d) crate.
11//! * For Rapier 3D, add the [bevy-tnua-rapier3d](https://crates.io/crates/bevy-tnua-rapier3d) crate.
12//! * For Avian 2D, add the [bevy-tnua-avian2d](https://crates.io/crates/bevy-tnua-avian2d) crate.
13//! * For Avian 3D, add the [bevy-tnua-avian3d](https://crates.io/crates/bevy-tnua-avian3d) crate.
14//! * Third party integration crates. Such crates should depend on
15//!   [bevy-tnua-physics-integration-layer](https://crates.io/crates/bevy-tnua-physics-integration-layer)
16//!   and not the main bevy-tnua crate.
17//!
18//! Each physics integration crate has basic usage instructions for adding it in its documentation.
19//!
20//! When using a physics backend with double precision (like Avian with the `f64` flag), the `f64`
21//! flag should be added to all the Tnua crates. This applies to double precision data that gets
22//! defined by the physics backend - Bevy itself will still use single precision, and this is the
23//! precision the position and rotation will use.
24//!
25//! In addition to the physics integration plugin, the [`TnuaControllerPlugin`] should also be
26//! added.
27//!
28//! Some physics backends support running in different schedules (e.g. `FixedUpdate` to make the
29//! simulation deterministic). When using this feature, the physics integration plugin,
30//! `TnuaControllerPlugin`, and any other Tnua plugin that supports it  must also be registered in
31//! that schedule, using their `::new()` method instead of `::default()`.
32//!
33//! ## Defining the control scheme
34//!
35//! The range of movement actions available to the character controller is defined by the _control
36//! scheme_. The controle scheme is an enum that derives [`TnuaScheme`]. It needs to use an
37//! attribute to define the _basis_ - a constant mode of movement that character is always going to
38//! be in. Simple games prboably want to use [`TnuaBuiltinWalk`](crate::builtins::TnuaBuiltinWalk),
39//! which defines a simple floating character that can be moved around with a simple vector and can
40//! be told to face a direcetion.
41//!
42//! The enum's variants define the actions - various movement commands on top of the basis, like
43//! jumping, crouching, climbing, etc. The variants need to be tuple variants, with the action's
44//! type as the first tuple member of each variants.
45//!
46//! ```no_run
47//! # use bevy::prelude::*;
48//! # use bevy_tnua::prelude::*;
49//! #[derive(TnuaScheme)]
50//! #[scheme(basis = TnuaBuiltinWalk)]
51//! enum ControlScheme {
52//!     Jump(TnuaBuiltinJump),
53//!     // more actions can be defined as more variants
54//! }
55//! ```
56//!
57//! For more avaialbe attributes, see [the `TnuaScheme` derive macro](bevy_tnua_macros::TnuaScheme)
58//! documentation
59//!
60//! ## Spawning the character controller
61//!
62//! A Tnua controlled character must have a dynamic rigid body, a [`TnuaConfig`]. The controller is
63//! parameterized by the control scheme and needs a configuration (based on the control scheme) as
64//! an asset handle:
65//! ```no_run
66//! # use bevy::prelude::*;
67//! # // Not importing from Rapier because there are two versions and the default features does not
68//! # // enable either:
69//! # #[derive(Component)]
70//! # enum RigidBody { Dynamic }
71//! # use bevy_tnua::prelude::*;
72//! # use bevy_tnua::builtins::{TnuaBuiltinWalkConfig, TnuaBuiltinJumpConfig};
73//! # let mut commands: Commands = panic!();
74//! # let mut cmd = commands.spawn_empty();
75//! # #[derive(TnuaScheme)] #[scheme(basis = TnuaBuiltinWalk)] enum ControlScheme {Jump(TnuaBuiltinJump)}
76//! # let control_scheme_configs: Assets<ControlSchemeConfig> = panic!();
77//! cmd.insert(RigidBody::Dynamic);
78//! cmd.insert(TnuaController::<ControlScheme>::default());
79//! cmd.insert(TnuaConfig::<ControlScheme>(
80//!     // This example creates the configuration by code and injects it to the Assets resource,
81//!     // but a proper game will probably want to load it from an asset file.
82//!     control_scheme_configs.add(ControlSchemeConfig {
83//!         // The basis' configuration is alwayts named `basis`:
84//!         basis: TnuaBuiltinWalkConfig {
85//!             // Must be larger than the height of the entity's center from the bottom of its
86//!             // collider, or else the character will not float and Tnua will not work properly:
87//!             float_height: 2.0,
88//!
89//!             // TnuaBuiltinWalkConfig has many other fields that can be configured:
90//!             ..Default::default()
91//!         },
92//!         // Actions' configurations are named after the variants defining the actions:
93//!         jump: TnuaBuiltinJumpConfig {
94//!             // The full height of the jump, if the player does not release the button:
95//!             height: 4.0,
96//!
97//!             // TnuaBuiltinJumpConfig too has other fields that can be configured:
98//!             ..Default::default()
99//!         },
100//!     })
101//! ));
102//! ```
103//! Typically though it'd also include a `Collider`.
104//!
105//! ## Optional But Recommended
106//!
107//! * Tnua, by default, casts a single ray to the ground. This can be a problem when the character
108//!   stands on a ledge, because the ray may be past the ledge while the character's collider
109//!   isn't. To avoid that, use `Tnua<physics-backend>SensorShape` (e.g. - for Rapier 3D, use
110//!   `TnuaRapier3dSensorShape`) to replace the ray with a shape that resembles the collider. It is
111//!   better to use a shape a little bit smaller than the collider, so that when the character
112//!   presses against a wall Tnua won't think it should be lifted up when the casted shape hits
113//!   that wall.
114//! * Tnua will apply forces to keep the character upright, but it is also possible to lock
115//!   rotation so that there would be no tilting at all. This is done by Tnua itself - it has to be
116//!   done by the physics engine. Both Rapier and Avian can do it using a component called
117//!   `LockedAxes`. When using it in 3D in combination of rotation controls (such as
118//!   [`TnuaBuiltinWalk::desired_forward`](builtins::TnuaBuiltinWalk::desired_forward)) make sure
119//!   to only lock the X and Z axess, so that Tnua could rotate the character around the Y axis.
120//!
121//! ## Controlling the Character
122//!
123//! To control the character, update the [`TnuaController`] by feeding it a [basis](TnuaBasis) and
124//! zero or more [actions](TnuaAction). For some of the advanced features to work, the system that
125//! does this needs to be placed inside the [`TnuaUserControlsSystems`] system set.
126//!
127//! ```no_run
128//! # use bevy::prelude::*;
129//! # use bevy_tnua::prelude::*;
130//! # use bevy_tnua::math::Vector3;
131//! # #[derive(Component)]
132//! # struct PlayerInputComponent;
133//! # impl PlayerInputComponent {
134//! # fn direction_vector(&self) -> Vector3 { Vector3::ZERO }
135//! # fn jump_pressed(&self) -> bool { false }
136//! # }
137//! # #[derive(TnuaScheme)] #[scheme(basis = TnuaBuiltinWalk)] enum ControlScheme {Jump(TnuaBuiltinJump)}
138//! fn player_control_system(mut query: Query<(
139//!     &mut TnuaController<ControlScheme>,
140//!     &PlayerInputComponent,  // not part of Tnua - defined in user code
141//! )>) {
142//!     for (mut controller, player_input) in query.iter_mut() {
143//!         controller.basis = TnuaBuiltinWalk {
144//!             // Move in the direction the player entered:
145//!             desired_motion: player_input.direction_vector(),
146//!
147//!             // Turn the character in the movement direction:
148//!             desired_forward: Dir3::new(player_input.direction_vector()).ok(),
149//!         };
150//!
151//!         if player_input.jump_pressed() {
152//!             // The jump action must be fed as long as the player holds the button.
153//!             controller.action(ControlScheme::Jump(Default::default()));
154//!         }
155//!     }
156//! }
157//! ```
158//! Refer to the documentation of [`TnuaController`] for more information, but essentially the
159//! _basis_ controls the general movement and the _action_ is something special (jump, dash,
160//! crouch, etc.)
161//!
162//! ## Motion Based Animation
163//!
164//! [`TnuaController`] can also be used to retreive data that can be used to decide which animation
165//! to play. A useful helper for that is [`TnuaAnimatingState`].
166pub mod action_state;
167mod animating_helper;
168mod basis_action_traits;
169#[doc(hidden)]
170pub use serde;
171pub mod basis_capabilities;
172pub mod builtins;
173pub mod control_helpers;
174pub mod controller;
175pub mod ghost_overrides;
176pub mod radar_lens;
177pub mod sensor_sets;
178pub mod util;
179pub use animating_helper::{TnuaAnimatingState, TnuaAnimatingStateDirective};
180pub use basis_action_traits::{
181    TnuaAction, TnuaActionContext, TnuaActionDiscriminant, TnuaBasis, TnuaBasisAccess, TnuaScheme,
182    TnuaSchemeConfig, TnuaUpdateInActionStateResult,
183};
184pub use basis_action_traits::{
185    TnuaActionInitiationDirective, TnuaActionLifecycleDirective, TnuaActionLifecycleStatus,
186    TnuaActionState, TnuaBasisContext, TnuaConfigModifier,
187};
188pub use bevy_tnua_macros::TnuaScheme;
189pub use controller::{TnuaConfig, TnuaController, TnuaControllerPlugin};
190pub use ghost_overrides::TnuaGhostOverwrites;
191pub use sensor_sets::TnuaSensorsEntities;
192
193pub mod prelude {
194    pub use crate::TnuaScheme;
195    pub use crate::builtins::{TnuaBuiltinJump, TnuaBuiltinWalk};
196    pub use crate::{
197        TnuaConfig, TnuaController, TnuaControllerPlugin, TnuaPipelineSystems,
198        TnuaUserControlsSystems,
199    };
200}
201
202pub use bevy_tnua_physics_integration_layer::data_for_backends::*;
203pub use bevy_tnua_physics_integration_layer::*;
204
205use bevy::prelude::*;
206
207/// The user controls should be applied in this system set.
208#[derive(SystemSet, Clone, PartialEq, Eq, Debug, Hash)]
209pub struct TnuaUserControlsSystems;