Skip to content

Commit

Permalink
Docs
Browse files Browse the repository at this point in the history
  • Loading branch information
Shute052 committed May 7, 2024
1 parent 94c12c5 commit 61ab859
Show file tree
Hide file tree
Showing 20 changed files with 1,096 additions and 865 deletions.
2 changes: 1 addition & 1 deletion README.md
Original file line number Diff line number Diff line change
Expand Up @@ -36,7 +36,7 @@ and a single input can result in multiple actions being triggered, which can be
- Ergonomic insertion API that seamlessly blends multiple input types for you
- Can't decide between `input_map.insert(Action::Jump, KeyCode::Space)` and `input_map.insert(Action::Jump, GamepadButtonType::South)`? Have both!
- Full support for arbitrary button combinations: chord your heart out.
- `input_map.insert(Action::Console, InputChord::multiple([KeyCode::ControlLeft, KeyCode::Shift, KeyCode::KeyC]))`
- `input_map.insert(Action::Console, InputChord::from_multiple([KeyCode::ControlLeft, KeyCode::Shift, KeyCode::KeyC]))`
- Sophisticated input disambiguation with the `ClashStrategy` enum: stop triggering individual buttons when you meant to press a chord!
- Create an arbitrary number of strongly typed disjoint action sets by adding multiple copies of this plugin: decouple your camera and player state
- Local multiplayer support: freely bind keys to distinct entities, rather than worrying about singular global state
Expand Down
45 changes: 30 additions & 15 deletions RELEASES.md
Original file line number Diff line number Diff line change
Expand Up @@ -11,18 +11,16 @@
- applied clashing check to continuous mouse inputs, for example:
- `MouseScrollAxis::Y` will clash with `MouseScrollDirection::UP` and `MouseScrollDirection::DOWN`.
- `MouseMove` will clash with all the two axes and the four directions.
- refactored the method signatures of `InputMap` to fit the new input types.
- removed `InputMap::insert_chord` and `InputMap::insert_modified` due to their limited applicability within the type system.
- the new `InputChord` contructors and builders allow you to define chords with guaranteed type safety.
- the new `ModifierKey::with` method simplifies the creation of input chords that include the modifier and your desired input.
- removed `InputMap::build` method in favor of new fluent builder pattern (see 'Usability: InputMap' for details).
- renamed `InputMap::which_pressed` method to `process_actions` to better reflect its current functionality for clarity.
- removed `DeadZoneShape` in favor of new dead zone processors (see 'Enhancements: Input Processors' for details).
- refactored the method signatures of `InputMap` to fit the new input types.
- refactored the fields and methods of `RawInputs` to fit the new input types.
- removed `Direction` type in favor of `bevy::math::primitives::Direction2d`.
- split `MockInput::send_input` method to two methods:
- `fn press_input(&self, input: impl UserInput)` for focusing on simulating button and key presses.
- `fn send_axis_values(&self, input: impl UserInput, values: impl IntoIterator<Item = f32>)` for sending value changed events to each axis of the input.
- removed `MockInput::send_input` methods, in favor of new input mocking APIs (see 'Usability: MockInput' for details).
- made the dependency on bevy's `bevy_gilrs` feature optional.
- it is still enabled by leafwing-input-manager's default features.
- if you're using leafwing-input-manager with `default_features = false`, you can readd it by adding `bevy/bevy_gilrs` as a dependency.
Expand All @@ -43,9 +41,8 @@
- added `UserInput` impls for keyboard inputs:
- implemented `UserInput` for Bevy’s `KeyCode` directly.
- implemented `UserInput` for `ModifierKey`.
- added `KeyboardKey` enum, including individual `KeyCode`s, `ModifierKey`s, and checks for any key press.
- added `KeyboardVirtualAxis`, similar to the old `UserInput::VirtualAxis` using two `KeyboardKey`s.
- added `KeyboardVirtualDPad`, similar to the old `UserInput::VirtualDPad` using four `KeyboardKey`s.
- added `KeyboardVirtualAxis`, similar to the old `UserInput::VirtualAxis` using two `KeyCode`s.
- added `KeyboardVirtualDPad`, similar to the old `UserInput::VirtualDPad` using four `KeyCode`s.
- added `UserInput` impls for mouse inputs:
- implemented `UserInput` for movement-related inputs.
- `MouseMove`: Continuous or discrete movement events of the mouse both X and Y axes.
Expand All @@ -65,22 +62,22 @@
- `MouseScrollAxis::X` and `MouseScrollAxis::Y` for continuous mouse wheel movement.
- the old `DualAxis` is now:
- `GamepadStick` for gamepad sticks.
- `MouseMove::RAW` for continuous mouse movement.
- `MouseScroll::RAW` for continuous mouse wheel movement.
- `MouseMove::default()` for continuous mouse movement.
- `MouseScroll::default()` for continuous mouse wheel movement.
- the old `Modifier` is now `ModifierKey`.
- the old `MouseMotionDirection` is now `MouseMoveDirection`.
- the old `MouseWheelDirection` is now `MouseScrollDirection`.
- the old `UserInput::Chord` is now `InputChord`.
- the old `UserInput::VirtualAxis` is now:
- `GamepadVirtualAxis` for four gamepad buttons.
- `KeyboardVirtualAxis` for four keys.
- `MouseMoveAxis::X_DIGITAL` and `MouseMoveAxis::X_DIGITAL` for discrete mouse movement.
- `MouseScrollAxis::X_DIGITAL` and `MouseScrollAxis::Y_DIGITAL` for discrete mouse wheel movement.
- `MouseMoveAxis::X.digital()` and `MouseMoveAxis::Y.digital()` for discrete mouse movement.
- `MouseScrollAxis::X.digital()` and `MouseScrollAxis::Y.digital()` for discrete mouse wheel movement.
- the old `UserInput::VirtualDPad` is now:
- `GamepadVirtualDPad` for four gamepad buttons.
- `KeyboardVirtualDPad` for four keys.
- `MouseMove::DIGITAL` for discrete mouse movement.
- `MouseScroll::DIGITAL` for discrete mouse wheel movement.
- `MouseMove::default().digital()` for discrete mouse movement.
- `MouseScroll::default().digital()` for discrete mouse wheel movement.

