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
26//! [`TnuaControllerPlugin`](prelude::TnuaControllerPlugin) should also be 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 (such as
31//! [`TnuaCrouchEnforcer`](crate::control_helpers::TnuaCrouchEnforcer)) must also be registered in
32//! that schedule, using their `::new()` method instead of `::default()`. The player controls
33//! systems must also be registered under that same schedule (instead of under `Update`, which is
34//! where it should usually be registered)
35//!
36//! A Tnua controlled character must have a dynamic rigid body, everything from
37//! `Tnua<physics-backend>IOBundle` (e.g. - for Rapier 3D, use `TnuaRapier3dIOBundle`), and a
38//! [`TnuaController`](prelude::TnuaController) (and its automatically added required component):
39//! ```no_run
40//! # use bevy::prelude::*;
41//! # // Not importing from Rapier because there are two versions and the default features does not
42//! # // enable either:
43//! # type TnuaRapier3dIOBundle = ();
44//! # #[derive(Component)]
45//! # enum RigidBody { Dynamic }
46//! # use bevy_tnua::prelude::*;
47//! # let mut commands: Commands = panic!();
48//! # let mut cmd = commands.spawn_empty();
49//! cmd.insert(RigidBody::Dynamic);
50//! cmd.insert(TnuaRapier3dIOBundle::default()); // this one depends on the physics backend
51//! cmd.insert(TnuaController::default());
52//! ```
53//! Typically though it'd also include a `Collider`.
54//!
55//! ## Optional But Recommended
56//!
57//! * Tnua, by default, casts a single ray to the ground. This can be a problem when the character
58//! stands on a ledge, because the ray may be past the ledge while the character's collider
59//! isn't. To avoid that, use `Tnua<physics-backend>SensorShape` (e.g. - for Rapier 3D, use
60//! `TnuaRapier3dSensorShape`) to replace the ray with a shape that resembles the collider. It is
61//! better to use a shape a little bit smaller than the collider, so that when the character
62//! presses against a wall Tnua won't think it should be lifted up when the casted shape hits
63//! that wall.
64//! * Tnua will apply forces to keep the character upright, but it is also possible to lock
65//! rotation so that there would be no tilting at all. This is done by Tnua itself - it has to be
66//! done by the physics engine. Both Rapier and Avian can do it using a component called
67//! `LockedAxes`. When using it in 3D in combination of rotation controls (such as
68//! [`TnuaBuiltinWalk::desired_forward`](builtins::TnuaBuiltinWalk::desired_forward)) make sure
69//! to only lock the X and Z axess, so that Tnua could rotate the character around the Y axis.
70//!
71//! ## Controlling the Character
72//!
73//! To control the character, update the [`TnuaController`](prelude::TnuaController) by feeding it
74//! a [basis](TnuaBasis) and zero or more [actions](TnuaAction). For some of the advanced features
75//! to work, the system that does this needs to be placed inside the [`TnuaUserControlsSystemSet`]
76//! system set.
77//!
78//! ```no_run
79//! # use bevy::prelude::*;
80//! # use bevy_tnua::prelude::*;
81//! # use bevy_tnua::math::Vector3;
82//! # #[derive(Component)]
83//! # struct PlayerInputComponent;
84//! # impl PlayerInputComponent {
85//! # fn direction_vector(&self) -> Vector3 { Vector3::ZERO }
86//! # fn jump_pressed(&self) -> bool { false }
87//! # }
88//! fn player_control_system(mut query: Query<(
89//! &mut TnuaController,
90//! &PlayerInputComponent, // not part of Tnua - defined in user code
91//! )>) {
92//! for (mut controller, player_input) in query.iter_mut() {
93//! controller.basis(TnuaBuiltinWalk {
94//! // Move in the direction the player entered, at a speed of 10.0:
95//! desired_velocity: player_input.direction_vector() * 10.0,
96//!
97//! // Turn the character in the movement direction:
98//! desired_forward: Dir3::new(player_input.direction_vector()).ok(),
99//!
100//! // Must be larger than the height of the entity's center from the bottom of its
101//! // collider, or else the character will not float and Tnua will not work properly:
102//! float_height: 2.0,
103//!
104//! // TnuaBuiltinWalk has many other fields that can be configured:
105//! ..Default::default()
106//! });
107//!
108//! if player_input.jump_pressed() {
109//! // The jump action must be fed as long as the player holds the button.
110//! controller.action(TnuaBuiltinJump {
111//! // The full height of the jump, if the player does not release the button:
112//! height: 4.0,
113//!
114//! // TnuaBuiltinJump too has other fields that can be configured:
115//! ..Default::default()
116//! });
117//! }
118//! }
119//! }
120//! ```
121//! Refer to the documentation of [`TnuaController`](prelude::TnuaController) for more information,
122//! but essentially the _basis_ controls the general movement and the _action_ is something
123//! special (jump, dash, crouch, etc.)
124//!
125//! ## Motion Based Animation
126//!
127//! [`TnuaController`](crate::prelude::TnuaController) can also be used to retreive data that can
128//! be used to decide which animation to play. A useful helper for that is [`TnuaAnimatingState`].
129mod animating_helper;
130mod basis_action_traits;
131pub mod builtins;
132pub mod control_helpers;
133pub mod controller;
134pub mod radar_lens;
135pub mod util;
136pub use animating_helper::{TnuaAnimatingState, TnuaAnimatingStateDirective};
137pub use basis_action_traits::{
138 DynamicAction, DynamicBasis, TnuaAction, TnuaActionContext, TnuaActionInitiationDirective,
139 TnuaActionLifecycleDirective, TnuaActionLifecycleStatus, TnuaBasis, TnuaBasisContext,
140};
141
142pub mod prelude {
143 pub use crate::builtins::{TnuaBuiltinJump, TnuaBuiltinWalk};
144 pub use crate::controller::{TnuaController, TnuaControllerPlugin};
145 pub use crate::{TnuaAction, TnuaPipelineStages, TnuaUserControlsSystemSet};
146}
147
148pub use bevy_tnua_physics_integration_layer::data_for_backends::*;
149pub use bevy_tnua_physics_integration_layer::*;
150
151use bevy::prelude::*;
152
153/// The user controls should be applied in this system set.
154#[derive(SystemSet, Clone, PartialEq, Eq, Debug, Hash)]
155pub struct TnuaUserControlsSystemSet;