1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
52
53
54
55
56
57
58
59
60
61
62
63
64
65
66
67
68
69
70
71
72
73
74
75
76
77
78
79
80
81
82
83
84
85
86
87
88
89
90
91
92
93
94
95
96
97
98
99
100
101
102
103
104
105
106
107
108
109
110
111
112
113
114
115
116
117
118
119
120
121
122
123
124
125
126
127
128
129
130
131
132
133
134
135
136
137
138
139
140
141
142
143
144
145
146
//! # Tnua - A Character Controller for bevy_rapier.
//!
//! Tnua ("motion" in Hebrew) is a floating character controller, which means that instead of
//! constantly touching the ground the character floats above it, which makes many aspects of the
//! motion control simpler.
//!
//! Tnua uses [Rapier](https://rapier.rs/), and supports both the 2D and 3D versions of it:
//!
//! * For 2D, enable `features = ["rapier_2d"]` and use [`TnuaRapier2dPlugin`].
//! * For 3D, enable `features = ["rapier_3d"]` and use [`TnuaRapier3dPlugin`].
//!
//! In addition to the physics backend plugin, the [`TnuaPlatformerPlugin`] should also be added.
//!
//! A Tnua controlled character must have a dynamic rigid body, everything from
//! [`TnuaRapier2dIOBundle`]/[`TnuaRapier3dIOBundle`] (depending on the physics backend), and
//! everything from [`TnuaPlatformerBundle`]:
//! ```no_run
//! # use bevy::prelude::*;
//! # // Not importing from Rapier because there are two versions and the default features does not
//! # // enable either:
//! # type TnuaRapier3dIOBundle = ();
//! # #[derive(Component)]
//! # enum RigidBody { Dynamic }
//! # use bevy_tnua::{TnuaPlatformerBundle, TnuaPlatformerConfig, TnuaFreeFallBehavior};
//! # let mut commands: Commands = panic!();
//! # let mut cmd = commands.spawn_empty();
//! cmd.insert(RigidBody::Dynamic);
//! cmd.insert(TnuaRapier3dIOBundle::default());
//! cmd.insert(TnuaPlatformerBundle {
//!     config: TnuaPlatformerConfig {
//!         full_speed: 20.0,
//!         full_jump_height: 4.0,
//!         up: Vec3::Y,
//!         forward: -Vec3::Z,
//!         float_height: 2.0,
//!         cling_distance: 1.0,
//!         spring_strengh: 400.0,
//!         spring_dampening: 1.2,
//!         acceleration: 60.0,
//!         air_acceleration: 20.0,
//!         coyote_time: 0.15,
//!         jump_input_buffer_time: 0.2,
//!         held_jump_cooldown: None,
//!         upslope_jump_extra_gravity: 30.0,
//!         jump_takeoff_extra_gravity: 30.0,
//!         jump_takeoff_above_velocity: 2.0,
//!         jump_fall_extra_gravity: 20.0,
//!         jump_shorten_extra_gravity: 60.0,
//!         jump_peak_prevention_at_upward_velocity: 1.0,
//!         jump_peak_prevention_extra_gravity: 20.0,
//!         free_fall_behavior: TnuaFreeFallBehavior::LikeJumpShorten,
//!         tilt_offset_angvel: 10.0,
//!         tilt_offset_angacl: 1000.0,
//!         turning_angvel: 10.0,
//!         height_change_impulse_for_duration: 0.02,
//!         height_change_impulse_limit: 10.0,
//!     },
//!     ..Default::default()
//! });
//! ```
//! Typically though it'd also include a `Collider`.
//!
//! ## Optional But Recommended
//!
//! * Tnua, by default, casts a single ray to the ground. This can be a problem when the character
//!   stands on a ledge, because the ray may be past the ledge while the character's collider
//!   isn't. To avoid that, use [`TnuaRapier2dSensorShape`] or [`TnuaRapier3dSensorShape`]
//!   (depending on the physics backend) to replace the ray with a shape that resembles the
//!   collider. It is better to use a shape a little bit smaller than the collider, so that when
//!   the character presses against a wall Tnua won't think it should be lifted up when the casted
//!   shape hits that wall.
//! * Tnua will apply forces to keep the character upright, but `LockedAxes` can also be used to
//!   prevent tilting entirely (without it the tilting will be visible)
//!
//! ## Controlling the Character
//!
//! To control the character, update the [`TnuaPlatformerControls`] in a system. For some of the
//! advanced features to work, this system needs to be placed inside the
//! [`TnuaUserControlsSystemSet`] system set.
//!
//! ```no_run
//! # use bevy::prelude::*;
//! # use bevy_tnua::{TnuaPlatformerControls};
//! fn player_control_system(mut query: Query<&mut TnuaPlatformerControls>) {
//!     for mut controls in query.iter_mut() {
//!         *controls = TnuaPlatformerControls {
//!             desired_velocity: Vec3::X, // always go right for some reason
//!             desired_forward: -Vec3::X, // face backwards from walking direction
//!             jump: None, // no jumping
//!             float_height_offset: 0.0, // not crouching
//!         };
//!     }
//! }
//! ```
//! Tnua does not write to [`TnuaPlatformerControls`] - only reads from it - so it should be updated
//! every frame.
//!
//! ## Motion Based Animation
//!
//! If the [`TnuaPlatformerAnimatingOutput`] component is added to the entity, Tnua will keep it
//! updated with data that can be used to decide which animation to play.
//! a useful helper for that.
mod animating_helper;
#[cfg(feature = "rapier_2d")]
mod backend_rapier2d;
#[cfg(feature = "rapier_3d")]
mod backend_rapier3d;
pub mod control_helpers;
mod platformer;
mod subservient_sensors;
mod util;
pub use animating_helper::{TnuaAnimatingState, TnuaAnimatingStateDirective};

#[cfg(feature = "rapier_2d")]
pub use backend_rapier2d::*;
#[cfg(feature = "rapier_3d")]
pub use backend_rapier3d::*;
pub use platformer::*;

mod data_for_backends;
pub use data_for_backends::*;

use bevy::prelude::*;

/// Umbrella system set for [`TnuaPipelineStages`].
///
/// To disable Tnua in specific state, put a run condition on this system set.
#[derive(SystemSet, Clone, PartialEq, Eq, Debug, Hash)]
pub struct TnuaSystemSet;

/// The various stages of the Tnua pipeline.
#[derive(SystemSet, Clone, PartialEq, Eq, Debug, Hash)]
pub enum TnuaPipelineStages {
    /// Data is read from the physics backend.
    Sensors,
    /// Data is propagated through the subservient sensors.
    SubservientSensors,
    /// Tnua decieds how the entity should be manipulated.
    Logic,
    /// Forces are applied in the physiscs backend.
    Motors,
}

/// The user controls should be applied in this system set.
#[derive(SystemSet, Clone, PartialEq, Eq, Debug, Hash)]
pub struct TnuaUserControlsSystemSet;