#### Input Processors

Expand All @@ -100,6 +97,9 @@ Input processors allow you to create custom logic for axis-like input manipulati
- you can also create them by these methods:
- `AxisProcessor::pipeline` or `AxisProcessor::with_processor` for `AxisProcessor::Pipeline`.
- `DualAxisProcessor::pipeline` or `DualAxisProcessor::with_processor` for `DualAxisProcessor::Pipeline`.
- Digital Conversion: Similar to `signum` but returning `0.0` for zero values:
- `AxisProcessor::Digital`: Single-axis digital conversion.
- `DualAxisProcessor::Digital`: Dual-axis digital conversion.
- Inversion: Reverses control (positive becomes negative, etc.)
- `AxisProcessor::Inverted`: Single-axis inversion.
- `DualAxisInverted`: Dual-axis inversion, implemented `Into<DualAxisProcessor>`.
Expand Down Expand Up @@ -128,14 +128,29 @@ Input processors allow you to create custom logic for axis-like input manipulati

- added new fluent builders for creating a new `InputMap<A>` with short configurations:
- `fn with(mut self, action: A, input: impl UserInput)`.
- `fn with_one_to_many<U: UserInput>(mut self, action: A, inputs: impl IntoIterator<Item = UserInput>)`.
- `fn with_multiple<U: UserInput>(mut self, bindings: impl IntoIterator<Item = (A, UserInput)>) -> Self`.
- `fn with_one_to_many(mut self, action: A, inputs: impl IntoIterator<Item = impl UserInput>)`.
- `fn with_multiple(mut self, bindings: impl IntoIterator<Item = (A, impl UserInput)>) -> Self`.
- `fn with_gamepad(mut self, gamepad: Gamepad) -> Self`.

