diff --git a/src/action_value.rs b/src/action_value.rs index 057619e..c88d2ab 100644 --- a/src/action_value.rs +++ b/src/action_value.rs @@ -44,6 +44,24 @@ impl ActionValue { } } + /// Returns `true` if the value in sufficiently large. + pub fn is_actuated(self, actuation: f32) -> bool { + let value = match self { + ActionValue::Bool(value) => { + if value { + 1.0 + } else { + 0.0 + } + } + ActionValue::Axis1D(value) => value * value, + ActionValue::Axis2D(value) => value.length_squared(), + ActionValue::Axis3D(value) => value.length_squared(), + }; + + value >= actuation * actuation + } + /// Returns the value as a boolean. /// /// If the value is not [`ActionValue::Bool`], diff --git a/src/input_context/input_condition.rs b/src/input_context/input_condition.rs index 4ec5086..3a90fd2 100644 --- a/src/input_context/input_condition.rs +++ b/src/input_context/input_condition.rs @@ -1,10 +1,10 @@ pub mod blocked_by; pub mod chord; pub mod down; +pub mod held_timer; pub mod hold; pub mod hold_and_release; pub mod pressed; -pub mod primitives; pub mod pulse; pub mod released; pub mod tap; @@ -16,6 +16,8 @@ use bevy::prelude::*; use super::input_action::{ActionState, ActionsData}; use crate::action_value::ActionValue; +pub const DEFAULT_ACTUATION: f32 = 0.5; + /// Defines how input activates. /// /// Most conditions analyze the input itself, checking for minimum actuation values diff --git a/src/input_context/input_condition/down.rs b/src/input_context/input_condition/down.rs index c88aedf..9a1adee 100644 --- a/src/input_context/input_condition/down.rs +++ b/src/input_context/input_condition/down.rs @@ -1,25 +1,31 @@ use bevy::prelude::*; -use super::{primitives::Actuation, InputCondition}; +use super::{InputCondition, DEFAULT_ACTUATION}; use crate::{ action_value::ActionValue, input_context::input_action::{ActionState, ActionsData}, }; /// Returns [`ActionState::Fired`] when the input exceeds the actuation threshold. -#[derive(Default, Debug)] +#[derive(Debug)] pub struct Down { /// Trigger threshold. - pub actuation: Actuation, + pub actuation: f32, } impl Down { #[must_use] - pub fn new(actuation: Actuation) -> Self { + pub fn new(actuation: f32) -> Self { Self { actuation } } } +impl Default for Down { + fn default() -> Self { + Self::new(DEFAULT_ACTUATION) + } +} + impl InputCondition for Down { fn evaluate( &mut self, @@ -28,7 +34,7 @@ impl InputCondition for Down { _delta: f32, value: ActionValue, ) -> ActionState { - if self.actuation.is_actuated(value) { + if value.is_actuated(self.actuation) { ActionState::Fired } else { ActionState::None diff --git a/src/input_context/input_condition/held_timer.rs b/src/input_context/input_condition/held_timer.rs new file mode 100644 index 0000000..da29d8e --- /dev/null +++ b/src/input_context/input_condition/held_timer.rs @@ -0,0 +1,38 @@ +use bevy::prelude::*; + +/// Helper for building triggers that have firing conditions governed by elapsed time. +#[derive(Default, Debug)] +pub struct HeldTimer { + /// If set to `true`, [`Time::relative_speed`] will be applied to the held duration. + /// + /// By default is set to `false`. + pub relative_to_speed: bool, + + duration: f32, +} + +impl HeldTimer { + pub fn relative_to_speed(relative_to_speed: bool) -> Self { + Self { + relative_to_speed, + duration: 0.0, + } + } + + pub fn update(&mut self, world: &World, mut delta: f32) { + if self.relative_to_speed { + let time = world.resource::>(); + delta *= time.relative_speed() + } + + self.duration += delta; + } + + pub fn reset(&mut self) { + self.duration = 0.0; + } + + pub fn duration(&self) -> f32 { + self.duration + } +} diff --git a/src/input_context/input_condition/hold.rs b/src/input_context/input_condition/hold.rs index 806ba88..712f999 100644 --- a/src/input_context/input_condition/hold.rs +++ b/src/input_context/input_condition/hold.rs @@ -1,9 +1,6 @@ use bevy::prelude::*; -use super::{ - primitives::{Actuation, HeldTimer}, - InputCondition, -}; +use super::{held_timer::HeldTimer, InputCondition, DEFAULT_ACTUATION}; use crate::{ action_value::ActionValue, input_context::input_action::{ActionState, ActionsData}, @@ -23,7 +20,7 @@ pub struct Hold { pub one_shot: bool, /// Trigger threshold. - pub actuation: Actuation, + pub actuation: f32, held_timer: HeldTimer, @@ -36,7 +33,7 @@ impl Hold { Self { hold_time, one_shot: false, - actuation: Default::default(), + actuation: DEFAULT_ACTUATION, held_timer: Default::default(), fired: false, } @@ -49,8 +46,8 @@ impl Hold { } #[must_use] - pub fn with_actuation(mut self, actuation: impl Into) -> Self { - self.actuation = actuation.into(); + pub fn with_actuation(mut self, actuation: f32) -> Self { + self.actuation = actuation; self } @@ -69,7 +66,7 @@ impl InputCondition for Hold { delta: f32, value: ActionValue, ) -> ActionState { - let actuated = self.actuation.is_actuated(value); + let actuated = value.is_actuated(self.actuation); if actuated { self.held_timer.update(world, delta); } else { diff --git a/src/input_context/input_condition/hold_and_release.rs b/src/input_context/input_condition/hold_and_release.rs index ce6ece8..b2af676 100644 --- a/src/input_context/input_condition/hold_and_release.rs +++ b/src/input_context/input_condition/hold_and_release.rs @@ -1,9 +1,6 @@ use bevy::prelude::*; -use super::{ - primitives::{Actuation, HeldTimer}, - InputCondition, -}; +use super::{held_timer::HeldTimer, InputCondition, DEFAULT_ACTUATION}; use crate::{ action_value::ActionValue, input_context::input_action::{ActionState, ActionsData}, @@ -19,7 +16,7 @@ pub struct HoldAndRelease { pub hold_time: f32, /// Trigger threshold. - pub actuation: Actuation, + pub actuation: f32, held_timer: HeldTimer, } @@ -29,14 +26,14 @@ impl HoldAndRelease { pub fn new(hold_time: f32) -> Self { Self { hold_time, - actuation: Default::default(), + actuation: DEFAULT_ACTUATION, held_timer: Default::default(), } } #[must_use] - pub fn with_actuation(mut self, actuation: impl Into) -> Self { - self.actuation = actuation.into(); + pub fn with_actuation(mut self, actuation: f32) -> Self { + self.actuation = actuation; self } @@ -61,7 +58,7 @@ impl InputCondition for HoldAndRelease { self.held_timer.update(world, delta); let held_duration = self.held_timer.duration(); - if self.actuation.is_actuated(value) { + if value.is_actuated(self.actuation) { ActionState::Ongoing } else { self.held_timer.reset(); diff --git a/src/input_context/input_condition/pressed.rs b/src/input_context/input_condition/pressed.rs index ad0750e..1766f32 100644 --- a/src/input_context/input_condition/pressed.rs +++ b/src/input_context/input_condition/pressed.rs @@ -1,6 +1,6 @@ use bevy::prelude::*; -use super::{primitives::Actuation, InputCondition}; +use super::{InputCondition, DEFAULT_ACTUATION}; use crate::{ action_value::ActionValue, input_context::input_action::{ActionState, ActionsData}, @@ -9,16 +9,16 @@ use crate::{ /// Like [`super::down::Down`] but returns [`ActionState::Fired`] only once until the next actuation. /// /// Holding the input will not cause further triggers. -#[derive(Default, Debug)] +#[derive(Debug)] pub struct Pressed { /// Trigger threshold. - pub actuation: Actuation, + pub actuation: f32, actuated: bool, } impl Pressed { #[must_use] - pub fn new(actuation: Actuation) -> Self { + pub fn new(actuation: f32) -> Self { Self { actuation, actuated: false, @@ -26,6 +26,12 @@ impl Pressed { } } +impl Default for Pressed { + fn default() -> Self { + Self::new(DEFAULT_ACTUATION) + } +} + impl InputCondition for Pressed { fn evaluate( &mut self, @@ -35,7 +41,7 @@ impl InputCondition for Pressed { value: ActionValue, ) -> ActionState { let previosly_actuated = self.actuated; - self.actuated = self.actuation.is_actuated(value); + self.actuated = value.is_actuated(self.actuation); if self.actuated && !previosly_actuated { ActionState::Fired diff --git a/src/input_context/input_condition/primitives.rs b/src/input_context/input_condition/primitives.rs deleted file mode 100644 index e98bf77..0000000 --- a/src/input_context/input_condition/primitives.rs +++ /dev/null @@ -1,76 +0,0 @@ -use bevy::prelude::*; - -use crate::action_value::ActionValue; - -/// Helper for building triggers that have firing conditions governed by elapsed time. -#[derive(Default, Debug)] -pub struct HeldTimer { - /// If set to `true`, [`Time::relative_speed`] will be applied to the held duration. - /// - /// By default is set to `false`. - pub relative_to_speed: bool, - - duration: f32, -} - -impl HeldTimer { - pub fn relative_to_speed(relative_to_speed: bool) -> Self { - Self { - relative_to_speed, - duration: 0.0, - } - } - - pub fn update(&mut self, world: &World, mut delta: f32) { - if self.relative_to_speed { - let time = world.resource::>(); - delta *= time.relative_speed() - } - - self.duration += delta; - } - - pub fn reset(&mut self) { - self.duration = 0.0; - } - - pub fn duration(&self) -> f32 { - self.duration - } -} - -/// Value at which a button considered actuated. -#[derive(Clone, Copy, Debug)] -pub struct Actuation(pub f32); - -impl Actuation { - /// Returns `true` if the value in sufficiently large. - pub fn is_actuated(self, value: ActionValue) -> bool { - let value = match value { - ActionValue::Bool(value) => { - if value { - 1.0 - } else { - 0.0 - } - } - ActionValue::Axis1D(value) => value * value, - ActionValue::Axis2D(value) => value.length_squared(), - ActionValue::Axis3D(value) => value.length_squared(), - }; - - value >= self.0 * self.0 - } -} - -impl Default for Actuation { - fn default() -> Self { - Self(0.5) - } -} - -impl From for Actuation { - fn from(value: f32) -> Self { - Self(value) - } -} diff --git a/src/input_context/input_condition/pulse.rs b/src/input_context/input_condition/pulse.rs index 2efc740..6eafa4d 100644 --- a/src/input_context/input_condition/pulse.rs +++ b/src/input_context/input_condition/pulse.rs @@ -1,9 +1,6 @@ use bevy::prelude::*; -use super::{ - primitives::{Actuation, HeldTimer}, - InputCondition, -}; +use super::{held_timer::HeldTimer, InputCondition, DEFAULT_ACTUATION}; use crate::{ action_value::ActionValue, input_context::input_action::{ActionState, ActionsData}, @@ -27,7 +24,7 @@ pub struct Pulse { pub trigger_on_start: bool, /// Trigger threshold. - pub actuation: Actuation, + pub actuation: f32, held_timer: HeldTimer, @@ -42,7 +39,7 @@ impl Pulse { trigger_limit: 0, trigger_on_start: true, trigger_count: 0, - actuation: Default::default(), + actuation: DEFAULT_ACTUATION, held_timer: Default::default(), } } @@ -60,8 +57,8 @@ impl Pulse { } #[must_use] - pub fn with_actuation(mut self, actuation: impl Into) -> Self { - self.actuation = actuation.into(); + pub fn with_actuation(mut self, actuation: f32) -> Self { + self.actuation = actuation; self } @@ -80,7 +77,7 @@ impl InputCondition for Pulse { delta: f32, value: ActionValue, ) -> ActionState { - if self.actuation.is_actuated(value) { + if value.is_actuated(self.actuation) { self.held_timer.update(world, delta); if self.trigger_limit == 0 || self.trigger_count < self.trigger_limit { diff --git a/src/input_context/input_condition/released.rs b/src/input_context/input_condition/released.rs index b29ab95..feaf07b 100644 --- a/src/input_context/input_condition/released.rs +++ b/src/input_context/input_condition/released.rs @@ -1,6 +1,6 @@ use bevy::prelude::*; -use super::{primitives::Actuation, InputCondition}; +use super::{InputCondition, DEFAULT_ACTUATION}; use crate::{ action_value::ActionValue, input_context::input_action::{ActionState, ActionsData}, @@ -8,16 +8,16 @@ use crate::{ /// Returns [`ActionState::Ongoing`]` when the input exceeds the actuation threshold and /// [`ActionState::Fired`] once when the input drops back below the actuation threshold. -#[derive(Default, Debug)] +#[derive(Debug)] pub struct Released { /// Trigger threshold. - pub actuation: Actuation, + pub actuation: f32, actuated: bool, } impl Released { #[must_use] - pub fn new(actuation: Actuation) -> Self { + pub fn new(actuation: f32) -> Self { Self { actuation, actuated: false, @@ -25,6 +25,12 @@ impl Released { } } +impl Default for Released { + fn default() -> Self { + Self::new(DEFAULT_ACTUATION) + } +} + impl InputCondition for Released { fn evaluate( &mut self, @@ -34,7 +40,7 @@ impl InputCondition for Released { value: ActionValue, ) -> ActionState { let previosly_actuated = self.actuated; - self.actuated = self.actuation.is_actuated(value); + self.actuated = value.is_actuated(self.actuation); if self.actuated { // Ongoing on hold. diff --git a/src/input_context/input_condition/tap.rs b/src/input_context/input_condition/tap.rs index d441c2b..f788725 100644 --- a/src/input_context/input_condition/tap.rs +++ b/src/input_context/input_condition/tap.rs @@ -1,9 +1,6 @@ use bevy::prelude::*; -use super::{ - primitives::{Actuation, HeldTimer}, - InputCondition, -}; +use super::{held_timer::HeldTimer, InputCondition, DEFAULT_ACTUATION}; use crate::{ action_value::ActionValue, input_context::input_action::{ActionState, ActionsData}, @@ -19,7 +16,7 @@ pub struct Tap { pub release_time: f32, /// Trigger threshold. - pub actuation: Actuation, + pub actuation: f32, held_timer: HeldTimer, actuated: bool, @@ -30,15 +27,15 @@ impl Tap { pub fn new(release_time: f32) -> Self { Self { release_time, - actuation: Default::default(), + actuation: DEFAULT_ACTUATION, held_timer: Default::default(), actuated: false, } } #[must_use] - pub fn with_actuation(mut self, actuation: impl Into) -> Self { - self.actuation = actuation.into(); + pub fn with_actuation(mut self, actuation: f32) -> Self { + self.actuation = actuation; self } @@ -59,7 +56,7 @@ impl InputCondition for Tap { ) -> ActionState { let last_actuated = self.actuated; let last_held_duration = self.held_timer.duration(); - self.actuated = self.actuation.is_actuated(value); + self.actuated = value.is_actuated(self.actuation); if self.actuated { self.held_timer.update(world, delta); } else { diff --git a/src/lib.rs b/src/lib.rs index 6daab94..b5b2dc1 100644 --- a/src/lib.rs +++ b/src/lib.rs @@ -63,8 +63,8 @@ pub mod prelude { context_instance::{ActionMap, ContextInstance, GamepadStick, InputMap}, input_action::{Accumulation, ActionEvent, ActionEventKind, InputAction}, input_condition::{ - blocked_by::*, chord::*, down::*, hold::*, hold_and_release::*, pressed::*, - primitives::*, pulse::*, released::*, tap::*, ConditionKind, InputCondition, + blocked_by::*, chord::*, down::*, held_timer::*, hold::*, hold_and_release::*, + pressed::*, pulse::*, released::*, tap::*, ConditionKind, InputCondition, }, input_modifier::{ dead_zone::*, exponential_curve::*, negate::*, scalar::*, scale_by_delta::*,