-
-
Notifications
You must be signed in to change notification settings - Fork 3.5k
Commit
This commit does not belong to any branch on this repository, and may belong to a fork outside of the repository.
add common run conditions to bevy_input
- Loading branch information
1 parent
e27e04a
commit 9ec7e86
Showing
4 changed files
with
119 additions
and
85 deletions.
There are no files selected for viewing
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
Original file line number | Diff line number | Diff line change |
---|---|---|
@@ -0,0 +1,97 @@ | ||
use crate::Input; | ||
use bevy_ecs::system::Res; | ||
use std::hash::Hash; | ||
|
||
/// Stateful run condition that can be toggled via a input press using [`Input::just_pressed`]. | ||
/// | ||
/// ```rust,no_run | ||
/// use bevy::prelude::*; | ||
/// use bevy::input::common_conditions::input_toggle_active; | ||
/// | ||
/// fn main() { | ||
/// App::new() | ||
/// .add_plugins(DefaultPlugins) | ||
/// .add_system(pause_menu.run_if(input_toggle_active(false, KeyCode::Escape))) | ||
/// .run(); | ||
/// } | ||
/// | ||
/// fn pause_menu() { | ||
/// println!("in pause menu"); | ||
/// } | ||
/// ``` | ||
/// | ||
/// If you want other system to be able to access whether the toggled state is active, | ||
/// you should use a custom resource or a state for that: | ||
/// ```rust,no_run | ||
/// use bevy::prelude::*; | ||
/// use bevy::input::common_conditions::input_toggle_active; | ||
/// | ||
/// #[derive(Resource, Default)] | ||
/// struct Paused(bool); | ||
/// | ||
/// fn main() { | ||
/// App::new() | ||
/// .add_plugins(DefaultPlugins) | ||
/// .init_resource::<Paused>() | ||
/// .add_system(pause_menu.run_if(|paused: Res<Paused>| paused.0)) | ||
/// .run(); | ||
/// } | ||
/// | ||
/// fn update_pause_state(mut paused: ResMut<Paused>, input: Input<KeyCode>) { | ||
/// if input.just_pressed(KeyCode::Escape) { | ||
/// paused.0 = !paused.0; | ||
/// } | ||
/// } | ||
/// | ||
/// fn pause_menu() { | ||
/// println!("in pause menu"); | ||
/// } | ||
/// | ||
/// ``` | ||
pub fn input_toggle_active<T>(default: bool, input: T) -> impl FnMut(Res<Input<T>>) -> bool | ||
where | ||
T: Copy + Eq + Hash + Send + Sync + 'static, | ||
{ | ||
let mut active = default; | ||
move |inputs: Res<Input<T>>| { | ||
active ^= inputs.just_pressed(input); | ||
active | ||
} | ||
} | ||
|
||
/// Run condition that is active if [`Input::pressed`] is true for the given input. | ||
pub fn input_pressed<T>(input: T) -> impl FnMut(Res<Input<T>>) -> bool | ||
where | ||
T: Copy + Eq + Hash + Send + Sync + 'static, | ||
{ | ||
move |inputs: Res<Input<T>>| inputs.pressed(input) | ||
} | ||
|
||
/// Run condition that is active if [`Input::just_pressed`] is true for the given input. | ||
/// | ||
/// ```rust,no_run | ||
/// use bevy::prelude::*; | ||
/// use bevy::input::common_conditions::input_just_pressed; | ||
/// fn main() { | ||
/// App::new() | ||
/// .add_plugins(DefaultPlugins) | ||
/// .add_system(jump.run_if(input_just_pressed(KeyCode::Space))) | ||
/// .run(); | ||
/// } | ||
/// | ||
/// # fn jump() {} | ||
/// ``` | ||
pub fn input_just_pressed<T>(input: T) -> impl FnMut(Res<Input<T>>) -> bool | ||
where | ||
T: Copy + Eq + Hash + Send + Sync + 'static, | ||
{ | ||
move |inputs: Res<Input<T>>| inputs.just_pressed(input) | ||
} | ||
|
||
/// Run condition that is active if [`Input::just_released`] is true for the given input. | ||
pub fn input_just_released<T>(input: T) -> impl FnMut(Res<Input<T>>) -> bool | ||
where | ||
T: Copy + Eq + Hash + Send + Sync + 'static, | ||
{ | ||
move |inputs: Res<Input<T>>| inputs.just_released(input) | ||
} |
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
Original file line number | Diff line number | Diff line change |
---|---|---|
@@ -1,4 +1,6 @@ | ||
mod axis; | ||
/// Common run conditions | ||
pub mod common_conditions; | ||
pub mod gamepad; | ||
mod input; | ||
pub mod keyboard; | ||
|
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
Original file line number | Diff line number | Diff line change |
---|---|---|
@@ -1,99 +1,31 @@ | ||
//! This example demonstrates how to use run conditions to control when systems run. | ||
|
||
use std::hash::Hash; | ||
|
||
use bevy::prelude::*; | ||
|
||
fn main() { | ||
println!(); | ||
println!("For the first 2 seconds you will not be able to increment the counter"); | ||
println!("Once that time has passed you can press space, enter, left mouse, right mouse or touch the screen to increment the counter"); | ||
println!(); | ||
|
||
App::new() | ||
.add_plugins(DefaultPlugins) | ||
.init_resource::<InputCounter>() | ||
.add_system( | ||
increment_input_counter | ||
// The common_conditions module has a few useful run conditions | ||
// for checking resources and states. These are included in the prelude. | ||
.run_if(resource_exists::<InputCounter>()) | ||
// This is a custom run condition, defined using a system that returns | ||
// a `bool` and which has read-only `SystemParam`s. | ||
// Both run conditions must return `true` in order for the system to run. | ||
// Note that this second run condition will be evaluated even if the first returns `false`. | ||
.run_if(has_user_input), | ||
) | ||
.add_system( | ||
print_input_counter | ||
// `.and_then()` is a run condition combinator that only evaluates the second condition | ||
// if the first condition returns `true`. This behavior is known as "short-circuiting", | ||
// and is how the `&&` operator works in Rust (as well as most C-family languages). | ||
// In this case, the short-circuiting behavior prevents the second run condition from | ||
// panicking if the `InputCounter` resource has not been initialized. | ||
.run_if(resource_exists::<InputCounter>().and_then( | ||
// This is a custom run condition in the form of a closure. | ||
// This is useful for small, simple run conditions you don't need to reuse. | ||
// All the normal rules still apply: all parameters must be read only except for local parameters. | ||
|counter: Res<InputCounter>| counter.is_changed() && !counter.is_added(), | ||
)), | ||
) | ||
.add_system( | ||
print_time_message | ||
// This function returns a custom run condition, much like the common conditions module. | ||
// It will only return true once 2 seconds have passed. | ||
.run_if(time_passed(2.0)) | ||
// You can use the `not` condition from the common_conditions module | ||
// to inverse a run condition. In this case it will return true if | ||
// less than 2.5 seconds have elapsed since the app started. | ||
.run_if(not(time_passed(2.5))), | ||
) | ||
.add_system(print.run_if(input_toggle_active(true, KeyCode::Escape))) | ||
.add_system(print_2.after(print)) | ||
.run(); | ||
} | ||
|
||
#[derive(Resource, Default)] | ||
struct InputCounter(usize); | ||
|
||
/// Return true if any of the defined inputs were just pressed. | ||
/// This is a custom run condition, it can take any normal system parameters as long as | ||
/// they are read only (except for local parameters which can be mutable). | ||
/// It returns a bool which determines if the system should run. | ||
fn has_user_input( | ||
keyboard_input: Res<Input<KeyCode>>, | ||
mouse_button_input: Res<Input<MouseButton>>, | ||
touch_input: Res<Touches>, | ||
) -> bool { | ||
keyboard_input.just_pressed(KeyCode::Space) | ||
|| keyboard_input.just_pressed(KeyCode::Return) | ||
|| mouse_button_input.just_pressed(MouseButton::Left) | ||
|| mouse_button_input.just_pressed(MouseButton::Right) | ||
|| touch_input.any_just_pressed() | ||
} | ||
|
||
/// This is a function that returns a closure which can be used as a run condition. | ||
/// This is useful because you can reuse the same run condition but with different variables. | ||
/// This is how the common conditions module works. | ||
fn time_passed(t: f32) -> impl FnMut(Local<f32>, Res<Time>) -> bool { | ||
move |mut timer: Local<f32>, time: Res<Time>| { | ||
// Tick the timer | ||
*timer += time.delta_seconds(); | ||
// Return true if the timer has passed the time | ||
*timer >= t | ||
} | ||
} | ||
|
||
/// SYSTEM: Increment the input counter | ||
/// Notice how we can take just the `ResMut` and not have to wrap | ||
/// it in an option in case it hasn't been initialized, this is because | ||
/// it has a run condition that checks if the `InputCounter` resource exists | ||
fn increment_input_counter(mut counter: ResMut<InputCounter>) { | ||
counter.0 += 1; | ||
fn print_2() { | ||
println!(); | ||
} | ||
|
||
/// SYSTEM: Print the input counter | ||
fn print_input_counter(counter: Res<InputCounter>) { | ||
println!("Input counter: {}", counter.0); | ||
fn print() { | ||
dbg!("running"); | ||
} | ||
|
||
/// SYSTEM: Adds the input counter resource | ||
fn print_time_message() { | ||
println!("It has been more than 2 seconds since the program started and less than 2.5 seconds"); | ||
fn input_toggle_active<T>(default: bool, key: T) -> impl FnMut(Res<Input<T>>) -> bool | ||
where | ||
T: Copy + Eq + Hash + Send + Sync + 'static, | ||
{ | ||
let mut active = default; | ||
move |input: Res<Input<T>>| { | ||
active ^= input.just_pressed(key); | ||
active | ||
} | ||
} |