Skip to content
New issue

Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.

By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.

Already on GitHub? Sign in to your account

Remove action consuming #582

Merged
merged 1 commit into from
Aug 8, 2024
Merged
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
2 changes: 2 additions & 0 deletions RELEASES.md
Original file line number Diff line number Diff line change
Expand Up @@ -135,6 +135,8 @@ Input processors allow you to create custom logic for axis-like input manipulati
- removed the `no_ui_priority` feature. To get this behavior, now just turn off the default `ui` feature
- removed the `orientation` module, migrating to `bevy_math::Rot2`
- use the types provided in `bevy_math` instead
- remove action consuming (and various `consume` / `consumed` methods) to reduce complexity and avoid confusing overlap with action disabling
- write your own logic for cases where this was used: generally by working off of `ActionDiff` events that are consumed

### Migration Guide (0.15)

Expand Down
135 changes: 0 additions & 135 deletions examples/consuming_actions.rs

This file was deleted.

13 changes: 1 addition & 12 deletions src/action_state/action_data.rs
Original file line number Diff line number Diff line change
Expand Up @@ -43,10 +43,7 @@ impl ActionData {
data.state.tick();

#[cfg(feature = "timing")]
// Durations should not advance while actions are consumed
if !data.consumed {
data.timing.tick(_current_instant, _previous_instant);
}
data.timing.tick(_current_instant, _previous_instant);
}
ActionKindData::Axis(ref mut _data) => {}
ActionKindData::DualAxis(ref mut _data) => {}
Expand Down Expand Up @@ -117,11 +114,6 @@ pub struct ButtonData {
/// When was the button pressed / released, and how long has it been held for?
#[cfg(feature = "timing")]
pub timing: Timing,
/// Was this action consumed by [`ActionState::consume`](super::ActionState::consume)?
///
/// Actions that are consumed cannot be pressed again until they are explicitly released.
/// This ensures that consumed actions are not immediately re-pressed by continued inputs.
pub consumed: bool,
}

