bevy_controls 1.0.0

Bevy controls library
Documentation
use bevy::{
    input::{
        gamepad::{GamepadAxisChangedEvent, GamepadButtonChangedEvent},
        keyboard::KeyboardInput,
        mouse::MouseButtonInput,
    },
    prelude::*,
    state::state::FreelyMutableState,
};
use std::marker::PhantomData;

use crate::{
    analog::{analog_control::AnalogControl, analog_inputs::AnalogInputs},
    button::buttons::Buttons,
};

pub struct AnalogPlugin<TContext, TAction>(PhantomData<TContext>, PhantomData<TAction>);

impl<TC, TA> Default for AnalogPlugin<TC, TA> {
    fn default() -> Self {
        Self(Default::default(), Default::default())
    }
}

impl<TContext: FreelyMutableState + FromWorld, TAction: Sync + Send + 'static> Plugin
    for AnalogPlugin<TContext, TAction>
{
    fn build(&self, app: &mut App) {
        app.init_state::<TContext>()
            .add_systems(Update, Self::check_controls);
    }
}

impl<TContext: States, TAction: Sync + Send + 'static> AnalogPlugin<TContext, TAction> {
    fn check_controls(
        mut controls: Query<&mut AnalogControl<TContext, TAction>>,
        context: Res<State<TContext>>,
        mut keyboard_input: MessageReader<KeyboardInput>,
        mut mouse_button_input: MessageReader<MouseButtonInput>,
        mut gamepad_button_input: MessageReader<GamepadButtonChangedEvent>,
        mut gamepad_axis_input: MessageReader<GamepadAxisChangedEvent>,
    ) {
        let mut active_controls = controls
            .iter_mut()
            .filter(|c| c.contexts.contains(&context.get()))
            .collect::<Vec<_>>();

        let gamepad_inputs = gamepad_button_input.read().collect::<Vec<_>>();

        let button_inputs = keyboard_input
            .read()
            .filter(|k| !k.repeat)
            .map(|k| (Buttons::Keyboard(k.key_code), k.state.is_pressed()))
            .chain(
                mouse_button_input
                    .read()
                    .map(|m| (Buttons::Mouse(m.button), m.state.is_pressed())),
            )
            .chain(
                gamepad_inputs
                    .iter()
                    .map(|g| (Buttons::Gamepad(g.button), g.state.is_pressed())),
            );

        let analog_inputs = gamepad_inputs
            .iter()
            .map(|g| (AnalogInputs::Gamepad(g.button), g.value))
            .chain(
                gamepad_axis_input
                    .read()
                    .map(|ga| (AnalogInputs::GamepadAxis(ga.axis), ga.value)),
            );

        for (inputs, value) in analog_inputs {
            let interacted_controls = active_controls
                .iter_mut()
                .filter(|c| c.mappings.contains(&inputs));

            for control in interacted_controls {
                control.analog_input(value);
            }
        }

        for (button, is_pressed) in button_inputs {
            let interacted_controls = active_controls.iter_mut().map(|c| {
                (
                    c.mappings
                        .iter()
                        .find(|m| match m {
                            AnalogInputs::Button(button_mapping, _) => *button_mapping == button,
                            _ => false,
                        })
                        .cloned(),
                    c,
                )
            });

            for (input, control) in interacted_controls {
                let Some(AnalogInputs::Button(_, input_value)) = input else {
                    continue;
                };

                if is_pressed {
                    control.button_pressed(input_value);
                } else {
                    control.button_released(input_value);
                }
            }
        }
    }
}