smooth_bevy_cameras/controllers/
fps.rs

1use crate::{LookAngles, LookTransform, LookTransformBundle, Smoother};
2
3use bevy::{
4    app::prelude::*,
5    ecs::prelude::*,
6    input::{mouse::MouseMotion, prelude::*},
7    math::prelude::*,
8    prelude::ReflectDefault,
9    reflect::Reflect,
10    time::Time,
11    transform::components::Transform,
12};
13
14#[derive(Default)]
15pub struct FpsCameraPlugin {
16    pub override_input_system: bool,
17}
18
19impl FpsCameraPlugin {
20    pub fn new(override_input_system: bool) -> Self {
21        Self {
22            override_input_system,
23        }
24    }
25}
26
27impl Plugin for FpsCameraPlugin {
28    fn build(&self, app: &mut App) {
29        let app = app
30            .add_systems(PreUpdate, on_controller_enabled_changed)
31            .add_systems(Update, control_system)
32            .add_event::<ControlEvent>();
33
34        if !self.override_input_system {
35            app.add_systems(Update, default_input_map);
36        }
37    }
38}
39
40#[derive(Bundle)]
41pub struct FpsCameraBundle {
42    controller: FpsCameraController,
43    look_transform: LookTransformBundle,
44    transform: Transform,
45}
46
47impl FpsCameraBundle {
48    pub fn new(controller: FpsCameraController, eye: Vec3, target: Vec3, up: Vec3) -> Self {
49        // Make sure the transform is consistent with the controller to start.
50        let transform = Transform::from_translation(eye).looking_at(target, up);
51
52        Self {
53            controller,
54            look_transform: LookTransformBundle {
55                transform: LookTransform::new(eye, target, up),
56                smoother: Smoother::new(controller.smoothing_weight),
57            },
58            transform,
59        }
60    }
61}
62
63/// Your typical first-person camera controller.
64#[derive(Clone, Component, Copy, Debug, Reflect)]
65#[cfg_attr(feature = "serde", derive(serde::Deserialize, serde::Serialize))]
66#[reflect(Component, Default, Debug)]
67pub struct FpsCameraController {
68    pub enabled: bool,
69    pub mouse_rotate_sensitivity: Vec2,
70    pub translate_sensitivity: f32,
71    pub smoothing_weight: f32,
72}
73
74impl Default for FpsCameraController {
75    fn default() -> Self {
76        Self {
77            enabled: true,
78            mouse_rotate_sensitivity: Vec2::splat(0.2),
79            translate_sensitivity: 2.0,
80            smoothing_weight: 0.9,
81        }
82    }
83}
84
85#[derive(Event)]
86pub enum ControlEvent {
87    Rotate(Vec2),
88    TranslateEye(Vec3),
89}
90
91define_on_controller_enabled_changed!(FpsCameraController);
92
93pub fn default_input_map(
94    mut events: EventWriter<ControlEvent>,
95    keyboard: Res<ButtonInput<KeyCode>>,
96    mut mouse_motion_events: EventReader<MouseMotion>,
97    controllers: Query<&FpsCameraController>,
98) {
99    // Can only control one camera at a time.
100    let controller = if let Some(controller) = controllers.iter().find(|c| c.enabled) {
101        controller
102    } else {
103        return;
104    };
105    let FpsCameraController {
106        translate_sensitivity,
107        mouse_rotate_sensitivity,
108        ..
109    } = *controller;
110
111    let mut cursor_delta = Vec2::ZERO;
112    for event in mouse_motion_events.read() {
113        cursor_delta += event.delta;
114    }
115
116    events.write(ControlEvent::Rotate(
117        mouse_rotate_sensitivity * cursor_delta,
118    ));
119
120    for (key, dir) in [
121        (KeyCode::KeyW, Vec3::Z),
122        (KeyCode::KeyA, Vec3::X),
123        (KeyCode::KeyS, -Vec3::Z),
124        (KeyCode::KeyD, -Vec3::X),
125        (KeyCode::ShiftLeft, -Vec3::Y),
126        (KeyCode::Space, Vec3::Y),
127    ]
128    .iter()
129    .cloned()
130    {
131        if keyboard.pressed(key) {
132            events.write(ControlEvent::TranslateEye(translate_sensitivity * dir));
133        }
134    }
135}
136
137pub fn control_system(
138    mut events: EventReader<ControlEvent>,
139    mut cameras: Query<(&FpsCameraController, &mut LookTransform)>,
140    time: Res<Time>,
141) {
142    // Can only control one camera at a time.
143    let mut transform = if let Some((_, transform)) = cameras.iter_mut().find(|c| c.0.enabled) {
144        transform
145    } else {
146        return;
147    };
148
149    let look_vector = transform.look_direction().unwrap();
150    let mut look_angles = LookAngles::from_vector(look_vector);
151
152    let yaw_rot = Quat::from_axis_angle(Vec3::Y, look_angles.get_yaw());
153    let rot_x = yaw_rot * Vec3::X;
154    let rot_y = yaw_rot * Vec3::Y;
155    let rot_z = yaw_rot * Vec3::Z;
156
157    let dt = time.delta_secs();
158    for event in events.read() {
159        match event {
160            ControlEvent::Rotate(delta) => {
161                // Rotates with pitch and yaw.
162                look_angles.add_yaw(dt * -delta.x);
163                look_angles.add_pitch(dt * -delta.y);
164            }
165            ControlEvent::TranslateEye(delta) => {
166                // Translates up/down (Y) left/right (X) and forward/back (Z).
167                transform.eye += dt * delta.x * rot_x + dt * delta.y * rot_y + dt * delta.z * rot_z;
168            }
169        }
170    }
171
172    look_angles.assert_not_looking_up();
173
174    transform.target = transform.eye + transform.radius() * look_angles.unit_vector();
175}