bevy_input/
common_conditions.rs

1use crate::ButtonInput;
2use bevy_ecs::system::Res;
3use core::hash::Hash;
4
5/// Stateful run condition that can be toggled via a input press using [`ButtonInput::just_pressed`].
6///
7/// ```no_run
8/// # use bevy_app::{App, NoopPluginGroup as DefaultPlugins, Update};
9/// # use bevy_ecs::prelude::IntoSystemConfigs;
10/// # use bevy_input::{common_conditions::input_toggle_active, prelude::KeyCode};
11///
12/// fn main() {
13///     App::new()
14///         .add_plugins(DefaultPlugins)
15///         .add_systems(Update, pause_menu.run_if(input_toggle_active(false, KeyCode::Escape)))
16///         .run();
17/// }
18///
19/// fn pause_menu() {
20///     println!("in pause menu");
21/// }
22/// ```
23///
24/// If you want other systems to be able to access whether the toggled state is active,
25/// you should use a custom resource or a state for that:
26/// ```no_run
27/// # use bevy_app::{App, NoopPluginGroup as DefaultPlugins, Update};
28/// # use bevy_ecs::prelude::{IntoSystemConfigs, Res, ResMut, Resource};
29/// # use bevy_input::{common_conditions::input_just_pressed, prelude::KeyCode};
30///
31/// #[derive(Resource, Default)]
32/// struct Paused(bool);
33///
34/// fn main() {
35///     App::new()
36///         .add_plugins(DefaultPlugins)
37///         .init_resource::<Paused>()
38///         .add_systems(Update, toggle_pause_state.run_if(input_just_pressed(KeyCode::Escape)))
39///         .add_systems(Update, pause_menu.run_if(|paused: Res<Paused>| paused.0))
40///         .run();
41/// }
42///
43/// fn toggle_pause_state(mut paused: ResMut<Paused>) {
44///     paused.0 = !paused.0;
45/// }
46///
47/// fn pause_menu() {
48///     println!("in pause menu");
49/// }
50/// ```
51pub fn input_toggle_active<T>(
52    default: bool,
53    input: T,
54) -> impl FnMut(Res<ButtonInput<T>>) -> bool + Clone
55where
56    T: Copy + Eq + Hash + Send + Sync + 'static,
57{
58    let mut active = default;
59    move |inputs: Res<ButtonInput<T>>| {
60        active ^= inputs.just_pressed(input);
61        active
62    }
63}
64
65/// Run condition that is active if [`ButtonInput::pressed`] is true for the given input.
66pub fn input_pressed<T>(input: T) -> impl FnMut(Res<ButtonInput<T>>) -> bool + Clone
67where
68    T: Copy + Eq + Hash + Send + Sync + 'static,
69{
70    move |inputs: Res<ButtonInput<T>>| inputs.pressed(input)
71}
72
73/// Run condition that is active if [`ButtonInput::just_pressed`] is true for the given input.
74///
75/// ```no_run
76/// # use bevy_app::{App, NoopPluginGroup as DefaultPlugins, Update};
77/// # use bevy_ecs::prelude::IntoSystemConfigs;
78/// # use bevy_input::{common_conditions::input_just_pressed, prelude::KeyCode};
79/// fn main() {
80///     App::new()
81///         .add_plugins(DefaultPlugins)
82///         .add_systems(Update, jump.run_if(input_just_pressed(KeyCode::Space)))
83///         .run();
84/// }
85///
86/// # fn jump() {}
87/// ```
88pub fn input_just_pressed<T>(input: T) -> impl FnMut(Res<ButtonInput<T>>) -> bool + Clone
89where
90    T: Copy + Eq + Hash + Send + Sync + 'static,
91{
92    move |inputs: Res<ButtonInput<T>>| inputs.just_pressed(input)
93}
94
95/// Run condition that is active if [`ButtonInput::just_released`] is true for the given input.
96pub fn input_just_released<T>(input: T) -> impl FnMut(Res<ButtonInput<T>>) -> bool + Clone
97where
98    T: Copy + Eq + Hash + Send + Sync + 'static,
99{
100    move |inputs: Res<ButtonInput<T>>| inputs.just_released(input)
101}
102
103#[cfg(test)]
104mod tests {
105    use super::*;
106    use crate::prelude::KeyCode;
107    use bevy_ecs::schedule::{IntoSystemConfigs, Schedule};
108
109    fn test_system() {}
110
111    // Ensure distributive_run_if compiles with the common conditions.
112    #[test]
113    fn distributive_run_if_compiles() {
114        Schedule::default().add_systems(
115            (test_system, test_system)
116                .distributive_run_if(input_toggle_active(false, KeyCode::Escape))
117                .distributive_run_if(input_pressed(KeyCode::Escape))
118                .distributive_run_if(input_just_pressed(KeyCode::Escape))
119                .distributive_run_if(input_just_released(KeyCode::Escape)),
120        );
121    }
122}