Skip to main content

leafwing_input_manager/
systems.rs

1//! The systems that power each [`InputManagerPlugin`](crate::plugin::InputManagerPlugin).
2
3use crate::prelude::updating::CentralInputStore;
4use bevy::ecs::query::QueryFilter;
5use bevy::log::debug;
6
7use crate::{
8    Actionlike, action_state::ActionState, clashing_inputs::ClashStrategy, input_map::InputMap,
9};
10
11use bevy::ecs::prelude::*;
12use bevy::input::gamepad::Gamepad;
13use bevy::{
14    platform::time::Instant,
15    time::{Real, Time},
16};
17
18use crate::action_diff::{ActionDiffMessage, SummarizedActionState};
19
20/// We are about to enter the `Main` schedule, so we:
21/// - save all the changes applied to `state` into the `fixed_update_state`
22/// - switch to loading the `update_state`
23pub fn swap_to_update<A: Actionlike>(mut query: Query<&mut ActionState<A>>) {
24    for mut action_state in query.iter_mut() {
25        action_state.swap_to_update_state();
26    }
27}
28
29/// We are about to enter the `FixedMain` schedule, so we:
30/// - save all the changes applied to `state` into the `update_state`
31/// - switch to loading the `fixed_update_state`
32pub fn swap_to_fixed_update<A: Actionlike>(mut query: Query<&mut ActionState<A>>) {
33    for mut action_state in query.iter_mut() {
34        action_state.swap_to_fixed_update_state();
35    }
36}
37
38/// Advances actions timer.
39///
40/// Clears the just-pressed and just-released values of all [`ActionState`]s.
41/// Also resets the internal `pressed_this_tick` field, used to track whether to release an action.
42pub fn tick_action_state<A: Actionlike>(
43    mut query: Query<&mut ActionState<A>>,
44    time: Res<Time<Real>>,
45    mut stored_previous_instant: Local<Option<Instant>>,
46) {
47    // If this is the very first tick, measure from the start of the app
48    let current_instant = time.last_update().unwrap_or_else(|| time.startup());
49    let previous_instant = stored_previous_instant.unwrap_or_else(|| time.startup());
50
51    // Only tick the ActionState components if they exist
52    for mut action_state in query.iter_mut() {
53        // If `Time` has not ever been advanced, something has gone horribly wrong
54        // and the user probably forgot to add the `core_plugin`.
55        action_state.tick(current_instant, previous_instant);
56    }
57
58    // Store the previous time in the system
59    *stored_previous_instant = time.last_update();
60}
61
62/// Fetches the [`CentralInputStore`]
63/// to update [`ActionState`] according to the [`InputMap`].
64///
65/// Clashes will be resolved according to the [`ClashStrategy`] resource.
66pub fn update_action_state<A: Actionlike>(
67    input_store: Res<CentralInputStore>,
68    clash_strategy: Res<ClashStrategy>,
69    mut gamepads: Query<Entity, With<Gamepad>>,
70    mut query: Query<(&mut ActionState<A>, &InputMap<A>)>,
71) {
72    for (mut action_state, input_map) in query.iter_mut() {
73        action_state.update(input_map.process_actions(
74            Some(gamepads.reborrow()),
75            &input_store,
76            *clash_strategy,
77        ));
78    }
79}
80
81/// Generates an [`Messages`] stream of [`ActionDiff`s](crate::action_diff::ActionDiff) from every [`ActionState`].
82///
83/// This system is not part of the [`InputManagerPlugin`](crate::plugin::InputManagerPlugin) and must be added manually.
84/// Generally speaking, this should be added as part of [`PostUpdate`](bevy::prelude::PostUpdate),
85/// to ensure that all inputs have been processed and any manual actions have been sent.
86pub fn generate_action_diffs<A: Actionlike>(
87    action_state_query: Query<(Entity, &ActionState<A>)>,
88    previous_action_state: Local<SummarizedActionState<A>>,
89    action_diff_messages: MessageWriter<ActionDiffMessage<A>>,
90) {
91    generate_action_diffs_filtered(
92        action_state_query,
93        previous_action_state,
94        action_diff_messages,
95    )
96}
97
98/// Generates an [`Messages`] stream of [`ActionDiff`s](crate::action_diff::ActionDiff) from the [`ActionState`] of certain entities.
99///
100/// This system is not part of the [`InputManagerPlugin`](crate::plugin::InputManagerPlugin) and must be added manually.
101/// Generally speaking, this should be added as part of [`PostUpdate`](bevy::prelude::PostUpdate),
102/// to ensure that all inputs have been processed and any manual actions have been sent.
103///
104/// This system accepts a [`QueryFilter`] to limit which entities should have action diffs generated.
105pub fn generate_action_diffs_filtered<A: Actionlike, F: QueryFilter>(
106    action_state_query: Query<(Entity, &ActionState<A>), F>,
107    mut previous_action_state: Local<SummarizedActionState<A>>,
108    mut action_diff_messages: MessageWriter<ActionDiffMessage<A>>,
109) {
110    let current_action_state = SummarizedActionState::summarize_filtered(action_state_query);
111    current_action_state.send_diffs(&previous_action_state, &mut action_diff_messages);
112    debug!("previous_action_state: {:?}", previous_action_state);
113    debug!("current_action_state: {:?}", current_action_state);
114    *previous_action_state = current_action_state;
115}
116
117/// Release all inputs when an [`InputMap<A>`] is removed to prmessage them from being held forever.
118///
119/// By default, [`InputManagerPlugin<A>`](crate::plugin::InputManagerPlugin) will run this on [`PostUpdate`](bevy::prelude::PostUpdate).
120/// For components you must remove the [`InputMap<A>`] before [`PostUpdate`](bevy::prelude::PostUpdate)
121/// or this will not run.
122pub fn release_on_input_map_removed<A: Actionlike>(
123    mut removed_components: RemovedComponents<InputMap<A>>,
124    mut action_state_query: Query<&mut ActionState<A>>,
125) {
126    let mut iter = action_state_query.iter_many_mut(removed_components.read());
127    while let Some(mut action_state) = iter.fetch_next() {
128        action_state.reset_all();
129    }
130}
131
132/// Clears all values from the [`CentralInputStore`],
133/// making sure that it can read fresh inputs for the frame.
134pub fn clear_central_input_store(mut input_store: ResMut<CentralInputStore>) {
135    input_store.clear();
136}