leafwing-input-manager 0.21.0

A powerful, flexible and ergonomic way to manage action-input keybindings for the Bevy game engine.
Documentation
//! The systems that power each [`InputManagerPlugin`](crate::plugin::InputManagerPlugin).

use crate::prelude::updating::CentralInputStore;
use bevy::ecs::query::QueryFilter;
use bevy::log::debug;

use crate::{
    Actionlike, action_state::ActionState, clashing_inputs::ClashStrategy, input_map::InputMap,
};

use bevy::ecs::prelude::*;
use bevy::input::gamepad::Gamepad;
use bevy::{
    platform::time::Instant,
    time::{Real, Time},
};

use crate::action_diff::{ActionDiffMessage, SummarizedActionState};

/// We are about to enter the `Main` schedule, so we:
/// - save all the changes applied to `state` into the `fixed_update_state`
/// - switch to loading the `update_state`
pub fn swap_to_update<A: Actionlike>(mut query: Query<&mut ActionState<A>>) {
    for mut action_state in query.iter_mut() {
        action_state.swap_to_update_state();
    }
}

/// We are about to enter the `FixedMain` schedule, so we:
/// - save all the changes applied to `state` into the `update_state`
/// - switch to loading the `fixed_update_state`
pub fn swap_to_fixed_update<A: Actionlike>(mut query: Query<&mut ActionState<A>>) {
    for mut action_state in query.iter_mut() {
        action_state.swap_to_fixed_update_state();
    }
}

/// Advances actions timer.
///
/// Clears the just-pressed and just-released values of all [`ActionState`]s.
/// Also resets the internal `pressed_this_tick` field, used to track whether to release an action.
pub fn tick_action_state<A: Actionlike>(
    mut query: Query<&mut ActionState<A>>,
    time: Res<Time<Real>>,
    mut stored_previous_instant: Local<Option<Instant>>,
) {
    // If this is the very first tick, measure from the start of the app
    let current_instant = time.last_update().unwrap_or_else(|| time.startup());
    let previous_instant = stored_previous_instant.unwrap_or_else(|| time.startup());

    // Only tick the ActionState components if they exist
    for mut action_state in query.iter_mut() {
        // If `Time` has not ever been advanced, something has gone horribly wrong
        // and the user probably forgot to add the `core_plugin`.
        action_state.tick(current_instant, previous_instant);
    }

    // Store the previous time in the system
    *stored_previous_instant = time.last_update();
}

/// Fetches the [`CentralInputStore`]
/// to update [`ActionState`] according to the [`InputMap`].
///
/// Clashes will be resolved according to the [`ClashStrategy`] resource.
pub fn update_action_state<A: Actionlike>(
    input_store: Res<CentralInputStore>,
    clash_strategy: Res<ClashStrategy>,
    mut gamepads: Query<Entity, With<Gamepad>>,
    mut query: Query<(&mut ActionState<A>, &InputMap<A>)>,
) {
    for (mut action_state, input_map) in query.iter_mut() {
        action_state.update(input_map.process_actions(
            Some(gamepads.reborrow()),
            &input_store,
            *clash_strategy,
        ));
    }
}

/// Generates an [`Messages`] stream of [`ActionDiff`s](crate::action_diff::ActionDiff) from every [`ActionState`].
///
/// This system is not part of the [`InputManagerPlugin`](crate::plugin::InputManagerPlugin) and must be added manually.
/// Generally speaking, this should be added as part of [`PostUpdate`](bevy::prelude::PostUpdate),
/// to ensure that all inputs have been processed and any manual actions have been sent.
pub fn generate_action_diffs<A: Actionlike>(
    action_state_query: Query<(Entity, &ActionState<A>)>,
    previous_action_state: Local<SummarizedActionState<A>>,
    action_diff_messages: MessageWriter<ActionDiffMessage<A>>,
) {
    generate_action_diffs_filtered(
        action_state_query,
        previous_action_state,
        action_diff_messages,
    )
}

/// Generates an [`Messages`] stream of [`ActionDiff`s](crate::action_diff::ActionDiff) from the [`ActionState`] of certain entities.
///
/// This system is not part of the [`InputManagerPlugin`](crate::plugin::InputManagerPlugin) and must be added manually.
/// Generally speaking, this should be added as part of [`PostUpdate`](bevy::prelude::PostUpdate),
/// to ensure that all inputs have been processed and any manual actions have been sent.
///
/// This system accepts a [`QueryFilter`] to limit which entities should have action diffs generated.
pub fn generate_action_diffs_filtered<A: Actionlike, F: QueryFilter>(
    action_state_query: Query<(Entity, &ActionState<A>), F>,
    mut previous_action_state: Local<SummarizedActionState<A>>,
    mut action_diff_messages: MessageWriter<ActionDiffMessage<A>>,
) {
    let current_action_state = SummarizedActionState::summarize_filtered(action_state_query);
    current_action_state.send_diffs(&previous_action_state, &mut action_diff_messages);
    debug!("previous_action_state: {:?}", previous_action_state);
    debug!("current_action_state: {:?}", current_action_state);
    *previous_action_state = current_action_state;
}

/// Release all inputs when an [`InputMap<A>`] is removed to prmessage them from being held forever.
///
/// By default, [`InputManagerPlugin<A>`](crate::plugin::InputManagerPlugin) will run this on [`PostUpdate`](bevy::prelude::PostUpdate).
/// For components you must remove the [`InputMap<A>`] before [`PostUpdate`](bevy::prelude::PostUpdate)
/// or this will not run.
pub fn release_on_input_map_removed<A: Actionlike>(
    mut removed_components: RemovedComponents<InputMap<A>>,
    mut action_state_query: Query<&mut ActionState<A>>,
) {
    let mut iter = action_state_query.iter_many_mut(removed_components.read());
    while let Some(mut action_state) = iter.fetch_next() {
        action_state.reset_all();
    }
}

/// Clears all values from the [`CentralInputStore`],
/// making sure that it can read fresh inputs for the frame.
pub fn clear_central_input_store(mut input_store: ResMut<CentralInputStore>) {
    input_store.clear();
}