Skip to content

Commit

Permalink
Fix impossible-to-fulfill requirements for custom input registration …
Browse files Browse the repository at this point in the history
…api (#680)

* fix impossible-to-fulfill requirements for custom input registration api

* remove register_standard_input_kinds from the public api

* remove unused import

* fix docs
  • Loading branch information
Piefayth authored Feb 1, 2025
1 parent 3eda257 commit 96d6c41
Show file tree
Hide file tree
Showing 3 changed files with 74 additions and 63 deletions.
3 changes: 3 additions & 0 deletions RELEASES.md
Original file line number Diff line number Diff line change
Expand Up @@ -2,6 +2,9 @@

## Version 0.17.0 (unreleased)

### Bugs (0.17.0)
- fixed the bug making it impossible to register custom input types via `register_input_kind`

### Dependencies (0.17.0)

- now supports bevy_egui 0.32
Expand Down
11 changes: 6 additions & 5 deletions src/plugin.rs
Original file line number Diff line number Diff line change
Expand Up @@ -18,6 +18,7 @@ use crate::action_state::{ActionState, ButtonData};
use crate::clashing_inputs::ClashStrategy;
use crate::input_map::InputMap;
use crate::input_processing::*;
use crate::prelude::updating::register_standard_input_kinds;
#[cfg(feature = "timing")]
use crate::timing::Timing;
use crate::user_input::*;
Expand Down Expand Up @@ -312,15 +313,15 @@ impl<A: Actionlike> Default for TickActionStateSystem<A> {
/// This plugin is added by default by [`InputManagerPlugin`],
/// and will register all of the standard [`UserInput`]s.
///
/// To add more inputs, call [`CentralInputStore::register_input_kind`] during [`App`] setup.
/// To add more inputs, call [`crate::user_input::updating::InputRegistration::register_input_kind`] via [`App`] during [`App`] setup.
pub struct CentralInputStorePlugin;

impl Plugin for CentralInputStorePlugin {
fn build(&self, app: &mut App) {
let mut central_input_store = CentralInputStore::default();
central_input_store.register_standard_input_kinds(app);
app.insert_resource(CentralInputStore::default());

app.insert_resource(central_input_store)
.configure_sets(PreUpdate, InputManagerSystem::Unify.after(InputSystem));
register_standard_input_kinds(app);

app.configure_sets(PreUpdate, InputManagerSystem::Unify.after(InputSystem));
}
}
123 changes: 65 additions & 58 deletions src/user_input/updating.rs
Original file line number Diff line number Diff line change
Expand Up @@ -21,7 +21,7 @@ use crate::{plugin::InputManagerSystem, InputControlKind};
/// This resource allows values to be updated and fetched in a single location,
/// and ensures that their values are only recomputed once per frame.
///
/// To add a new kind of input, call [`CentralInputStore::register_input_kind`] during [`App`] setup.
/// To add a new kind of input, call [`InputRegistration::register_input_kind`] during [`App`] setup.
#[derive(Resource, Default, Debug, Reflect)]
pub struct CentralInputStore {
/// Stores the updated values of each kind of input.
Expand All @@ -31,61 +31,6 @@ pub struct CentralInputStore {
}

impl CentralInputStore {
/// Registers a new source of raw input data of a matching `kind`.
///
/// This will allow the input to be updated based on the state of the world,
/// by adding the [`UpdatableInput::compute`] system to [`InputManagerSystem::Unify`] during [`PreUpdate`].
///
/// To improve clarity and data consistency, only one kind of input should be registered for each new data stream:
/// compute the values of all related inputs from the data stored the [`CentralInputStore`].
///
/// This method has no effect if the input kind has already been registered.
pub fn register_input_kind<I: UpdatableInput>(
&mut self,
kind: InputControlKind,
app: &mut App,
) {
// Ensure this method is idempotent.
if self.registered_input_kinds.contains(&TypeId::of::<I>()) {
return;
}

self.updated_values.insert(
TypeId::of::<I>(),
UpdatedValues::from_input_control_kind(kind),
);
self.registered_input_kinds.insert(TypeId::of::<I>());
app.add_systems(PreUpdate, I::compute.in_set(InputManagerSystem::Unify));
}

/// Registers the standard input types defined by [`bevy`] and [`leafwing_input_manager`](crate).
///
/// The set of input kinds registered by this method is controlled by the features enabled:
/// turn off default features to avoid registering input kinds that are not needed.
#[allow(unused_variables)]
pub fn register_standard_input_kinds(&mut self, app: &mut App) {
// Buttonlike
#[cfg(feature = "keyboard")]
self.register_input_kind::<bevy::input::keyboard::KeyCode>(InputControlKind::Button, app);
#[cfg(feature = "mouse")]
self.register_input_kind::<bevy::input::mouse::MouseButton>(InputControlKind::Button, app);
#[cfg(feature = "gamepad")]
self.register_input_kind::<bevy::input::gamepad::GamepadButton>(
InputControlKind::Button,
app,
);

// Axislike
#[cfg(feature = "gamepad")]
self.register_input_kind::<bevy::input::gamepad::GamepadAxis>(InputControlKind::Axis, app);

// Dualaxislike
#[cfg(feature = "mouse")]
self.register_input_kind::<crate::prelude::MouseMove>(InputControlKind::DualAxis, app);
#[cfg(feature = "mouse")]
self.register_input_kind::<crate::prelude::MouseScroll>(InputControlKind::DualAxis, app);
}

/// Clears all existing values.
///
/// This should be called once at the start of each frame, before polling for new input.
Expand Down Expand Up @@ -257,6 +202,68 @@ impl CentralInputStore {
}
}

/// Trait for registering updatable inputs with the central input store
pub trait InputRegistration {
/// Registers a new source of raw input data of a matching `kind`.
///
/// This will allow the input to be updated based on the state of the world,
/// by adding the [`UpdatableInput::compute`] system to [`InputManagerSystem::Unify`] during [`PreUpdate`].
///
/// To improve clarity and data consistency, only one kind of input should be registered for each new data stream:
/// compute the values of all related inputs from the data stored the [`CentralInputStore`].
///
/// This method has no effect if the input kind has already been registered.
fn register_input_kind<I: UpdatableInput>(&mut self, kind: InputControlKind);
}

impl InputRegistration for App {
fn register_input_kind<I: UpdatableInput>(&mut self, kind: InputControlKind) {
let mut central_input_store = self.world_mut().resource_mut::<CentralInputStore>();

// Ensure this method is idempotent.
if central_input_store
.registered_input_kinds
.contains(&TypeId::of::<I>())
{
return;
}

central_input_store.updated_values.insert(
TypeId::of::<I>(),
UpdatedValues::from_input_control_kind(kind),
);
central_input_store
.registered_input_kinds
.insert(TypeId::of::<I>());
self.add_systems(PreUpdate, I::compute.in_set(InputManagerSystem::Unify));
}
}

/// Registers the standard input types defined by [`bevy`] and [`leafwing_input_manager`](crate).
///
/// The set of input kinds registered by this method is controlled by the features enabled:
/// turn off default features to avoid registering input kinds that are not needed.
#[allow(unused_variables)]
pub(crate) fn register_standard_input_kinds(app: &mut App) {
// Buttonlike
#[cfg(feature = "keyboard")]
app.register_input_kind::<bevy::input::keyboard::KeyCode>(InputControlKind::Button);
#[cfg(feature = "mouse")]
app.register_input_kind::<bevy::input::mouse::MouseButton>(InputControlKind::Button);
#[cfg(feature = "gamepad")]
app.register_input_kind::<bevy::input::gamepad::GamepadButton>(InputControlKind::Button);

// Axislike
#[cfg(feature = "gamepad")]
app.register_input_kind::<bevy::input::gamepad::GamepadAxis>(InputControlKind::Axis);

// Dualaxislike
#[cfg(feature = "mouse")]
app.register_input_kind::<crate::prelude::MouseMove>(InputControlKind::DualAxis);
#[cfg(feature = "mouse")]
app.register_input_kind::<crate::prelude::MouseScroll>(InputControlKind::DualAxis);
}

/// A map of values that have been updated during the current frame.
///
/// The key should be the default form of the input if there is no need to differentiate between possible inputs of the same type,
Expand Down Expand Up @@ -293,7 +300,7 @@ impl UpdatedValues {
/// however when configuration is needed (such as for processed axes or virtual d-pads),
/// two distinct types must be used.
///
/// To add a new kind of input, call [`CentralInputStore::register_input_kind`] during [`App`] setup.
/// To add a new kind of input, call [`InputRegistration::register_input_kind`] during [`App`] setup.
pub trait UpdatableInput: 'static {
/// The [`SystemParam`] that must be fetched from the world in order to update the user input.
///
Expand All @@ -308,7 +315,7 @@ pub trait UpdatableInput: 'static {
///
/// # Warning
///
/// This system should not be added manually: instead, call [`CentralInputStore::register_input_kind`].
/// This system should not be added manually: instead, call [`InputRegistration::register_input_kind`].
fn compute(
central_input_store: ResMut<CentralInputStore>,
source_data: StaticSystemParam<Self::SourceData>,
Expand Down

0 comments on commit 96d6c41

Please sign in to comment.