Skip to content

Commit

Permalink
Add InputManagerBundle::with_map and fix a mishandling of DualAxis (#…
Browse files Browse the repository at this point in the history
…489)

* Add `InputManagerBundle::with_map` and fix mishandling of DualAxis

* typo

* typo

* typo

* README.md

* Further
  • Loading branch information
Shute052 authored Feb 23, 2024
1 parent cd7f795 commit 1c1f661
Show file tree
Hide file tree
Showing 23 changed files with 160 additions and 220 deletions.
9 changes: 3 additions & 6 deletions README.md
Original file line number Diff line number Diff line change
Expand Up @@ -87,13 +87,10 @@ enum Action {
struct Player;
fn spawn_player(mut commands: Commands) {
// Describes how to convert from player inputs into those actions
let input_map = InputMap::new([(Action::Jump, KeyCode::Space)]);
commands
.spawn(InputManagerBundle::<Action> {
// Stores "which actions are currently pressed"
action_state: ActionState::default(),
// Describes how to convert from player inputs into those actions
input_map: InputMap::new([(Action::Jump, KeyCode::Space)]),
})
.spawn(InputManagerBundle::with_map(input_map))
.insert(Player);
}
Expand Down
18 changes: 14 additions & 4 deletions RELEASES.md
Original file line number Diff line number Diff line change
@@ -1,12 +1,22 @@
# Release Notes

## Version 0.13.3

### Bugs

- fixed a bug where `DualAxis` was being considered pressed even when its data was [0.0, 0.0].

### Usability

- added `InputManagerBundle::with_map(InputMap)` allowing you to create the bundle with the given `InputMap` and default `ActionState`.

## Version 0.13.2

### Usability

- Added `with_threshold()` for const `SingleAxis` creation.
- Added `horizontal_gamepad_face_buttons()` and `vertical_gamepad_face_buttons()` for `VirtualAxis`, similar to `VirtualDpad::gamepad_face_buttons()`.
- Changed various creations of `DualAxis`, `VirtualAxis`, `VirtualDpad` into const functions as they should be:
- added `with_threshold()` for const `SingleAxis` creation.
- added `horizontal_gamepad_face_buttons()` and `vertical_gamepad_face_buttons()` for `VirtualAxis`, similar to `VirtualDpad::gamepad_face_buttons()`.
- changed various creations of `DualAxis`, `VirtualAxis`, `VirtualDpad` into const functions as they should be:
- `left_stick()`, `right_stick()` for `DualAxis`.
- `from_keys()`, `horizontal_arrow_keys()`, `vertical_arrow_keys()`, `ad()`, `ws()`, `horizontal_dpad()`, `vertical_dpad()` for `VirtualAxis`.
- `arrow_keys()`, `wasd()`, `dpad()`, `gamepad_face_buttons()`, `mouse_wheel()`, `mouse_motion()` for `VirtualDpad`.
Expand Down Expand Up @@ -171,7 +181,7 @@
### Enhancements

- Changed `entity` field of `ActionStateDriver` to `targets: ActionStateDriverTarget` with variants for 0, 1, or multiple targets, to allow for one driver
to update multiple entities if needed.
to update multiple entities if needed.
- Added builder-style functions to `SingleAxis`, `DualAxis`, and `VirtualDPad` that invert their output values, allowing, for example, binding inverted camera controls.

### Docs
Expand Down
35 changes: 16 additions & 19 deletions examples/axis_inputs.rs
Original file line number Diff line number Diff line change
Expand Up @@ -25,24 +25,21 @@ enum Action {
struct Player;

fn spawn_player(mut commands: Commands) {
// Describes how to convert from player inputs into those actions
let input_map = InputMap::default()
// Configure the left stick as a dual-axis
.insert(Action::Move, DualAxis::left_stick())
// Let's bind the right gamepad trigger to the throttle action
.insert(Action::Throttle, GamepadButtonType::RightTrigger2)
// And we'll use the right stick's x-axis as a rudder control
.insert(
// This will trigger if the axis is moved 10% or more in either direction.
Action::Rudder,
SingleAxis::symmetric(GamepadAxisType::RightStickX, 0.1),
)
.build();
commands
.spawn(InputManagerBundle::<Action> {
// Stores "which actions are currently activated"
action_state: ActionState::default(),
// Describes how to convert from player inputs into those actions
input_map: InputMap::default()
// Configure the left stick as a dual-axis
.insert(Action::Move, DualAxis::left_stick())
// Let's bind the right gamepad trigger to the throttle action
.insert(Action::Throttle, GamepadButtonType::RightTrigger2)
// And we'll use the right stick's x-axis as a rudder control
.insert(
// This will trigger if the axis is moved 10% or more in either direction.
Action::Rudder,
SingleAxis::symmetric(GamepadAxisType::RightStickX, 0.1),
)
.build(),
})
.spawn(InputManagerBundle::with_map(input_map))
.insert(Player);
}

Expand All @@ -62,8 +59,8 @@ fn move_player(query: Query<&ActionState<Action>, With<Player>>) {

if action_state.pressed(&Action::Throttle) {
// Note that some gamepad buttons are also tied to axes, so even though we used a
// GamepadbuttonType::RightTrigger2 binding to trigger the throttle action, we can get a
// variable value here if you have a variable right trigger on your gamepad.
// GamepadButtonType::RightTrigger2 binding to trigger the throttle action,
// we can get a variable value here if you have a variable right trigger on your gamepad.
//
// If you don't have a variable trigger, this will just return 0.0 when not pressed and 1.0
// when pressed.
Expand Down
5 changes: 1 addition & 4 deletions examples/clash_handling.rs
Original file line number Diff line number Diff line change
Expand Up @@ -44,10 +44,7 @@ fn spawn_input_map(mut commands: Commands) {

input_map.insert_chord(OneAndTwoAndThree, [Digit1, Digit2, Digit3]);

commands.spawn(InputManagerBundle {
input_map,
..Default::default()
});
commands.spawn(InputManagerBundle::with_map(input_map));
}

fn report_pressed_actions(
Expand Down
56 changes: 17 additions & 39 deletions examples/default_controls.rs
Original file line number Diff line number Diff line change
Expand Up @@ -20,55 +20,33 @@ enum PlayerAction {
}

impl PlayerAction {
// The `strum` crate provides a deriveable trait for this!
fn variants() -> &'static [PlayerAction] {
&[Self::Run, Self::Jump, Self::UseItem]
}
}
/// Define the default binding to the input
fn default_input_map() -> InputMap<Self> {
let mut input_map = InputMap::default();

// Exhaustively match `PlayerAction` and define the default binding to the input
impl PlayerAction {
fn default_keyboard_mouse_input(&self) -> UserInput {
// Match against the provided action to get the correct default keyboard-mouse input
match self {
Self::Run => UserInput::VirtualDPad(VirtualDPad::wasd()),
Self::Jump => UserInput::Single(InputKind::PhysicalKey(KeyCode::Space)),
Self::UseItem => UserInput::Single(InputKind::Mouse(MouseButton::Left)),
}
}
// Default gamepad input bindings
input_map.insert(Self::Run, DualAxis::left_stick());
input_map.insert(Self::Jump, GamepadButtonType::South);
input_map.insert(Self::UseItem, GamepadButtonType::RightTrigger2);

fn default_gamepad_input(&self) -> UserInput {
// Match against the provided action to get the correct default gamepad input
match self {
Self::Run => UserInput::Single(InputKind::DualAxis(DualAxis::left_stick())),
Self::Jump => UserInput::Single(InputKind::GamepadButton(GamepadButtonType::South)),
Self::UseItem => {
UserInput::Single(InputKind::GamepadButton(GamepadButtonType::RightTrigger2))
}
}
// Default kbm input bindings
input_map.insert(Self::Run, VirtualDPad::wasd());
input_map.insert(Self::Jump, KeyCode::Space);
input_map.insert(Self::UseItem, MouseButton::Left);

input_map
}
}

#[derive(Component)]
struct Player;

fn spawn_player(mut commands: Commands) {
// Create an `InputMap` to add default inputs to
let mut input_map = InputMap::default();

// Loop through each action in `PlayerAction` and get the default `UserInput`,
// then insert each default input into input_map
for action in PlayerAction::variants() {
input_map.insert(*action, PlayerAction::default_keyboard_mouse_input(action));
input_map.insert(*action, PlayerAction::default_gamepad_input(action));
}

// Spawn the player with the populated input_map
// Spawn the player with the default input_map
commands
.spawn(InputManagerBundle::<PlayerAction> {
input_map,
..default()
})
.spawn(InputManagerBundle::with_map(
PlayerAction::default_input_map(),
))
.insert(Player);
}

Expand Down
9 changes: 3 additions & 6 deletions examples/minimal.rs
Original file line number Diff line number Diff line change
Expand Up @@ -25,13 +25,10 @@ enum Action {
struct Player;

fn spawn_player(mut commands: Commands) {
// Describes how to convert from player inputs into those actions
let input_map = InputMap::new([(Action::Jump, KeyCode::Space)]);
commands
.spawn(InputManagerBundle::<Action> {
// Stores "which actions are currently pressed"
action_state: ActionState::default(),
// Describes how to convert from player inputs into those actions
input_map: InputMap::new([(Action::Jump, KeyCode::Space)]),
})
.spawn(InputManagerBundle::with_map(input_map))
.insert(Player);
}

Expand Down
15 changes: 7 additions & 8 deletions examples/mouse_motion.rs
Original file line number Diff line number Diff line change
Expand Up @@ -16,16 +16,15 @@ enum CameraMovement {
}

fn setup(mut commands: Commands) {
let input_map = InputMap::new([
// This will capture the total continuous value, for direct use.
// Note that you can also use discrete gesture-like motion,
// via the `MouseMotionDirection` enum.
(CameraMovement::Pan, DualAxis::mouse_motion()),
]);
commands
.spawn(Camera2dBundle::default())
.insert(InputManagerBundle::<CameraMovement> {
input_map: InputMap::default()
// This will capture the total continuous value, for direct use.
// Note that you can also use discrete gesture-like motion, via the `MouseMotionDirection` enum.
.insert(CameraMovement::Pan, DualAxis::mouse_motion())
.build(),
..default()
});
.insert(InputManagerBundle::with_map(input_map));

commands.spawn(SpriteBundle {
transform: Transform::from_scale(Vec3::new(100., 100., 1.)),
Expand Down
2 changes: 1 addition & 1 deletion examples/mouse_position.rs
Original file line number Diff line number Diff line change
Expand Up @@ -49,7 +49,7 @@ fn update_cursor_state_from_window(
window_query: Query<(&Window, &ActionStateDriver<BoxMovement>)>,
mut action_state_query: Query<&mut ActionState<BoxMovement>>,
) {
// Update each actionstate with the mouse position from the window
// Update each action state with the mouse position from the window
// by using the referenced entities in ActionStateDriver and the stored action as
// a key into the action data
for (window, driver) in window_query.iter() {
Expand Down
28 changes: 13 additions & 15 deletions examples/mouse_wheel.rs
Original file line number Diff line number Diff line change
Expand Up @@ -19,23 +19,21 @@ enum CameraMovement {
}

fn setup(mut commands: Commands) {
let input_map = InputMap::default()
// This will capture the total continuous value, for direct use.
.insert(CameraMovement::Zoom, SingleAxis::mouse_wheel_y())
// This will return a binary button-like output.
.insert(CameraMovement::PanLeft, MouseWheelDirection::Left)
.insert(CameraMovement::PanRight, MouseWheelDirection::Right)
// Alternatively, you could model this as a virtual Dpad.
// It's extremely useful for modeling 4-directional button-like inputs with the mouse wheel
// .insert(VirtualDpad::mouse_wheel(), Pan)
// Or even a continuous `DualAxis`!
// .insert(DualAxis::mouse_wheel(), Pan)
.build();
commands
.spawn(Camera2dBundle::default())
.insert(InputManagerBundle::<CameraMovement> {
input_map: InputMap::default()
// This will capture the total continuous value, for direct use.
.insert(CameraMovement::Zoom, SingleAxis::mouse_wheel_y())
// This will return a binary button-like output.
.insert(CameraMovement::PanLeft, MouseWheelDirection::Left)
.insert(CameraMovement::PanRight, MouseWheelDirection::Right)
// Alternatively, you could model this as a virtual Dpad.
// It's extremely useful for modeling 4-directional button-like inputs with the mouse wheel
// .insert(VirtualDpad::mouse_wheel(), Pan)
// Or even a continuous `DualAxis`!
// .insert(DualAxis::mouse_wheel(), Pan)
.build(),
..default()
});
.insert(InputManagerBundle::with_map(input_map));

commands.spawn(SpriteBundle {
transform: Transform::from_scale(Vec3::new(100., 100., 1.)),
Expand Down
10 changes: 2 additions & 8 deletions examples/multiplayer.rs
Original file line number Diff line number Diff line change
Expand Up @@ -67,17 +67,11 @@ impl PlayerBundle {
fn spawn_players(mut commands: Commands) {
commands.spawn(PlayerBundle {
player: Player::One,
input_manager: InputManagerBundle {
input_map: PlayerBundle::input_map(Player::One),
..Default::default()
},
input_manager: InputManagerBundle::with_map(PlayerBundle::input_map(Player::One)),
});

commands.spawn(PlayerBundle {
player: Player::Two,
input_manager: InputManagerBundle {
input_map: PlayerBundle::input_map(Player::Two),
..Default::default()
},
input_manager: InputManagerBundle::with_map(PlayerBundle::input_map(Player::Two)),
});
}
5 changes: 1 addition & 4 deletions examples/press_duration.rs
Original file line number Diff line number Diff line change
Expand Up @@ -59,10 +59,7 @@ fn spawn_player(mut commands: Commands) {
commands.spawn(PlayerBundle {
player: Player,
velocity: Velocity { x: 0.0 },
input_manager: InputManagerBundle {
input_map: PlayerBundle::default_input_map(),
..default()
},
input_manager: InputManagerBundle::with_map(PlayerBundle::default_input_map()),
sprite: SpriteBundle {
transform: Transform {
scale: Vec3::new(40.0, 80.0, 0.0),
Expand Down
17 changes: 8 additions & 9 deletions examples/register_gamepads.rs
Original file line number Diff line number Diff line change
Expand Up @@ -44,16 +44,15 @@ fn join(
if !joined_players.0.contains_key(&gamepad) {
println!("Player {} has joined the game!", gamepad.id);

let input_map = InputMap::new([
(Action::Jump, GamepadButtonType::South),
(Action::Disconnect, GamepadButtonType::Select),
])
// Make sure to set the gamepad or all gamepads will be used!
.set_gamepad(gamepad)
.build();
let player = commands
.spawn(InputManagerBundle::<Action> {
action_state: ActionState::default(),
input_map: InputMap::default()
.insert(Action::Jump, GamepadButtonType::South)
.insert(Action::Disconnect, GamepadButtonType::Select)
// Make sure to set the gamepad or all gamepads will be used!
.set_gamepad(gamepad)
.build(),
})
.spawn(InputManagerBundle::with_map(input_map))
.insert(Player { gamepad })
.id();

Expand Down
10 changes: 4 additions & 6 deletions examples/send_actions_over_network.rs
Original file line number Diff line number Diff line change
Expand Up @@ -125,13 +125,11 @@ fn spawn_player(mut commands: Commands) {
use FpsAction::*;
use KeyCode::*;

let input_map = InputMap::new([(MoveLeft, KeyW), (MoveRight, KeyD), (Jump, Space)])
.insert(Shoot, MouseButton::Left)
.build();
commands
.spawn(InputManagerBundle {
input_map: InputMap::new([(MoveLeft, KeyW), (MoveRight, KeyD), (Jump, Space)])
.insert(Shoot, MouseButton::Left)
.build(),
..default()
})
.spawn(InputManagerBundle::with_map(input_map))
.insert(Player);
}

Expand Down
5 changes: 1 addition & 4 deletions examples/single_player.rs
Original file line number Diff line number Diff line change
Expand Up @@ -111,10 +111,7 @@ impl PlayerBundle {
fn spawn_player(mut commands: Commands) {
commands.spawn(PlayerBundle {
player: Player,
input_manager: InputManagerBundle {
input_map: PlayerBundle::default_input_map(),
..default()
},
input_manager: InputManagerBundle::with_map(PlayerBundle::default_input_map()),
});
}

Expand Down
Loading

0 comments on commit 1c1f661

Please sign in to comment.