use crate::input_map::UpdatedValue;
use crate::{action_diff::ActionDiff, input_map::UpdatedActions};
use crate::{Actionlike, InputControlKind};
use bevy::prelude::Resource;
use bevy::reflect::Reflect;
#[cfg(feature = "timing")]
use bevy::utils::Duration;
use bevy::utils::{HashMap, Instant};
use bevy::{ecs::component::Component, prelude::ReflectComponent};
use bevy::{
math::{Vec2, Vec3},
prelude::ReflectResource,
};
use serde::{Deserialize, Serialize};
mod action_data;
pub use action_data::*;
#[derive(Resource, Component, Clone, Debug, PartialEq, Serialize, Deserialize, Reflect)]
#[reflect(Resource, Component)]
pub struct ActionState<A: Actionlike> {
disabled: bool,
action_data: HashMap<A, ActionData>,
}
impl<A: Actionlike> Default for ActionState<A> {
fn default() -> Self {
Self {
disabled: false,
action_data: HashMap::default(),
}
}
}
impl<A: Actionlike> ActionState<A> {
#[inline]
#[must_use]
pub fn all_action_data(&self) -> &HashMap<A, ActionData> {
&self.action_data
}
pub(crate) fn swap_to_update_state(&mut self) {
for action_datum in self.action_data.values_mut() {
action_datum.kind_data.swap_to_update_state();
}
}
pub(crate) fn swap_to_fixed_update_state(&mut self) {
for action_datum in self.action_data.values_mut() {
action_datum.kind_data.swap_to_fixed_update_state();
}
}
pub fn update(&mut self, updated_actions: UpdatedActions<A>) {
for (action, updated_value) in updated_actions.iter() {
match updated_value {
UpdatedValue::Button(pressed) => {
if *pressed {
self.press(action);
} else {
self.release(action);
}
}
UpdatedValue::Axis(value) => {
self.set_value(action, *value);
}
UpdatedValue::DualAxis(pair) => {
self.set_axis_pair(action, *pair);
}
UpdatedValue::TripleAxis(triple) => {
self.set_axis_triple(action, *triple);
}
}
}
}
pub fn tick(&mut self, _current_instant: Instant, _previous_instant: Instant) {
self.action_data
.values_mut()
.for_each(|action_datum| action_datum.tick(_current_instant, _previous_instant));
}
#[inline]
#[must_use]
pub fn action_data(&self, action: &A) -> Option<&ActionData> {
self.action_data.get(action)
}
#[inline]
#[must_use]
pub fn action_data_mut(&mut self, action: &A) -> Option<&mut ActionData> {
self.action_data.get_mut(action)
}
pub fn action_data_mut_or_default(&mut self, action: &A) -> &mut ActionData {
if self.action_data.contains_key(action) {
self.action_data.get_mut(action).unwrap()
} else {
self.action_data.insert(
action.clone(),
ActionData::from_kind(action.input_control_kind()),
);
self.action_data_mut(action).unwrap()
}
}
#[inline]
#[must_use]
pub fn button_data(&self, action: &A) -> Option<&ButtonData> {
match self.action_data(action) {
Some(action_data) => match action_data.kind_data {
ActionKindData::Button(ref button_data) => Some(button_data),
_ => None,
},
None => None,
}
}
#[inline]
#[must_use]
pub fn button_data_mut(&mut self, action: &A) -> Option<&mut ButtonData> {
match self.action_data_mut(action) {
Some(action_data) => match &mut action_data.kind_data {
ActionKindData::Button(ref mut button_data) => Some(button_data),
_ => None,
},
None => None,
}
}
#[inline]
#[must_use]
#[track_caller]
pub fn button_data_mut_or_default(&mut self, action: &A) -> &mut ButtonData {
debug_assert_eq!(action.input_control_kind(), InputControlKind::Button);
let action_data = self.action_data_mut_or_default(action);
let ActionKindData::Button(ref mut button_data) = action_data.kind_data else {
panic!("{action:?} is not a Button");
};
button_data
}
#[inline]
#[must_use]
#[track_caller]
pub fn axis_data(&self, action: &A) -> Option<&AxisData> {
debug_assert_eq!(action.input_control_kind(), InputControlKind::Axis);
match self.action_data(action) {
Some(action_data) => match action_data.kind_data {
ActionKindData::Axis(ref axis_data) => Some(axis_data),
_ => None,
},
None => None,
}
}
#[inline]
#[must_use]
pub fn axis_data_mut(&mut self, action: &A) -> Option<&mut AxisData> {
match self.action_data_mut(action) {
Some(action_data) => match &mut action_data.kind_data {
ActionKindData::Axis(ref mut axis_data) => Some(axis_data),
_ => None,
},
None => None,
}
}
#[inline]
#[must_use]
#[track_caller]
pub fn axis_data_mut_or_default(&mut self, action: &A) -> &mut AxisData {
debug_assert_eq!(action.input_control_kind(), InputControlKind::Axis);
let action_data = self.action_data_mut_or_default(action);
let ActionKindData::Axis(ref mut axis_data) = action_data.kind_data else {
panic!("{action:?} is not an Axis");
};
axis_data
}
#[inline]
#[must_use]
#[track_caller]
pub fn dual_axis_data(&self, action: &A) -> Option<&DualAxisData> {
debug_assert_eq!(action.input_control_kind(), InputControlKind::DualAxis);
match self.action_data(action) {
Some(action_data) => match action_data.kind_data {
ActionKindData::DualAxis(ref dual_axis_data) => Some(dual_axis_data),
_ => None,
},
None => None,
}
}
#[inline]
#[must_use]
#[track_caller]
pub fn dual_axis_data_mut(&mut self, action: &A) -> Option<&mut DualAxisData> {
debug_assert_eq!(action.input_control_kind(), InputControlKind::DualAxis);
match self.action_data_mut(action) {
Some(action_data) => match &mut action_data.kind_data {
ActionKindData::DualAxis(ref mut dual_axis_data) => Some(dual_axis_data),
_ => None,
},
None => None,
}
}
#[inline]
#[must_use]
#[track_caller]
pub fn dual_axis_data_mut_or_default(&mut self, action: &A) -> &mut DualAxisData {
debug_assert_eq!(action.input_control_kind(), InputControlKind::DualAxis);
let action_data = self.action_data_mut_or_default(action);
let ActionKindData::DualAxis(ref mut dual_axis_data) = action_data.kind_data else {
panic!("{action:?} is not a DualAxis");
};
dual_axis_data
}
#[inline]
#[must_use]
#[track_caller]
pub fn triple_axis_data(&self, action: &A) -> Option<&TripleAxisData> {
debug_assert_eq!(action.input_control_kind(), InputControlKind::TripleAxis);
match self.action_data(action) {
Some(action_data) => match action_data.kind_data {
ActionKindData::TripleAxis(ref triple_axis_data) => Some(triple_axis_data),
_ => None,
},
None => None,
}
}
#[inline]
#[must_use]
#[track_caller]
pub fn triple_axis_data_mut(&mut self, action: &A) -> Option<&mut TripleAxisData> {
debug_assert_eq!(action.input_control_kind(), InputControlKind::TripleAxis);
match self.action_data_mut(action) {
Some(action_data) => match &mut action_data.kind_data {
ActionKindData::TripleAxis(ref mut triple_axis_data) => Some(triple_axis_data),
_ => None,
},
None => None,
}
}
#[inline]
#[must_use]
#[track_caller]
pub fn triple_axis_data_mut_or_default(&mut self, action: &A) -> &mut TripleAxisData {
debug_assert_eq!(action.input_control_kind(), InputControlKind::TripleAxis);
let action_data = self.action_data_mut_or_default(action);
let ActionKindData::TripleAxis(ref mut triple_axis_data) = action_data.kind_data else {
panic!("{action:?} is not a TripleAxis");
};
triple_axis_data
}
#[inline]
#[must_use]
#[track_caller]
pub fn value(&self, action: &A) -> f32 {
debug_assert_eq!(action.input_control_kind(), InputControlKind::Axis);
if self.action_disabled(action) {
return 0.0;
}
match self.axis_data(action) {
Some(axis_data) => axis_data.value,
None => 0.0,
}
}
#[track_caller]
pub fn set_value(&mut self, action: &A, value: f32) {
debug_assert_eq!(action.input_control_kind(), InputControlKind::Axis);
let axis_data = self.axis_data_mut_or_default(action);
axis_data.value = value;
}
pub fn clamped_value(&self, action: &A) -> f32 {
self.value(action).clamp(-1., 1.)
}
#[must_use]
#[track_caller]
pub fn axis_pair(&self, action: &A) -> Vec2 {
debug_assert_eq!(action.input_control_kind(), InputControlKind::DualAxis);
if self.action_disabled(action) {
return Vec2::ZERO;
}
let action_data = self.dual_axis_data(action);
action_data.map_or(Vec2::ZERO, |action_data| action_data.pair)
}
#[track_caller]
pub fn set_axis_pair(&mut self, action: &A, pair: Vec2) {
debug_assert_eq!(action.input_control_kind(), InputControlKind::DualAxis);
let dual_axis_data = self.dual_axis_data_mut_or_default(action);
dual_axis_data.pair = pair;
}
pub fn clamped_axis_pair(&self, action: &A) -> Vec2 {
let pair = self.axis_pair(action);
pair.clamp(Vec2::NEG_ONE, Vec2::ONE)
}
#[must_use]
#[track_caller]
pub fn axis_triple(&self, action: &A) -> Vec3 {
debug_assert_eq!(action.input_control_kind(), InputControlKind::TripleAxis);
if self.action_disabled(action) {
return Vec3::ZERO;
}
let action_data = self.triple_axis_data(action);
action_data.map_or(Vec3::ZERO, |action_data| action_data.triple)
}
#[track_caller]
pub fn set_axis_triple(&mut self, action: &A, triple: Vec3) {
debug_assert_eq!(action.input_control_kind(), InputControlKind::TripleAxis);
let triple_axis_data = self.triple_axis_data_mut_or_default(action);
triple_axis_data.triple = triple;
}
pub fn clamped_axis_triple(&self, action: &A) -> Vec3 {
let triple = self.axis_triple(action);
triple.clamp(Vec3::NEG_ONE, Vec3::ONE)
}
#[inline]
#[track_caller]
pub fn set_button_data(&mut self, action: A, data: ButtonData) {
debug_assert_eq!(action.input_control_kind(), InputControlKind::Button);
let button_data = self.button_data_mut_or_default(&action);
*button_data = data;
}
#[inline]
#[track_caller]
pub fn press(&mut self, action: &A) {
debug_assert_eq!(action.input_control_kind(), InputControlKind::Button);
let action_data = self.button_data_mut_or_default(action);
#[cfg(feature = "timing")]
if action_data.state.released() {
action_data.timing.flip();
}
action_data.state.press();
}
#[inline]
pub fn release(&mut self, action: &A) {
debug_assert_eq!(action.input_control_kind(), InputControlKind::Button);
let action_data = self.button_data_mut_or_default(action);
#[cfg(feature = "timing")]
if action_data.state.pressed() {
action_data.timing.flip();
}
action_data.state.release();
}
pub fn reset(&mut self, action: &A) {
match action.input_control_kind() {
InputControlKind::Button => self.release(action),
InputControlKind::Axis => {
self.set_value(action, 0.0);
}
InputControlKind::DualAxis => {
self.set_axis_pair(action, Vec2::ZERO);
}
InputControlKind::TripleAxis => {
self.set_axis_triple(action, Vec3::ZERO);
}
}
}
pub fn reset_all(&mut self) {
let all_actions = self.action_data.keys().cloned().collect::<Vec<A>>();
for action in all_actions.into_iter() {
self.reset(&action);
}
}
pub fn disabled(&self) -> bool {
self.disabled
}
#[inline]
#[must_use]
pub fn action_disabled(&self, action: &A) -> bool {
if self.disabled {
return true;
}
match self.action_data(action) {
Some(action_data) => action_data.disabled,
None => false,
}
}
#[inline]
pub fn disable(&mut self) {
self.disabled = true;
self.reset_all();
}
#[inline]
pub fn disable_action(&mut self, action: &A) {
let action_data = self.action_data_mut_or_default(action);
action_data.disabled = true;
self.reset(action);
}
#[inline]
pub fn disable_all_actions(&mut self) {
for action in self.keys() {
self.disable_action(&action);
}
}
#[inline]
pub fn enable(&mut self) {
self.disabled = false;
}
#[inline]
pub fn enable_action(&mut self, action: &A) {
let action_data = self.action_data_mut_or_default(action);
action_data.disabled = false;
}
#[inline]
pub fn enable_all_actions(&mut self) {
for action in self.keys() {
self.enable_action(&action);
}
}
#[inline]
#[must_use]
#[track_caller]
pub fn pressed(&self, action: &A) -> bool {
debug_assert_eq!(action.input_control_kind(), InputControlKind::Button);
if self.action_disabled(action) {
return false;
}
match self.button_data(action) {
Some(button_data) => button_data.pressed(),
None => false,
}
}
#[inline]
#[must_use]
#[track_caller]
pub fn just_pressed(&self, action: &A) -> bool {
debug_assert_eq!(action.input_control_kind(), InputControlKind::Button);
if self.action_disabled(action) {
return false;
}
match self.button_data(action) {
Some(button_data) => button_data.just_pressed(),
None => false,
}
}
#[inline]
#[must_use]
#[track_caller]
pub fn released(&self, action: &A) -> bool {
debug_assert_eq!(action.input_control_kind(), InputControlKind::Button);
if self.action_disabled(action) {
return true;
}
match self.button_data(action) {
Some(button_data) => button_data.released(),
None => true,
}
}
#[inline]
#[must_use]
#[track_caller]
pub fn just_released(&self, action: &A) -> bool {
debug_assert_eq!(action.input_control_kind(), InputControlKind::Button);
if self.action_disabled(action) {
return false;
}
match self.button_data(action) {
Some(button_data) => button_data.just_released(),
None => false,
}
}
#[must_use]
pub fn get_pressed(&self) -> Vec<A> {
let all_actions = self.action_data.keys().cloned();
all_actions
.into_iter()
.filter(|action| action.input_control_kind() == InputControlKind::Button)
.filter(|action| self.pressed(action))
.collect()
}
#[must_use]
pub fn get_just_pressed(&self) -> Vec<A> {
let all_actions = self.action_data.keys().cloned();
all_actions
.into_iter()
.filter(|action| action.input_control_kind() == InputControlKind::Button)
.filter(|action| self.just_pressed(action))
.collect()
}
#[must_use]
pub fn get_released(&self) -> Vec<A> {
let all_actions = self.action_data.keys().cloned();
all_actions
.into_iter()
.filter(|action| action.input_control_kind() == InputControlKind::Button)
.filter(|action| self.released(action))
.collect()
}
#[must_use]
pub fn get_just_released(&self) -> Vec<A> {
let all_actions = self.action_data.keys().cloned();
all_actions
.into_iter()
.filter(|action| action.input_control_kind() == InputControlKind::Button)
.filter(|action| self.just_released(action))
.collect()
}
#[cfg(feature = "timing")]
#[must_use]
#[track_caller]
pub fn instant_started(&self, action: &A) -> Option<Instant> {
debug_assert_eq!(action.input_control_kind(), InputControlKind::Button);
let button_data = self.button_data(action)?;
button_data.timing.instant_started
}
#[cfg(feature = "timing")]
#[must_use]
#[track_caller]
pub fn current_duration(&self, action: &A) -> Duration {
debug_assert_eq!(action.input_control_kind(), InputControlKind::Button);
self.button_data(action)
.map(|data| data.timing.current_duration)
.unwrap_or_default()
}
#[cfg(feature = "timing")]
#[must_use]
#[track_caller]
pub fn previous_duration(&self, action: &A) -> Duration {
debug_assert_eq!(action.input_control_kind(), InputControlKind::Button);
self.button_data(action)
.map(|data| data.timing.previous_duration)
.unwrap_or_default()
}
pub fn apply_diff(&mut self, action_diff: &ActionDiff<A>) {
match action_diff {
ActionDiff::Pressed { action } => {
self.press(action);
}
ActionDiff::Released { action } => {
self.release(action);
}
ActionDiff::AxisChanged { action, value } => {
self.set_value(action, *value);
}
ActionDiff::DualAxisChanged { action, axis_pair } => {
self.set_axis_pair(action, *axis_pair);
}
ActionDiff::TripleAxisChanged {
action,
axis_triple,
} => {
self.set_axis_triple(action, *axis_triple);
}
};
}
#[inline]
#[must_use]
pub fn keys(&self) -> Vec<A> {
self.action_data.keys().cloned().collect()
}
}
#[cfg(test)]
mod tests {
use crate as leafwing_input_manager;
use crate::action_state::ActionState;
use bevy::prelude::*;
use leafwing_input_manager_macros::Actionlike;
#[cfg(feature = "keyboard")]
#[test]
fn press_lifecycle() {
use std::time::{Duration, Instant};
use crate::input_map::InputMap;
use crate::plugin::{AccumulatorPlugin, CentralInputStorePlugin};
use crate::prelude::updating::CentralInputStore;
use crate::prelude::ClashStrategy;
use crate::user_input::Buttonlike;
use bevy::input::InputPlugin;
let mut app = App::new();
app.add_plugins((InputPlugin, AccumulatorPlugin, CentralInputStorePlugin));
#[derive(Actionlike, Clone, Copy, PartialEq, Eq, Hash, Debug, bevy::prelude::Reflect)]
enum Action {
Run,
Jump,
Hide,
}
let mut action_state = ActionState::<Action>::default();
println!(
"Default button data: {:?}",
action_state.button_data(&Action::Run)
);
let mut input_map = InputMap::default();
input_map.insert(Action::Run, KeyCode::KeyR);
let input_store = app.world().resource::<CentralInputStore>();
action_state.update(input_map.process_actions(
&Gamepads::default(),
input_store,
ClashStrategy::PressAll,
));
println!(
"Initialized button data: {:?}",
action_state.button_data(&Action::Run)
);
assert!(!action_state.pressed(&Action::Run));
assert!(!action_state.just_pressed(&Action::Run));
assert!(action_state.released(&Action::Run));
assert!(!action_state.just_released(&Action::Run));
KeyCode::KeyR.press(app.world_mut());
app.update();
let input_store = app.world().resource::<CentralInputStore>();
action_state.update(input_map.process_actions(
&Gamepads::default(),
input_store,
ClashStrategy::PressAll,
));
assert!(action_state.pressed(&Action::Run));
assert!(action_state.just_pressed(&Action::Run));
assert!(!action_state.released(&Action::Run));
assert!(!action_state.just_released(&Action::Run));
action_state.tick(Instant::now(), Instant::now() - Duration::from_micros(1));
action_state.update(input_map.process_actions(
&Gamepads::default(),
input_store,
ClashStrategy::PressAll,
));
assert!(action_state.pressed(&Action::Run));
assert!(!action_state.just_pressed(&Action::Run));
assert!(!action_state.released(&Action::Run));
assert!(!action_state.just_released(&Action::Run));
KeyCode::KeyR.release(app.world_mut());
app.update();
let input_store = app.world().resource::<CentralInputStore>();
action_state.update(input_map.process_actions(
&Gamepads::default(),
input_store,
ClashStrategy::PressAll,
));
assert!(!action_state.pressed(&Action::Run));
assert!(!action_state.just_pressed(&Action::Run));
assert!(action_state.released(&Action::Run));
assert!(action_state.just_released(&Action::Run));
action_state.tick(Instant::now(), Instant::now() - Duration::from_micros(1));
action_state.update(input_map.process_actions(
&Gamepads::default(),
input_store,
ClashStrategy::PressAll,
));
assert!(!action_state.pressed(&Action::Run));
assert!(!action_state.just_pressed(&Action::Run));
assert!(action_state.released(&Action::Run));
assert!(!action_state.just_released(&Action::Run));
}
#[test]
fn synthetic_press() {
#[derive(Actionlike, Clone, Copy, PartialEq, Eq, Hash, Debug, Reflect)]
enum Action {
One,
Two,
}
let mut action_state = ActionState::<Action>::default();
action_state.press(&Action::One);
dbg!(&action_state);
assert!(action_state.pressed(&Action::One));
assert!(action_state.just_pressed(&Action::One));
assert!(!action_state.released(&Action::One));
assert!(!action_state.just_released(&Action::One));
assert!(!action_state.pressed(&Action::Two));
assert!(!action_state.just_pressed(&Action::Two));
assert!(action_state.released(&Action::Two));
assert!(!action_state.just_released(&Action::Two));
}
#[cfg(feature = "keyboard")]
#[test]
#[ignore = "Clashing inputs for non-buttonlike inputs is broken."]
fn update_with_clashes_prioritizing_longest() {
use std::time::{Duration, Instant};
use crate::input_map::InputMap;
use crate::plugin::{AccumulatorPlugin, CentralInputStorePlugin};
use crate::prelude::updating::CentralInputStore;
use crate::prelude::ClashStrategy;
use crate::user_input::chord::ButtonlikeChord;
use crate::user_input::Buttonlike;
use bevy::input::InputPlugin;
use bevy::prelude::KeyCode::*;
use bevy::prelude::*;
#[derive(Actionlike, Clone, Copy, PartialEq, Eq, Hash, Debug, Reflect)]
enum Action {
One,
Two,
OneAndTwo,
}
let mut input_map = InputMap::default();
input_map.insert(Action::One, Digit1);
input_map.insert(Action::Two, Digit2);
input_map.insert(Action::OneAndTwo, ButtonlikeChord::new([Digit1, Digit2]));
let mut app = App::new();
app.add_plugins(InputPlugin)
.add_plugins((AccumulatorPlugin, CentralInputStorePlugin));
let mut action_state = ActionState::<Action>::default();
let input_store = app.world().resource::<CentralInputStore>();
action_state.update(input_map.process_actions(
&Gamepads::default(),
input_store,
ClashStrategy::PrioritizeLongest,
));
assert!(action_state.released(&Action::One));
assert!(action_state.released(&Action::Two));
assert!(action_state.released(&Action::OneAndTwo));
Digit1.press(app.world_mut());
app.update();
let input_store = app.world().resource::<CentralInputStore>();
action_state.update(input_map.process_actions(
&Gamepads::default(),
input_store,
ClashStrategy::PrioritizeLongest,
));
assert!(action_state.pressed(&Action::One));
assert!(action_state.released(&Action::Two));
assert!(action_state.released(&Action::OneAndTwo));
action_state.tick(Instant::now(), Instant::now() - Duration::from_micros(1));
action_state.update(input_map.process_actions(
&Gamepads::default(),
input_store,
ClashStrategy::PrioritizeLongest,
));
assert!(action_state.pressed(&Action::One));
assert!(action_state.released(&Action::Two));
assert!(action_state.released(&Action::OneAndTwo));
Digit2.press(app.world_mut());
app.update();
let input_store = app.world().resource::<CentralInputStore>();
action_state.update(input_map.process_actions(
&Gamepads::default(),
input_store,
ClashStrategy::PrioritizeLongest,
));
assert!(action_state.released(&Action::One));
assert!(action_state.released(&Action::Two));
assert!(action_state.pressed(&Action::OneAndTwo));
action_state.tick(Instant::now(), Instant::now() - Duration::from_micros(1));
action_state.update(input_map.process_actions(
&Gamepads::default(),
input_store,
ClashStrategy::PrioritizeLongest,
));
assert!(action_state.released(&Action::One));
assert!(action_state.released(&Action::Two));
assert!(action_state.pressed(&Action::OneAndTwo));
}
}