Skip to content

Commit 9ab020a

Browse files
committed
Add basic support for gamepads
It is now possible to use keyboard/mouse/gamepad in tandem. Also, FpsControllerInput is now stateless and truly represents one frame of input.
1 parent 6ae9101 commit 9ab020a

File tree

1 file changed

+87
-29
lines changed

1 file changed

+87
-29
lines changed

src/controller.rs

+87-29
Original file line numberDiff line numberDiff line change
@@ -1,6 +1,7 @@
11
use std::f32::consts::*;
2+
use std::hash::Hash;
23

3-
use bevy::{input::mouse::MouseMotion, math::Vec3Swizzles, prelude::*};
4+
use bevy::{input::mouse::MouseMotion, prelude::*};
45
use bevy_rapier3d::prelude::*;
56

67
/// Manages the FPS controllers. Executes in `PreUpdate`, after bevy's internal
@@ -33,7 +34,9 @@ impl Plugin for FpsControllerPlugin {
3334
app.add_systems(
3435
PreUpdate,
3536
(
36-
fps_controller_input,
37+
fps_controller_reset_input,
38+
fps_controller_keyboard_mouse_input,
39+
fps_controller_gamepad_input,
3740
fps_controller_look,
3841
fps_controller_move,
3942
fps_controller_render,
@@ -112,7 +115,7 @@ pub struct FpsController {
112115
pub yaw: f32,
113116
pub ground_tick: u8,
114117
pub stop_speed: f32,
115-
pub sensitivity: f32,
118+
pub mouse_sensitivity: f32,
116119
pub enable_input: bool,
117120
pub step_offset: f32,
118121
pub key_forward: KeyCode,
@@ -125,6 +128,17 @@ pub struct FpsController {
125128
pub key_jump: KeyCode,
126129
pub key_fly: KeyCode,
127130
pub key_crouch: KeyCode,
131+
pub pad_move_x: GamepadAxis,
132+
pub pad_move_y: GamepadAxis,
133+
pub pad_look_x: GamepadAxis,
134+
pub pad_look_y: GamepadAxis,
135+
pub pad_fly_up: GamepadButton,
136+
pub pad_fly_down: GamepadButton,
137+
pub pad_jump: GamepadButton,
138+
pub pad_sprint: GamepadButton,
139+
pub pad_fly: GamepadButton,
140+
pub pad_crouch: GamepadButton,
141+
pub pad_sensitivity: f32,
128142
}
129143

130144
impl Default for FpsController {
@@ -171,7 +185,18 @@ impl Default for FpsController {
171185
key_jump: KeyCode::Space,
172186
key_fly: KeyCode::KeyF,
173187
key_crouch: KeyCode::ControlLeft,
174-
sensitivity: 0.001,
188+
mouse_sensitivity: 0.001,
189+
pad_move_x: GamepadAxis::LeftStickX,
190+
pad_move_y: GamepadAxis::LeftStickY,
191+
pad_look_x: GamepadAxis::RightStickX,
192+
pad_look_y: GamepadAxis::RightStickY,
193+
pad_fly_up: GamepadButton::DPadUp,
194+
pad_fly_down: GamepadButton::DPadDown,
195+
pad_jump: GamepadButton::South,
196+
pad_sprint: GamepadButton::LeftThumb,
197+
pad_fly: GamepadButton::RightThumb,
198+
pad_crouch: GamepadButton::East,
199+
pad_sensitivity: 0.025,
175200
}
176201
}
177202
}
@@ -188,7 +213,13 @@ const ANGLE_EPSILON: f32 = 0.001953125;
188213

189214
const SLIGHT_SCALE_DOWN: f32 = 0.9375;
190215

191-
pub fn fps_controller_input(
216+
pub fn fps_controller_reset_input(mut query: Query<&mut FpsControllerInput>) {
217+
for mut input in query.iter_mut() {
218+
*input = default();
219+
}
220+
}
221+
222+
pub fn fps_controller_keyboard_mouse_input(
192223
key_input: Res<ButtonInput<KeyCode>>,
193224
mut mouse_events: EventReader<MouseMotion>,
194225
mut query: Query<(&FpsController, &mut FpsControllerInput)>,
@@ -201,7 +232,7 @@ pub fn fps_controller_input(
201232
for mouse_event in mouse_events.read() {
202233
mouse_delta += mouse_event.delta;
203234
}
204-
mouse_delta *= controller.sensitivity;
235+
mouse_delta *= controller.mouse_sensitivity;
205236

206237
input.pitch = (input.pitch - mouse_delta.y)
207238
.clamp(-FRAC_PI_2 + ANGLE_EPSILON, FRAC_PI_2 - ANGLE_EPSILON);
@@ -210,22 +241,57 @@ pub fn fps_controller_input(
210241
input.yaw = input.yaw.rem_euclid(TAU);
211242
}
212243

213-
input.movement = Vec3::new(
214-
get_axis(&key_input, controller.key_right, controller.key_left),
215-
get_axis(&key_input, controller.key_up, controller.key_down),
216-
get_axis(&key_input, controller.key_forward, controller.key_back),
244+
input.movement += Vec3::new(
245+
to_axis(&key_input, controller.key_right, controller.key_left),
246+
to_axis(&key_input, controller.key_up, controller.key_down),
247+
to_axis(&key_input, controller.key_forward, controller.key_back),
217248
);
218-
input.sprint = key_input.pressed(controller.key_sprint);
219-
input.jump = key_input.pressed(controller.key_jump);
220-
input.fly = key_input.just_pressed(controller.key_fly);
221-
input.crouch = key_input.pressed(controller.key_crouch);
249+
input.sprint = input.sprint || key_input.pressed(controller.key_sprint);
250+
input.jump = input.jump || key_input.pressed(controller.key_jump);
251+
input.fly = input.fly || key_input.just_pressed(controller.key_fly);
252+
input.crouch = input.crouch || key_input.pressed(controller.key_crouch);
253+
}
254+
}
255+
256+
pub fn fps_controller_gamepad_input(
257+
gamepads: Query<&Gamepad>,
258+
mut query: Query<(&FpsController, &mut FpsControllerInput)>,
259+
) {
260+
for gamepad in gamepads.iter() {
261+
262+
for (controller, mut input) in query.iter_mut() {
263+
if !controller.enable_input {
264+
continue;
265+
}
266+
267+
// Helper function to get axis values
268+
let axis = |axis_type| gamepad.get(axis_type).unwrap();
269+
let move_vec = Vec2::new(axis(controller.pad_move_x), axis(controller.pad_move_y));
270+
let look_vec = Vec2::new(axis(controller.pad_look_x), axis(controller.pad_look_y))
271+
* controller.pad_sensitivity;
272+
273+
input.pitch = (input.pitch + look_vec.y)
274+
.clamp(-FRAC_PI_2 + ANGLE_EPSILON, FRAC_PI_2 - ANGLE_EPSILON);
275+
input.yaw -= look_vec.x;
276+
if input.yaw.abs() > PI {
277+
input.yaw = input.yaw.rem_euclid(TAU);
278+
}
279+
280+
let vertical_axis = to_axis(gamepad.digital(), controller.pad_fly_up, controller.pad_fly_down);
281+
282+
input.movement += Vec3::new(move_vec.x, vertical_axis, move_vec.y);
283+
input.sprint = input.sprint || gamepad.pressed(controller.pad_sprint);
284+
input.jump = input.jump || gamepad.pressed(controller.pad_jump);
285+
input.fly = input.fly || gamepad.just_pressed(controller.pad_fly);
286+
input.crouch = input.crouch || gamepad.pressed(controller.pad_crouch);
287+
}
222288
}
223289
}
224290

225291
pub fn fps_controller_look(mut query: Query<(&mut FpsController, &FpsControllerInput)>) {
226292
for (mut controller, input) in query.iter_mut() {
227-
controller.pitch = input.pitch;
228-
controller.yaw = input.yaw;
293+
controller.pitch += input.pitch;
294+
controller.yaw += input.yaw;
229295
}
230296
}
231297

@@ -267,8 +333,7 @@ pub fn fps_controller_move(
267333
} else {
268334
controller.fly_speed
269335
};
270-
let mut move_to_world =
271-
Mat3::from_euler(EulerRot::YXZ, input.yaw, input.pitch, 0.0);
336+
let mut move_to_world = Mat3::from_euler(EulerRot::YXZ, controller.yaw, controller.pitch, 0.0);
272337
move_to_world.z_axis *= -1.0; // Forward is -Z
273338
move_to_world.y_axis = Vec3::Y; // Vertical movement aligned with world up
274339
velocity.linvel = move_to_world * input.movement * fly_speed;
@@ -291,7 +356,7 @@ pub fn fps_controller_move(
291356
);
292357

293358
let speeds = Vec3::new(controller.side_speed, 0.0, controller.forward_speed);
294-
let mut move_to_world = Mat3::from_axis_angle(Vec3::Y, input.yaw);
359+
let mut move_to_world = Mat3::from_axis_angle(Vec3::Y, controller.yaw);
295360
move_to_world.z_axis *= -1.0; // Forward is -Z
296361
let mut wish_direction = move_to_world * (input.movement * speeds);
297362
let mut wish_speed = wish_direction.length();
@@ -561,16 +626,9 @@ fn acceleration(
561626
wish_direction * acceleration_speed
562627
}
563628

564-
fn get_pressed(key_input: &Res<ButtonInput<KeyCode>>, key: KeyCode) -> f32 {
565-
if key_input.pressed(key) {
566-
1.0
567-
} else {
568-
0.0
569-
}
570-
}
571-
572-
fn get_axis(key_input: &Res<ButtonInput<KeyCode>>, key_pos: KeyCode, key_neg: KeyCode) -> f32 {
573-
get_pressed(key_input, key_pos) - get_pressed(key_input, key_neg)
629+
/// Converts two button inputs into an f32 with a range of [-1, 1]
630+
fn to_axis<T: Copy + Eq + Send + Sync + Hash>(input: &ButtonInput<T>, pos: T, neg: T) -> f32 {
631+
input.pressed(pos) as u8 as f32 - input.pressed(neg) as u8 as f32
574632
}
575633

576634
// ██████╗ ███████╗███╗ ██╗██████╗ ███████╗██████╗

0 commit comments

Comments
 (0)