use bevy::prelude::*;
use crate::prelude::*;
#[derive(Component, Reflect, Debug, Clone, Copy)]
pub struct DeadZone {
pub kind: DeadZoneKind,
pub lower_threshold: f32,
pub upper_threshold: f32,
}
impl DeadZone {
#[must_use]
pub const fn new(kind: DeadZoneKind) -> Self {
Self {
kind,
lower_threshold: 0.2,
upper_threshold: 1.0,
}
}
fn dead_zone(self, axis_value: f32) -> f32 {
let lower_bound = (axis_value.abs() - self.lower_threshold).max(0.0);
let scaled_value = lower_bound / (self.upper_threshold - self.lower_threshold);
scaled_value.min(1.0) * axis_value.signum()
}
}
impl Default for DeadZone {
fn default() -> Self {
Self::new(Default::default())
}
}
impl InputModifier for DeadZone {
fn transform(
&mut self,
_actions: &ActionsQuery,
_time: &ContextTime,
value: ActionValue,
) -> ActionValue {
match value {
ActionValue::Bool(value) => {
let value = if value { 1.0 } else { 0.0 };
self.dead_zone(value).into()
}
ActionValue::Axis1D(value) => self.dead_zone(value).into(),
ActionValue::Axis2D(mut value) => match self.kind {
DeadZoneKind::Radial => {
(value.normalize_or_zero() * self.dead_zone(value.length())).into()
}
DeadZoneKind::Axial => {
value.x = self.dead_zone(value.x);
value.y = self.dead_zone(value.y);
value.into()
}
},
ActionValue::Axis3D(mut value) => match self.kind {
DeadZoneKind::Radial => {
(value.normalize_or_zero() * self.dead_zone(value.length())).into()
}
DeadZoneKind::Axial => {
value.x = self.dead_zone(value.x);
value.y = self.dead_zone(value.y);
value.z = self.dead_zone(value.z);
value.into()
}
},
}
}
}
#[derive(Reflect, Default, Debug, Clone, Copy)]
pub enum DeadZoneKind {
#[default]
Radial,
Axial,
}
#[cfg(test)]
mod tests {
use bevy::prelude::*;
use super::*;
use crate::context;
#[test]
fn radial() {
let (world, mut state) = context::init_world();
let (time, actions) = state.get(&world);
let mut modifier = DeadZone::new(DeadZoneKind::Radial);
assert_eq!(modifier.transform(&actions, &time, true.into()), 1.0.into());
assert_eq!(
modifier.transform(&actions, &time, false.into()),
0.0.into()
);
assert_eq!(modifier.transform(&actions, &time, 1.0.into()), 1.0.into());
assert_eq!(
modifier.transform(&actions, &time, 0.5.into()),
0.375.into()
);
assert_eq!(modifier.transform(&actions, &time, 0.2.into()), 0.0.into());
assert_eq!(modifier.transform(&actions, &time, 2.0.into()), 1.0.into());
assert_eq!(
modifier.transform(&actions, &time, (Vec2::ONE * 0.5).into()),
(Vec2::ONE * 0.4482233).into()
);
assert_eq!(
modifier.transform(&actions, &time, Vec2::ONE.into()),
(Vec2::ONE * 0.70710677).into()
);
assert_eq!(
modifier.transform(&actions, &time, (Vec2::ONE * 0.2).into()),
(Vec2::ONE * 0.07322331).into()
);
assert_eq!(
modifier.transform(&actions, &time, (Vec3::ONE * 0.5).into()),
(Vec3::ONE * 0.48066244).into()
);
assert_eq!(
modifier.transform(&actions, &time, Vec3::ONE.into()),
(Vec3::ONE * 0.57735026).into()
);
assert_eq!(
modifier.transform(&actions, &time, (Vec3::ONE * 0.2).into()),
(Vec3::ONE * 0.105662435).into()
);
}
#[test]
fn axial() {
let (world, mut state) = context::init_world();
let (time, actions) = state.get(&world);
let mut modifier = DeadZone::new(DeadZoneKind::Axial);
assert_eq!(modifier.transform(&actions, &time, true.into()), 1.0.into());
assert_eq!(
modifier.transform(&actions, &time, false.into()),
0.0.into()
);
assert_eq!(modifier.transform(&actions, &time, 1.0.into()), 1.0.into());
assert_eq!(
modifier.transform(&actions, &time, 0.5.into()),
0.375.into()
);
assert_eq!(modifier.transform(&actions, &time, 0.2.into()), 0.0.into());
assert_eq!(modifier.transform(&actions, &time, 2.0.into()), 1.0.into());
assert_eq!(
modifier.transform(&actions, &time, (Vec2::ONE * 0.5).into()),
(Vec2::ONE * 0.375).into()
);
assert_eq!(
modifier.transform(&actions, &time, Vec2::ONE.into()),
Vec2::ONE.into()
);
assert_eq!(
modifier.transform(&actions, &time, (Vec2::ONE * 0.2).into()),
Vec2::ZERO.into()
);
assert_eq!(
modifier.transform(&actions, &time, (Vec3::ONE * 0.5).into()),
(Vec3::ONE * 0.375).into()
);
assert_eq!(
modifier.transform(&actions, &time, Vec3::ONE.into()),
Vec3::ONE.into()
);
assert_eq!(
modifier.transform(&actions, &time, (Vec3::ONE * 0.2).into()),
Vec3::ZERO.into()
);
}
}