bevy_controls 1.1.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, PartialEq, Eq, Reflect)]
enum ControlAction {
    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, setup)
        .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;

    info!("Test Accumulative: {}", *test_accumulative);
}

fn setup(mut commands: Commands) {
    commands.spawn(Camera2d);

    commands.spawn((
        Name::new("Next Context Control"),
        ButtonControl::new(
            ControlAction::NextContext,
            vec![
                ControlContext::MainMenu,
                ControlContext::Game,
                ControlContext::Paused,
                ControlContext::OpenInventory,
            ],
            vec![Buttons::Keyboard(KeyCode::Space)],
        ),
    ));

    commands.spawn((
        Name::new("Test1 Control"),
        ButtonControl::new(
            ControlAction::Test1,
            vec![ControlContext::MainMenu],
            vec![
                Buttons::Keyboard(KeyCode::Enter),
                Buttons::Mouse(MouseButton::Left),
                Buttons::Gamepad(GamepadButton::North),
            ],
        ),
    ));

    commands.spawn((
        Name::new("TestAnalog1 Control"),
        AnalogControl::new(
            ControlAction::TestAnalog1,
            vec![ControlContext::Paused],
            vec![
                AnalogInputs::Gamepad(GamepadButton::RightTrigger2),
                AnalogInputs::Button(Buttons::Gamepad(GamepadButton::North), 1.),
                AnalogInputs::Button(Buttons::Gamepad(GamepadButton::South), -1.),
            ],
        ),
    ));

    commands.spawn((
        Name::new("Test Movement X"),
        AnalogControl::new(
            ControlAction::TestMovementX,
            vec![ControlContext::Game, ControlContext::OpenInventory],
            vec![
                AnalogInputs::GamepadAxis(GamepadAxis::LeftStickX),
                AnalogInputs::Button(Buttons::Keyboard(KeyCode::KeyA), -1.),
                AnalogInputs::Button(Buttons::Keyboard(KeyCode::KeyD), 1.),
            ],
        ),
    ));

    commands.spawn((
        Name::new("Test Movement Y"),
        AnalogControl::new(
            ControlAction::TestMovementY,
            vec![ControlContext::Game, ControlContext::OpenInventory],
            vec![
                AnalogInputs::GamepadAxis(GamepadAxis::LeftStickY),
                AnalogInputs::Button(Buttons::Keyboard(KeyCode::KeyS), -1.),
                AnalogInputs::Button(Buttons::Keyboard(KeyCode::KeyW), 1.),
            ],
        ),
    ));

    commands.spawn((
        Name::new("Test Accumulative X"),
        AccumulativeControl::new(
            ControlAction::TestAccumulativeX,
            vec![ControlContext::Game],
            vec![
                AccumulativeInputs::GamepadAxis(GamepadAxis::RightStickX),
                AccumulativeInputs::MouseX,
            ],
        ),
    ));

    commands.spawn((
        Name::new("Test Accumulative Y"),
        AccumulativeControl::new(
            ControlAction::TestAccumulativeY,
            vec![ControlContext::Game],
            vec![
                AccumulativeInputs::GamepadAxis(GamepadAxis::RightStickY),
                AccumulativeInputs::MouseY,
            ],
        ),
    ));
}

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);
}