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}