bevy_controls 1.0.0

Bevy controls library
Documentation
use bevy::{ecs::entity_disabling::Disabled, prelude::*, state::state::FreelyMutableState};
use std::marker::PhantomData;

use crate::{
    analog::{analog_control::AnalogControl, analog_plugin::AnalogPlugin},
    button::{button_control::ButtonControl, button_plugin::ButtonPlugin},
};

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

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

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

impl<TContext: States, TAction: Sync + Send + 'static> ControlsPlugin<TContext, TAction> {
    fn context_change(
        mut messages: MessageReader<StateTransitionEvent<TContext>>,
        mut commands: Commands,
        button_controls: Query<(Entity, &ButtonControl<TContext, TAction>, Has<Disabled>)>,
        analog_controls: Query<(Entity, &AnalogControl<TContext, TAction>, Has<Disabled>)>,
    ) {
        for message in messages.read() {
            if message.entered == message.exited {
                continue;
            }

            let controls = button_controls
                .iter()
                .map(|(e, b, d)| (e, &b.contexts, d))
                .chain(analog_controls.iter().map(|(e, a, d)| (e, &a.contexts, d)))
                .map(|(e, c, d)| (e, c.iter().map(|c| Some(c.clone())).collect::<Vec<_>>(), d));

            for (entity, contexts, has_disabled) in controls {
                if has_disabled && contexts.contains(&message.entered) {
                    commands.entity(entity).remove::<Disabled>();
                }
                if !has_disabled && !contexts.contains(&message.entered) {
                    commands.entity(entity).insert(Disabled);
                }
            }

            info!(
                "Switched control contex from {:?} to {:?}",
                message.exited, message.entered
            );
        }
    }
}