bevy_mod_wanderlust/components/settings.rs
1use bevy::ecs::reflect::ReflectComponent;
2use bevy::math::vec3;
3use bevy::prelude::{default, warn, Component, Entity, Vec3};
4use bevy::reflect::Reflect;
5use bevy::utils::HashSet;
6use bevy_rapier3d::prelude::Collider;
7
8use crate::Spring;
9
10/// The settings of a character controller. See each individual field for more description.
11///
12/// The [`Default::default()`] of this type is not well configured; it is not a good reference for any character controller, and will not do much.
13/// See bundles like [`CharacterControllerBundle`](super::bundles::CharacterControllerBundle) for well-config
14#[derive(Component, Reflect)]
15#[reflect(Component)]
16pub struct ControllerSettings {
17 /// How quickly to interpolate from `last_goal_velocity` to the new `input_goal_velocity`.
18 /// In other words, how quickly to go from "not moving" to "moving at max speed".
19 pub acceleration: f32,
20 /// The length of the calculated `input_goal_velocity`.
21 /// In other words, the speed to attempt to reach if a movement input (such as forwards) is fully saturated.
22 ///
23 /// Keys are generally either not saturated or fully saturated, while analog controls like a joystick can be partially saturated (half tilt).
24 pub max_speed: f32,
25 /// The maximum amount of force that can be applied to fulfill [`acceleration`](ControllerSettings::acceleration).
26 pub max_acceleration_force: f32,
27 /// The direction to jump, which is also the direction that gravity is opposite to.
28 pub up_vector: Vec3,
29 /// The direction to face towards, or `None` to not rotate to face any direction. Must be perpendicular to the up vector and normalized.
30 pub forward_vector: Option<Vec3>,
31 /// The strength of gravity.
32 pub gravity: f32,
33 /// The maximum angle that the ground can be, in radians, before it is no longer considered suitable for being "grounded" on.
34 ///
35 /// For example, if this is set to `π/4` (45 degrees), then a player standing on a slope steeper than 45 degrees will slip and fall, and will not have
36 /// their jump refreshed by landing on that surface.
37 pub max_ground_angle: f32,
38 /// While floating, the character can be floating at a different exact distance than [`float_distance`] depending on other forces acting on them.
39 /// This field controls how much lower than [`float_distance`] they can be and still be considered grounded.
40 ///
41 /// This helps keep jumps more consistent when the ground cast length is longer than the float distance.
42 pub min_float_offset: f32,
43 /// While floating, the character can be floating at a different exact distance than [`float_distance`] depending on other forces acting on them.
44 /// This field controls how much higher than [`float_distance`] they can be and still be considered grounded.
45 ///
46 /// This helps keep jumps more consistent when the ground cast length is longer than the float distance.
47 pub max_float_offset: f32,
48 /// The amount of force to apply on the first frame when a jump begins.
49 pub jump_initial_force: f32,
50 /// The amount of force to continuously apply every second during a jump.
51 pub jump_force: f32,
52 /// The amount of force to apply downwards when the jump control is released prior to a jump expiring.
53 /// This allows analog jumping by cutting the jump short when the control is released.
54 pub jump_stop_force: f32,
55 /// How long a jump can last.
56 pub jump_time: f32,
57 /// A function taking the current progress of a jump, from 0.0 to 1.0, with 0.0 indicating a jump has just begun and 1.0 indicating the jump has ended,
58 /// which returns a modifier (usually from 0.0 to 1.0, but not necessarily) to multiply [`jump_force`](ControllerSettings::jump_force) by.
59 #[reflect(ignore)]
60 pub jump_decay_function: Option<fn(f32) -> f32>,
61 /// How long to skip ground checks after jumping. Usually this should be set just high enough that the character is out of range of the ground
62 /// just before the timer elapses.
63 pub jump_skip_ground_check_duration: f32,
64 /// Override skip ground check. If true, never checks for the ground.
65 pub skip_ground_check_override: bool,
66 /// How many extra times the character can jump after leaving the ground. 0 is normal, 1 corresponds to double jump, etc.
67 pub extra_jumps: u32,
68 /// How long should the character still be able to jump after leaving the ground, in seconds.
69 /// For example, if this is set to 0.5, the player can fall off a ledge and then jump if they do so within 0.5 seconds of leaving the ledge.
70 pub coyote_time_duration: f32,
71 /// If the jump input is pressed before landing, how long will the jump be buffered for?
72 /// In other words, if this is 0.5, the character can input jump up to 0.5 seconds before landing and the jump will occur when they land.
73 pub jump_buffer_duration: f32,
74 /// Scales movement force. This is useful to ensure movement does not affect vertical velocity (by setting it to e.g. `Vec3(1.0, 0.0, 1.0)`).
75 pub force_scale: Vec3,
76 /// How long of a ray to cast to detect the ground. Setting this unnecessarily high will permanently count the player as grounded,
77 /// and too low will allow the player to slip and become disconnected from the ground easily.
78 pub float_cast_length: f32,
79 /// An offset to start the ground check from, relative to the character's origin.
80 pub float_cast_origin: Vec3,
81 /// What shape of ray to cast. See [`Collider`] and [`RapierContext::cast_shape`](RapierContext).
82 #[reflect(ignore)]
83 pub float_cast_collider: Collider,
84 /// How far to attempt to float away from the ground.
85 pub float_distance: f32,
86 /// How strongly to float away from the ground.
87 pub float_spring: Spring,
88 /// How strongly to force the character upright/avoid overshooting. Alternatively, see [`LockedAxes`] to lock rotation entirely.
89 pub upright_spring: Spring,
90 /// Set of entities that should be ignored when ground casting.
91 pub exclude_from_ground: HashSet<Entity>,
92 /// Scaling factor for the impulse applied to the ground to keep the character moving/off the ground.
93 pub opposing_impulse_scale: f32,
94 /// Scaling factor for the movement impulse applied to the ground.
95 /// Setting this to 0.0 would make it so things don't "slip" out from the characters feet.
96 pub opposing_movement_impulse_scale: f32,
97}
98
99impl Default for ControllerSettings {
100 fn default() -> Self {
101 Self {
102 acceleration: default(),
103 max_speed: default(),
104 max_acceleration_force: default(),
105 up_vector: default(),
106 forward_vector: default(),
107 gravity: default(),
108 max_ground_angle: default(),
109 min_float_offset: default(),
110 max_float_offset: default(),
111 jump_initial_force: default(),
112 jump_force: default(),
113 jump_stop_force: default(),
114 jump_time: 1.0,
115 jump_decay_function: None,
116 jump_skip_ground_check_duration: default(),
117 skip_ground_check_override: default(),
118 extra_jumps: default(),
119 coyote_time_duration: default(),
120 jump_buffer_duration: default(),
121 force_scale: default(),
122 float_cast_length: default(),
123 float_cast_origin: default(),
124 float_cast_collider: Collider::ball(1.0),
125 float_distance: default(),
126 float_spring: default(),
127 upright_spring: default(),
128 exclude_from_ground: default(),
129 opposing_impulse_scale: 1.0,
130 opposing_movement_impulse_scale: 1.0,
131 }
132 }
133}
134
135impl ControllerSettings {
136 /// A basic preset for a standard, walking character controller. Works for most first and third person games.
137 pub fn character() -> Self {
138 ControllerSettings {
139 acceleration: 50.0,
140 max_speed: 10.0,
141 max_acceleration_force: 10.0,
142 up_vector: Vec3::Y,
143 //gravity: -9.8,
144 gravity: -20.0,
145 max_ground_angle: 45.0 * (std::f32::consts::PI / 180.0),
146 min_float_offset: -0.3,
147 max_float_offset: 0.05,
148 jump_time: 0.5,
149 jump_initial_force: 15.0,
150 jump_stop_force: 0.3,
151 jump_decay_function: Some(|x| (1.0 - x).sqrt()),
152 jump_skip_ground_check_duration: 0.5,
153 coyote_time_duration: 0.16,
154 jump_buffer_duration: 0.16,
155 force_scale: vec3(1.0, 0.0, 1.0),
156 float_cast_length: 1.0,
157 float_cast_collider: Collider::ball(0.45),
158 float_distance: 0.55,
159 float_spring: Spring {
160 strength: 100.0,
161 damping: 0.8,
162 },
163 upright_spring: Spring {
164 strength: 10.0,
165 damping: 0.5,
166 },
167 ..default()
168 }
169 }
170
171 /// A sample controller preset for a spaceship which can fly in any direction.
172 pub fn starship() -> Self {
173 ControllerSettings {
174 acceleration: 0.3,
175 max_speed: 100.0,
176 max_acceleration_force: 10.0,
177 up_vector: Vec3::Y,
178 force_scale: vec3(1.0, 1.0, 1.0),
179 upright_spring: Spring {
180 strength: 0.0,
181 damping: 0.0,
182 },
183 ..default()
184 }
185 }
186
187 /// Validate that assumptions made in the settings are correct.
188 pub fn valid(&self) -> bool {
189 let mut valid = true;
190 if !self.up_vector.is_normalized() {
191 warn!("controller up vector is not normalized");
192 valid = false;
193 }
194
195 valid
196 }
197}