Skip to content

Commit e50eed7

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 a2ed29b commit e50eed7

File tree

1 file changed

+87
-28
lines changed

1 file changed

+87
-28
lines changed

src/controller.rs

+87-28
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,
@@ -110,7 +113,7 @@ pub struct FpsController {
110113
pub yaw: f32,
111114
pub ground_tick: u8,
112115
pub stop_speed: f32,
113-
pub sensitivity: f32,
116+
pub mouse_sensitivity: f32,
114117
pub enable_input: bool,
115118
pub step_offset: f32,
116119
pub key_forward: KeyCode,
@@ -123,6 +126,17 @@ pub struct FpsController {
123126
pub key_jump: KeyCode,
124127
pub key_fly: KeyCode,
125128
pub key_crouch: KeyCode,
129+
pub pad_move_x: GamepadAxis,
130+
pub pad_move_y: GamepadAxis,
131+
pub pad_look_x: GamepadAxis,
132+
pub pad_look_y: GamepadAxis,
133+
pub pad_fly_up: GamepadButton,
134+
pub pad_fly_down: GamepadButton,
135+
pub pad_jump: GamepadButton,
136+
pub pad_sprint: GamepadButton,
137+
pub pad_fly: GamepadButton,
138+
pub pad_crouch: GamepadButton,
139+
pub pad_sensitivity: f32,
126140
}
127141

128142
impl Default for FpsController {
@@ -168,7 +182,18 @@ impl Default for FpsController {
168182
key_jump: KeyCode::Space,
169183
key_fly: KeyCode::KeyF,
170184
key_crouch: KeyCode::ControlLeft,
171-
sensitivity: 0.001,
185+
mouse_sensitivity: 0.001,
186+
pad_move_x: GamepadAxis::LeftStickX,
187+
pad_move_y: GamepadAxis::LeftStickY,
188+
pad_look_x: GamepadAxis::RightStickX,
189+
pad_look_y: GamepadAxis::RightStickY,
190+
pad_fly_up: GamepadButton::DPadUp,
191+
pad_fly_down: GamepadButton::DPadDown,
192+
pad_jump: GamepadButton::South,
193+
pad_sprint: GamepadButton::LeftThumb,
194+
pad_fly: GamepadButton::RightThumb,
195+
pad_crouch: GamepadButton::East,
196+
pad_sensitivity: 0.025,
172197
}
173198
}
174199
}
@@ -188,7 +213,13 @@ const GROUNDED_DISTANCE: f32 = 0.125;
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)>,
@@ -199,7 +230,7 @@ pub fn fps_controller_input(
199230
for mouse_event in mouse_events.read() {
200231
mouse_delta += mouse_event.delta;
201232
}
202-
mouse_delta *= controller.sensitivity;
233+
mouse_delta *= controller.mouse_sensitivity;
203234

204235
input.pitch = (input.pitch - mouse_delta.y)
205236
.clamp(-FRAC_PI_2 + ANGLE_EPSILON, FRAC_PI_2 - ANGLE_EPSILON);
@@ -208,22 +239,57 @@ pub fn fps_controller_input(
208239
input.yaw = input.yaw.rem_euclid(TAU);
209240
}
210241

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

223289
pub fn fps_controller_look(mut query: Query<(&mut FpsController, &FpsControllerInput)>) {
224290
for (mut controller, input) in query.iter_mut() {
225-
controller.pitch = input.pitch;
226-
controller.yaw = input.yaw;
291+
controller.pitch += input.pitch;
292+
controller.yaw += input.yaw;
227293
}
228294
}
229295

@@ -265,7 +331,7 @@ pub fn fps_controller_move(
265331
} else {
266332
controller.fly_speed
267333
};
268-
let mut move_to_world = Mat3::from_euler(EulerRot::YXZ, input.yaw, input.pitch, 0.0);
334+
let mut move_to_world = Mat3::from_euler(EulerRot::YXZ, controller.yaw, controller.pitch, 0.0);
269335
move_to_world.z_axis *= -1.0; // Forward is -Z
270336
move_to_world.y_axis = Vec3::Y; // Vertical movement aligned with world up
271337
velocity.linvel = move_to_world * input.movement * fly_speed;
@@ -288,7 +354,7 @@ pub fn fps_controller_move(
288354
);
289355

290356
let speeds = Vec3::new(controller.side_speed, 0.0, controller.forward_speed);
291-
let mut move_to_world = Mat3::from_axis_angle(Vec3::Y, input.yaw);
357+
let mut move_to_world = Mat3::from_axis_angle(Vec3::Y, controller.yaw);
292358
move_to_world.z_axis *= -1.0; // Forward is -Z
293359
let mut wish_direction = move_to_world * (input.movement * speeds);
294360
let mut wish_speed = wish_direction.length();
@@ -543,16 +609,9 @@ fn acceleration(
543609
wish_direction * acceleration_speed
544610
}
545611

546-
fn get_pressed(key_input: &Res<ButtonInput<KeyCode>>, key: KeyCode) -> f32 {
547-
if key_input.pressed(key) {
548-
1.0
549-
} else {
550-
0.0
551-
}
552-
}
553-
554-
fn get_axis(key_input: &Res<ButtonInput<KeyCode>>, key_pos: KeyCode, key_neg: KeyCode) -> f32 {
555-
get_pressed(key_input, key_pos) - get_pressed(key_input, key_neg)
612+
/// Converts two button inputs into an f32 with a range of [-1, 1]
613+
fn to_axis<T: Copy + Eq + Send + Sync + Hash>(input: &ButtonInput<T>, pos: T, neg: T) -> f32 {
614+
input.pressed(pos) as u8 as f32 - input.pressed(neg) as u8 as f32
556615
}
557616

558617
// ██████╗ ███████╗███╗ ██╗██████╗ ███████╗██████╗

0 commit comments

Comments
 (0)