bevy_input_bindings 0.0.2

High level, flexible and shareable input binding library for the Bevy game engine
Documentation
use bevy::input::gamepad::{Gamepad, GamepadAxis, GamepadButton};
use bevy::reflect::Reflect;
use std::time::Duration;

use crate::repeat::Repeat;

pub trait TriggerTrait {
    fn is_triggered(&mut self, gamepad: &Gamepad, dt: &Duration) -> bool;
}

/// Triggers the action whenever the defined button has just been pressed
#[derive(Clone, Debug, Reflect)]
pub struct ButtonPressed {
    button: GamepadButton,
    repeat: Option<Repeat>,
}

impl ButtonPressed {
    pub fn new(button: GamepadButton) -> Self {
        Self {
            button,
            repeat: None,
        }
    }

    /// Repeat the trigger as long as the button is pressed, with delays between each actions.
    pub fn with_repeat(
        mut self,
        duration_before_first_repeat: Duration,
        duration_between_repeat: Duration,
    ) -> Self {
        self.repeat = Some(Repeat::new(duration_before_first_repeat, duration_between_repeat));
        self
    }
}

impl TriggerTrait for ButtonPressed {
    fn is_triggered(&mut self, gamepad: &Gamepad, dt: &Duration) -> bool {
        let Some(repeat) = &mut self.repeat else {
            return gamepad.just_pressed(self.button);
        };
        if gamepad.just_pressed(self.button) {
            repeat.reset();
            return true;
        }
        let signal = gamepad.pressed(self.button);
        return repeat.is_triggered(signal, dt);
    }
}

/// Triggers the action whenever the defined button has just been released
#[derive(Clone, Debug, Reflect)]
pub struct ButtonJustReleased {
    button: GamepadButton,
}

impl ButtonJustReleased {
    pub fn new(button: GamepadButton) -> Self {
        Self {
            button,
        }
    }
}

impl TriggerTrait for ButtonJustReleased {
    fn is_triggered(&mut self, gamepad: &Gamepad, _dt: &Duration) -> bool {
        gamepad.just_released(self.button)
    }
}

/// Triggers the action whenever the defined axis goes above (or lower) a custom value.
/// The action can only be triggered again after the value has been lower (or higher) to a second
/// value.
#[derive(Clone, Debug, Reflect)]
pub struct Hysteresis {
    axis: GamepadAxis,
    above: bool,
    lower: f32,
    higher: f32,
    active: bool,
    repeat: Option<Repeat>,
}

impl Hysteresis {
    pub fn new_above(axis: GamepadAxis, lower: f32, higher: f32) -> Self {
        Self {
            axis,
            above: true,
            lower,
            higher,
            active: false,
            repeat: None,
        }
    }

    pub fn new_below(axis: GamepadAxis, lower: f32, higher: f32) -> Self {
        Self {
            axis,
            above: false,
            lower,
            higher,
            active: false,
            repeat: None,
        }
    }

    /// Repeat the trigger as long as the hysteresis is active, with delays between each actions.
    pub fn with_repeat(
        mut self,
        duration_before_first_repeat: Duration,
        duration_between_repeat: Duration,
    ) -> Self {
        self.repeat = Some(Repeat::new(duration_before_first_repeat, duration_between_repeat));
        self
    }
}

impl TriggerTrait for Hysteresis {
    fn is_triggered(&mut self, gamepad: &Gamepad, dt: &Duration) -> bool {
        // Get axis value
        let value = gamepad.get(self.axis.clone()).unwrap_or(0.0);
        let last_active = self.active.clone();
        // Compute signal
        if self.above {
            if self.active && value < self.lower {
                self.active = false;
            } else if !self.active && value > self.higher {
                self.active = true;
            }
        } else {
            if self.active && value > self.lower {
                self.active = false;
            } else if !self.active && value < self.higher {
                self.active = true;
            }
        }
        // Compute repeat
        if let Some(repeat) = &mut self.repeat {
            if last_active != self.active {
                repeat.reset();
                return self.active;
            }
            return repeat.is_triggered(self.active, dt);
        }
        if last_active != self.active {
            return self.active;
        } else {
            return false;
        }
    }
}