bevy_controls 1.2.0

Bevy controls library
Documentation
use bevy::prelude::*;
use bevy_controls::{
    accumulative::{
        accumulative_action_reader::AccumulativeActionReader,
        accumulative_control::AccumulativeControl, accumulative_input::AccumulativeInputs,
    },
    analog::{
        analog_action_reader::AnalogActionReader, analog_control::AnalogControl,
        analog_inputs::AnalogInputs,
    },
    button::{
        button_action_reader::ButtonActionReader, button_control::ButtonControl, buttons::Buttons,
    },
    controls_plugin::ControlsPlugin,
};
use bevy_inspector_egui::{bevy_egui::EguiPlugin, quick::WorldInspectorPlugin};

#[derive(Debug, Clone, Eq, PartialEq, Hash, Default, States, Reflect)]
enum ControlContext {
    #[default]
    MainMenu,
    Game,
    OpenInventory,
    Paused,
}

#[derive(Debug, Clone, Copy, Default, PartialEq, Eq, Reflect)]
enum ControlAction {
    #[default]
    Test1,
    TestAnalog1,
    TestMovementX,
    TestMovementY,
    TestAccumulativeX,
    TestAccumulativeY,
    NextContext,
}

fn main() {
    App::new()
        .register_type::<ButtonControl<ControlContext, ControlAction>>()
        .register_type::<AnalogControl<ControlContext, ControlAction>>()
        .add_plugins((
            DefaultPlugins,
            EguiPlugin::default(),
            WorldInspectorPlugin::new(),
            ControlsPlugin::<ControlContext, ControlAction>::default(),
        ))
        .add_systems(Startup, scene.spawn())
        .add_systems(Update, (update_state, update))
        .run();
}

fn update(
    mut controls: Query<&mut ButtonControl<ControlContext, ControlAction>>,
    analog_controls: Query<&AnalogControl<ControlContext, ControlAction>>,
    mut accumulative_controls: Query<&mut AccumulativeControl<ControlContext, ControlAction>>,
    mut test_accumulative: Local<Vec2>,
) {
    if controls.action_just_pressed(ControlAction::Test1) {
        info!("Executed TEST Action!");
    }

    if let Some(test_analog) = analog_controls.read_action(ControlAction::TestAnalog1) {
        info!("Test analog value: {}", test_analog);
    }

    if let Some(move_x) = analog_controls.read_action(ControlAction::TestMovementX)
        && let Some(move_y) = analog_controls.read_action(ControlAction::TestMovementY)
    {
        let mut movement = Vec2::new(move_x, move_y);
        if movement.length() > 1. {
            movement = movement.normalize_or_zero();
        }

        info!("Test Movement: {}", movement);
    }

    let delta = Vec2::new(
        accumulative_controls.read_action_delta(ControlAction::TestAccumulativeX),
        accumulative_controls.read_action_delta(ControlAction::TestAccumulativeY),
    );

    *test_accumulative += delta;

    if !accumulative_controls.is_empty() {
        info!("Test Accumulative: {}", *test_accumulative);
    }
}

fn scene() -> impl SceneList {
    bsn_list!(
        (
            Camera2d
        ),
        (
            Name("Next Context Control")
            ButtonControl<ControlContext, ControlAction> {
                action: ControlAction::NextContext,
                contexts: vec![
                    ControlContext::MainMenu,
                    ControlContext::Game,
                    ControlContext::Paused,
                    ControlContext::OpenInventory,
                ],
                mappings: vec![Buttons::Keyboard(KeyCode::Space)]
            }
        ),
        (
            Name("Test1 Control")
            ButtonControl<ControlContext, ControlAction> {
                action: ControlAction::Test1,
                contexts: vec![ControlContext::MainMenu],
                mappings: vec![
                    Buttons::Keyboard(KeyCode::Enter),
                    Buttons::Mouse(MouseButton::Left),
                    Buttons::Gamepad(GamepadButton::North),
                ],
            }
        ),
        (
            Name("TestAnalog1 Control")
            AnalogControl<ControlContext, ControlAction> {
                action: ControlAction::TestAnalog1,
                contexts: vec![ControlContext::Paused],
                mappings: vec![
                    AnalogInputs::Gamepad(GamepadButton::RightTrigger2),
                    AnalogInputs::Button(Buttons::Gamepad(GamepadButton::North), 1.),
                    AnalogInputs::Button(Buttons::Gamepad(GamepadButton::South), -1.),
                ],
            }
        ),
        (
            Name("Test Movement X")
            AnalogControl<ControlContext, ControlAction> {
                action: ControlAction::TestMovementX,
                contexts: vec![ControlContext::Game, ControlContext::OpenInventory],
                mappings: vec![
                    AnalogInputs::GamepadAxis(GamepadAxis::LeftStickX),
                    AnalogInputs::Button(Buttons::Keyboard(KeyCode::KeyA), -1.),
                    AnalogInputs::Button(Buttons::Keyboard(KeyCode::KeyD), 1.),
                ],
            }
        ),
        (
            Name("Test Movement Y")
            AnalogControl<ControlContext, ControlAction> {
                action: ControlAction::TestMovementY,
                contexts: vec![ControlContext::Game, ControlContext::OpenInventory],
                mappings: vec![
                    AnalogInputs::GamepadAxis(GamepadAxis::LeftStickY),
                    AnalogInputs::Button(Buttons::Keyboard(KeyCode::KeyS), -1.),
                    AnalogInputs::Button(Buttons::Keyboard(KeyCode::KeyW), 1.),
                ],
            }
        ),
        (
            Name("Test Accumulative X")
            AccumulativeControl<ControlContext, ControlAction> {
                action: ControlAction::TestAccumulativeX,
                contexts: vec![ControlContext::Game],
                mappings: vec![
                    (AccumulativeInputs::GamepadAxis(GamepadAxis::RightStickX), 1.),
                    (AccumulativeInputs::MouseX, 1.),
                ],
            }
        ),
        (
            Name("Test Accumulative Y")
            AccumulativeControl<ControlContext, ControlAction> {
                action: ControlAction::TestAccumulativeY,
                contexts: vec![ControlContext::Game],
                mappings: vec![
                    (AccumulativeInputs::GamepadAxis(GamepadAxis::RightStickY), 1.),
                    (AccumulativeInputs::MouseY, 1.),
                ],
            }
        )
    )
}

fn update_state(
    context: Res<State<ControlContext>>,
    mut next_context: ResMut<NextState<ControlContext>>,
    mut controls: Query<&mut ButtonControl<ControlContext, ControlAction>>,
) {
    if !controls.action_just_pressed(ControlAction::NextContext) {
        return;
    }

    let next_state = match context.get() {
        ControlContext::MainMenu => ControlContext::Game,
        ControlContext::Game => ControlContext::OpenInventory,
        ControlContext::OpenInventory => ControlContext::Paused,
        ControlContext::Paused => ControlContext::MainMenu,
    };

    next_context.set(next_state);
}