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;