- added new iterators over `InputMap<A>`:
- `actions(&self) -> impl Iterator<Item = &A>` for iterating over all registered actions.
- `bindings(&self) -> impl Iterator<Item = (&A, &dyn UserInput)>` for iterating over all registered action-input bindings.

### MockInput

- added new methods for the `MockInput` trait.
- `fn press_input(&self, input: impl UserInput)` for simulating button and key presses.
- `fn send_axis_values(&self, input: impl UserInput, values: impl IntoIterator<Item = f32>)` for sending value changed events to each axis represented by the input.
- as well as methods for a specific gamepad.
- implemented the methods for `MutableInputStreams`, `World`, and `App`.

### QueryInput

- added new methods for the `QueryInput` trait.
- `fn read_axis_values(&self, input: impl UserInput) -> Vec<f32>` to read the values on all axes represented by an input.
- as well as methods for a specific gamepad.
- implemented the methods for `InputStreams`, `World`, and `App`.

### Bugs

- fixed a bug in `InputStreams::button_pressed()` where unrelated gamepads were not filtered out when an `associated_gamepad` is defined.
Expand Down
2 changes: 1 addition & 1 deletion examples/input_processing.rs
Original file line number Diff line number Diff line change
Expand Up @@ -45,7 +45,7 @@ fn spawn_player(mut commands: Commands) {
.with(
Action::LookAround,
// You can also use a sequence of processors as the processing pipeline.
MouseMove::RAW.with_processor(DualAxisProcessor::pipeline([
MouseMove::default().with_processor(DualAxisProcessor::pipeline([
// The first processor is a circular deadzone.
CircleDeadZone::new(0.1).into(),
// The next processor doubles inputs normalized by the deadzone.
Expand Down
2 changes: 1 addition & 1 deletion examples/mouse_motion.rs
Original file line number Diff line number Diff line change
Expand Up @@ -20,7 +20,7 @@ fn setup(mut commands: Commands) {
// This will capture the total continuous value, for direct use.
// Note that you can also use discrete gesture-like motion,
// via the `MouseMoveDirection` enum.
(CameraMovement::Pan, MouseMove::RAW),
(CameraMovement::Pan, MouseMove::default()),
]);
commands
.spawn(Camera2dBundle::default())
Expand Down
4 changes: 2 additions & 2 deletions examples/mouse_wheel.rs
Original file line number Diff line number Diff line change
Expand Up @@ -27,9 +27,9 @@ fn setup(mut commands: Commands) {
.with(CameraMovement::PanLeft, MouseScrollDirection::LEFT)
.with(CameraMovement::PanRight, MouseScrollDirection::RIGHT)
// Alternatively, you could model them as a continuous dual-axis input
.with(CameraMovement::Pan, MouseScroll::RAW)
.with(CameraMovement::Pan, MouseScroll::default())
// Or even a digital dual-axis input!
.with(CameraMovement::Pan, MouseScroll::DIGITAL);
.with(CameraMovement::Pan, MouseScroll::default().digital());
commands
.spawn(Camera2dBundle::default())
.insert(InputManagerBundle::with_map(input_map));
Expand Down
69 changes: 0 additions & 69 deletions src/axislike.rs
Original file line number Diff line number Diff line change
Expand Up @@ -5,75 +5,6 @@ use serde::{Deserialize, Serialize};

use crate::orientation::Rotation;

/// Different ways that user input is represented on an axis.
#[derive(Debug, Default, Clone, Copy, PartialEq, Eq, Hash, Deserialize, Serialize, Reflect)]
#[must_use]
pub enum AxisInputMode {
/// Continuous input values, typically range from a negative maximum (e.g., `-1.0`)
/// to a positive maximum (e.g., `1.0`), allowing for smooth and precise control.
#[default]
Analog,

/// Discrete input values, using three distinct values to represent the states:
/// `-1.0` for active in negative direction, `0.0` for inactive, and `1.0` for active in positive direction.
Digital,
}

impl AxisInputMode {
/// Converts the given `f32` value based on the current [`AxisInputMode`].
///
/// # Returns
///
/// - [`AxisInputMode::Analog`]: Leaves values as is.
/// - [`AxisInputMode::Digital`]: Maps negative values to `-1.0` and positive values to `1.0`, leaving others as is.
#[must_use]
#[inline]
pub fn axis_value(&self, value: f32) -> f32 {
match self {
Self::Analog => value,
Self::Digital => {
if value < 0.0 {
-1.0
} else if value > 0.0 {
1.0
} else {
value
}
}
}
}

/// Converts the given [`Vec2`] value based on the current [`AxisInputMode`].
///
/// # Returns
///
/// - [`AxisInputMode::Analog`]: Leaves values as is.
/// - [`AxisInputMode::Digital`]: Maps negative values to `-1.0` and positive values to `1.0` along each axis, leaving others as is.
#[must_use]
#[inline]
pub fn dual_axis_value(&self, value: Vec2) -> Vec2 {
match self {
Self::Analog => value,
Self::Digital => Vec2::new(self.axis_value(value.x), self.axis_value(value.y)),
}
}

/// Computes the magnitude of given [`Vec2`] value based on the current [`AxisInputMode`].
///
/// # Returns
///
/// - [`AxisInputMode::Analog`]: Leaves values as is.
/// - [`AxisInputMode::Digital`]: `1.0` for non-zero values, `0.0` for others.
#[must_use]
#[inline]
pub fn dual_axis_magnitude(&self, value: Vec2) -> f32 {
match self {
Self::Analog => value.length(),
Self::Digital => f32::from(value != Vec2::ZERO),
}
}
}

/// The directions for single-axis inputs.
#[derive(Debug, Clone, Copy, PartialEq, Eq, Hash, Reflect, Serialize, Deserialize)]
#[must_use]
Expand Down
12 changes: 6 additions & 6 deletions src/clashing_inputs.rs
Original file line number Diff line number Diff line change
Expand Up @@ -201,7 +201,7 @@ impl<A: Actionlike> InputMap<A> {

for input_a in self.get(action_a)? {
for input_b in self.get(action_b)? {
if input_a.basic_inputs().clashed(&input_b.basic_inputs()) {
if input_a.decompose().clashed(&input_b.decompose()) {
clash.inputs_a.push(input_a.clone());
clash.inputs_b.push(input_b.clone());
}
Expand Down Expand Up @@ -256,7 +256,7 @@ fn check_clash<A: Actionlike>(clash: &Clash<A>, input_streams: &InputStreams) ->
.filter(|&input| input.pressed(input_streams))
{
// If a clash was detected
if input_a.basic_inputs().clashed(&input_b.basic_inputs()) {
if input_a.decompose().clashed(&input_b.decompose()) {
actual_clash.inputs_a.push(input_a.clone());
actual_clash.inputs_b.push(input_b.clone());
}
Expand Down Expand Up @@ -294,7 +294,7 @@ fn resolve_clash<A: Actionlike>(
for reason_b in reasons_b_is_pressed.iter() {
// If there is at least one non-clashing reason why these buttons should both be pressed,
// we can avoid resolving the clash completely
if !reason_a.basic_inputs().clashed(&reason_b.basic_inputs()) {
if !reason_a.decompose().clashed(&reason_b.decompose()) {
return None;
}
}
Expand All @@ -308,13 +308,13 @@ fn resolve_clash<A: Actionlike>(
ClashStrategy::PrioritizeLongest => {
let longest_a: usize = reasons_a_is_pressed
.iter()
.map(|input| input.basic_inputs().len())
.map(|input| input.decompose().len())
.reduce(|a, b| a.max(b))
.unwrap_or_default();

let longest_b: usize = reasons_b_is_pressed
.iter()
.map(|input| input.basic_inputs().len())
.map(|input| input.decompose().len())
.reduce(|a, b| a.max(b))
.unwrap_or_default();

Expand Down Expand Up @@ -379,7 +379,7 @@ mod tests {
}

fn test_input_clash(input_a: impl UserInput, input_b: impl UserInput) -> bool {
input_a.basic_inputs().clashed(&input_b.basic_inputs())
input_a.decompose().clashed(&input_b.decompose())
}

mod basic_functionality {
Expand Down
20 changes: 10 additions & 10 deletions src/input_map.rs
Original file line number Diff line number Diff line change
Expand Up @@ -46,7 +46,7 @@ use crate::Actionlike;
/// ```rust
/// use bevy::prelude::*;
/// use leafwing_input_manager::prelude::*;
/// use leafwing_input_manager::user_input::InputKind;
/// use leafwing_input_manager::user_input::InputControlKind;
///
/// // Define your actions.
/// #[derive(Actionlike, Clone, Copy, PartialEq, Eq, Hash, Reflect)]
Expand Down Expand Up @@ -109,7 +109,7 @@ impl<A: Actionlike> InputMap<A> {
/// This method ensures idempotence, meaning that adding the same input
/// for the same action multiple times will only result in a single binding being created.
#[inline(always)]
pub fn new<U: UserInput>(bindings: impl IntoIterator<Item = (A, U)>) -> Self {
pub fn new(bindings: impl IntoIterator<Item = (A, impl UserInput)>) -> Self {
bindings
.into_iter()
.fold(Self::default(), |map, (action, input)| {
Expand All @@ -134,10 +134,10 @@ impl<A: Actionlike> InputMap<A> {
/// This method ensures idempotence, meaning that adding the same input
/// for the same action multiple times will only result in a single binding being created.
#[inline(always)]
pub fn with_one_to_many<U: UserInput>(
pub fn with_one_to_many(
mut self,
action: A,
inputs: impl IntoIterator<Item = U>,
inputs: impl IntoIterator<Item = impl UserInput>,
) -> Self {
self.insert_one_to_many(action, inputs);
self
Expand All @@ -149,9 +149,9 @@ impl<A: Actionlike> InputMap<A> {
/// This method ensures idempotence, meaning that adding the same input
/// for the same action multiple times will only result in a single binding being created.
#[inline(always)]
pub fn with_multiple<U: UserInput>(
pub fn with_multiple(
mut self,
bindings: impl IntoIterator<Item = (A, U)>,
bindings: impl IntoIterator<Item = (A, impl UserInput)>,
) -> Self {
self.insert_multiple(bindings);
self
Expand Down Expand Up @@ -195,10 +195,10 @@ impl<A: Actionlike> InputMap<A> {
/// This method ensures idempotence, meaning that adding the same input
/// for the same action multiple times will only result in a single binding being created.
#[inline(always)]
pub fn insert_one_to_many<U: UserInput>(
pub fn insert_one_to_many(
&mut self,
action: A,
inputs: impl IntoIterator<Item = U>,
inputs: impl IntoIterator<Item = impl UserInput>,
) -> &mut Self {
let inputs = inputs
.into_iter()
Expand All @@ -221,9 +221,9 @@ impl<A: Actionlike> InputMap<A> {
/// This method ensures idempotence, meaning that adding the same input
/// for the same action multiple times will only result in a single binding being created.
#[inline(always)]
pub fn insert_multiple<U: UserInput>(
pub fn insert_multiple(
&mut self,
bindings: impl IntoIterator<Item = (A, U)>,
bindings: impl IntoIterator<Item = (A, impl UserInput)>,
) -> &mut Self {
for (action, input) in bindings.into_iter() {
self.insert(action, input);
Expand Down
Loading

0 comments on commit 61ab859

Please sign in to comment.