bevy_input_bindings 0.0.2

High level, flexible and shareable input binding library for the Bevy game engine
Documentation
use bevy::app::{App, Plugin, Update};
use bevy::ecs::component::{Component, Mutable};
use bevy::ecs::entity::Entity;
use bevy::ecs::query::Added;
use bevy::ecs::schedule::IntoScheduleConfigs;
use bevy::ecs::system::{Commands, Query, Res};
use bevy::input::ButtonInput;
use bevy::input::keyboard::KeyCode;
use bevy::time::Time;

use crate::action_event::ActionEvent;
use crate::controller_interface::ControllerInterface;
use crate::controller_layout::ControllerLayout;
use crate::controller_values::{ControllerValues, VariantValue};
use crate::relation::Controlling;
use crate::shared::ControllersSet;

use super::binding::KeyboardBinding;

pub struct KeyboardPlugin<ActionKey, ValueKey, Binding>
where
    Binding: KeyboardBinding<ActionKey, ValueKey>,
{
    _action_key: std::marker::PhantomData<ActionKey>,
    _value_key: std::marker::PhantomData<ValueKey>,
    _binding_key: std::marker::PhantomData<Binding>,
}

impl<ActionKey, ValueKey, Binding> KeyboardPlugin<ActionKey, ValueKey, Binding>
where
    Binding: KeyboardBinding<ActionKey, ValueKey>,
{
    pub fn new() -> Self {
        Self {
            _action_key: Default::default(),
            _value_key: Default::default(),
            _binding_key: Default::default(),
        }
    }
}

impl<ActionKey, ValueKey, Binding> Plugin for KeyboardPlugin<ActionKey, ValueKey, Binding>
where
    ActionKey: Sync + Send + 'static,
    ValueKey: std::hash::Hash + PartialEq + Eq + Sync + Send + 'static,
    Binding: KeyboardBinding<ActionKey, ValueKey>
        + Sync
        + Send
        + 'static
        + Component<Mutability = Mutable>,
{
    fn build(&self, app: &mut App) {
        app.add_systems(
            Update,
            (
                // Add the system that will attach the controller interface and values to the newly
                // introduced Keyboard binding components
                on_add_binding::<ActionKey, ValueKey, Binding>,
                // Add the system handling the keyboard presses
                keyboard_system::<ActionKey, ValueKey, Binding>,
            )
                .in_set(ControllersSet),
        );
    }
}

fn on_add_binding<
    ActionKey: Sync + Send + 'static,
    ValueKey,
    Binding: KeyboardBinding<ActionKey, ValueKey>,
>(
    mut commands: Commands,
    new_bindings: Query<Entity, Added<Binding>>,
) where
    ActionKey: Sync + Send + 'static,
    ValueKey: std::hash::Hash + PartialEq + Eq + Sync + Send + 'static,
    Binding: KeyboardBinding<ActionKey, ValueKey>,
{
    for new_binding in new_bindings {
        commands.entity(new_binding).insert_if_new((
            ControllerInterface::new(ControllerLayout::Keyboard),
            ControllerValues::<ValueKey>::new(),
        ));
    }
}

/// System to handle keyboard actions and values
fn keyboard_system<
    ActionKey: Sync + Send + 'static,
    ValueKey,
    Binding: KeyboardBinding<ActionKey, ValueKey> + Component<Mutability = Mutable>,
>(
    time: Res<Time>,
    keys: Res<ButtonInput<KeyCode>>,
    mut commands: Commands,
    mut keyboards: Query<(
        Entity,
        &mut Binding,
        &mut ControllerValues<ValueKey>,
        Option<&Controlling>,
    )>,
) where
    ActionKey: Sync + Send + 'static,
    ValueKey: std::hash::Hash + PartialEq + Eq + Sync + Send + 'static,
    Binding: KeyboardBinding<ActionKey, ValueKey>,
{
    for (controller_id, mut binding, mut controller_values, controlling) in keyboards.iter_mut() {
        // Get the controlled entities if required
        let controlled_entities = std::cell::LazyCell::new(|| {
            let Some(controlling) = controlling else {
                return Vec::new();
            };
            return controlling.controlling().clone();
        });
        // Actions
        for (trigger, action_key) in binding.get_action_bindings() {
            if trigger.is_triggered(&keys, &time.delta()) {
                let action_event = ActionEvent {
                    controller: controller_id,
                    controlling: controlled_entities.clone(),
                    action_key,
                };
                commands.trigger(action_event);
            }
        }
        // Values
        for (value_type, value_key) in binding.get_value_bindings() {
            // If the value does not yet exist, create it
            if let Some(value) = controller_values.values.get_mut(&value_key) {
                value_type.update_value(&keys, &time.delta(), value);
            } else {
                let mut new_value = VariantValue::None;
                value_type.update_value(&keys, &time.delta(), &mut new_value);
                controller_values.values.insert(value_key, new_value);
            };
        }
    }
}