impl ButtonData {
Expand All @@ -132,7 +124,6 @@ impl ButtonData {
fixed_update_state: ButtonState::JustPressed,
#[cfg(feature = "timing")]
timing: Timing::NEW,
consumed: false,
};

/// The default data for a button that was just released.
Expand All @@ -142,7 +133,6 @@ impl ButtonData {
fixed_update_state: ButtonState::JustReleased,
#[cfg(feature = "timing")]
timing: Timing::NEW,
consumed: false,
};

/// The default data for a button that is released,
Expand All @@ -156,7 +146,6 @@ impl ButtonData {
fixed_update_state: ButtonState::Released,
#[cfg(feature = "timing")]
timing: Timing::NEW,
consumed: false,
};

/// Is the action currently pressed?
Expand Down
73 changes: 0 additions & 73 deletions src/action_state/mod.rs
Original file line number Diff line number Diff line change
Expand Up @@ -591,11 +591,6 @@ impl<A: Actionlike> ActionState<A> {

let action_data = self.button_data_mut_or_default(action);

// Consumed actions cannot be pressed until they are released
if action_data.consumed {
return;
}

#[cfg(feature = "timing")]
if action_data.state.released() {
action_data.timing.flip();
Expand All @@ -614,9 +609,6 @@ impl<A: Actionlike> ActionState<A> {

let action_data = self.button_data_mut_or_default(action);

// Once released, consumed actions can be pressed again
action_data.consumed = false;

#[cfg(feature = "timing")]
if action_data.state.pressed() {
action_data.timing.flip();
Expand Down Expand Up @@ -651,71 +643,6 @@ impl<A: Actionlike> ActionState<A> {
}
}

/// Consumes the `action`
///
/// The action will be released, and will not be able to be pressed again
/// until it would have otherwise been released by [`ActionState::release`],
/// [`ActionState::reset`], [`ActionState::reset_all`] or [`ActionState::update`].
///
/// No initial instant will be recorded
/// Instead, this is set through [`ActionState::tick()`]
///
/// # Example
///
/// ```rust
/// use bevy::prelude::Reflect;
/// use leafwing_input_manager::prelude::*;
///
/// #[derive(Actionlike, Clone, Copy, PartialEq, Eq, Hash, Debug, Reflect)]
/// enum Action {
/// Eat,
/// Sleep,
/// }
///
/// let mut action_state = ActionState::<Action>::default();
///
/// action_state.press(&Action::Eat);
/// assert!(action_state.pressed(&Action::Eat));
///
/// // Consuming actions releases them
/// action_state.consume(&Action::Eat);
/// assert!(action_state.released(&Action::Eat));
///
/// // Doesn't work, as the action was consumed
/// action_state.press(&Action::Eat);
/// assert!(action_state.released(&Action::Eat));
///
/// // Releasing consumed actions allows them to be pressed again
/// action_state.release(&Action::Eat);
/// action_state.press(&Action::Eat);
/// assert!(action_state.pressed(&Action::Eat));
/// ```
#[inline]
pub fn consume(&mut self, action: &A) {
let action_data = self.button_data_mut_or_default(action);

// This is the only difference from action_state.release(&action)
action_data.consumed = true;
action_data.state.release();
#[cfg(feature = "timing")]
action_data.timing.flip();
}

/// Consumes all actions
#[inline]
pub fn consume_all(&mut self) {
for action in self.keys() {
self.consume(&action);
}
}

/// Is this `action` currently consumed?
#[inline]
#[must_use]
pub fn consumed(&self, action: &A) -> bool {
matches!(self.button_data(action), Some(action_data) if action_data.consumed)
}

/// Is the entire [`ActionState`] currently disabled?
pub fn disabled(&self) -> bool {
self.disabled
Expand Down
63 changes: 1 addition & 62 deletions tests/fixed_update.rs
Original file line number Diff line number Diff line change
Expand Up @@ -6,7 +6,7 @@ use bevy::time::TimeUpdateStrategy;
use bevy::MinimalPlugins;
use leafwing_input_manager::action_state::ActionState;
use leafwing_input_manager::input_map::InputMap;
use leafwing_input_manager::plugin::{InputManagerPlugin, InputManagerSystem};
use leafwing_input_manager::plugin::InputManagerPlugin;
use leafwing_input_manager::prelude::Buttonlike;
use leafwing_input_manager_macros::Actionlike;
use std::time::Duration;
Expand Down Expand Up @@ -205,64 +205,3 @@ fn frame_with_two_fixed_timestep() {
Duration::from_millis(18)
);
}

/// Check that if the action is consumed in FU1, it will still be consumed in F2.
/// (i.e. consuming is shared between the `FixedMain` and `Main` schedules)
#[test]
fn test_consume_in_fixed_update() {
let mut app = build_app(Duration::from_millis(5), Duration::from_millis(5));

app.add_systems(
FixedPostUpdate,
|mut action: ResMut<ActionState<TestAction>>| {
action.consume(&TestAction::Up);
},
);

KeyCode::ArrowUp.press(app.world_mut());

// Frame 1: the FixedUpdate schedule should run once and the button should be just_pressed only once
app.update();
check_update_just_pressed_count(&mut app, 1);
check_fixed_update_run_count(&mut app, 1);
check_fixed_update_just_pressed_count(&mut app, 1);
reset_counters(&mut app);

// the button should still be consumed, even after we exit the FixedUpdate schedule
assert!(
app.world()
.get_resource::<ActionState<TestAction>>()
.unwrap()
.button_data(&TestAction::Up)
.unwrap()
.consumed,
);
}

/// Check that if the action is consumed in F1, it will still be consumed in FU1.
/// (i.e. consuming is shared between the `FixedMain` and `Main` schedules)
#[test]
fn test_consume_in_update() {
let mut app = build_app(Duration::from_millis(5), Duration::from_millis(5));

KeyCode::ArrowUp.press(app.world_mut());
fn consume_action(mut action: ResMut<ActionState<TestAction>>) {
action.consume(&TestAction::Up);
}

app.add_systems(
PreUpdate,
consume_action.in_set(InputManagerSystem::ManualControl),
);

app.add_systems(FixedUpdate, |action: Res<ActionState<TestAction>>| {
// check that the action is still consumed in the FixedMain schedule
assert!(
action.consumed(&TestAction::Up),
"Action should still be consumed in FixedUpdate"
);
});

// the FixedUpdate schedule should run once
app.update();
}