decal/helpers/
camera_controller.rs1use bevy::{
9 input::mouse::{AccumulatedMouseMotion, AccumulatedMouseScroll, MouseScrollUnit},
10 prelude::*,
11 window::CursorGrabMode,
12};
13use std::{f32::consts::*, fmt};
14
15pub struct CameraControllerPlugin;
17
18impl Plugin for CameraControllerPlugin {
19 fn build(&self, app: &mut App) {
20 app.add_systems(Update, run_camera_controller);
21 }
22}
23
24pub const RADIANS_PER_DOT: f32 = 1.0 / 180.0;
28
29#[derive(Component)]
31pub struct CameraController {
32 pub enabled: bool,
34 pub initialized: bool,
36 pub sensitivity: f32,
38 pub key_forward: KeyCode,
40 pub key_back: KeyCode,
42 pub key_left: KeyCode,
44 pub key_right: KeyCode,
46 pub key_up: KeyCode,
48 pub key_down: KeyCode,
50 pub key_run: KeyCode,
53 pub mouse_key_cursor_grab: MouseButton,
55 pub keyboard_key_toggle_cursor_grab: KeyCode,
57 pub walk_speed: f32,
59 pub run_speed: f32,
61 pub scroll_factor: f32,
64 pub friction: f32,
66 pub pitch: f32,
68 pub yaw: f32,
70 pub velocity: Vec3,
72}
73
74impl Default for CameraController {
75 fn default() -> Self {
76 Self {
77 enabled: true,
78 initialized: false,
79 sensitivity: 1.0,
80 key_forward: KeyCode::KeyW,
81 key_back: KeyCode::KeyS,
82 key_left: KeyCode::KeyA,
83 key_right: KeyCode::KeyD,
84 key_up: KeyCode::KeyE,
85 key_down: KeyCode::KeyQ,
86 key_run: KeyCode::ShiftLeft,
87 mouse_key_cursor_grab: MouseButton::Left,
88 keyboard_key_toggle_cursor_grab: KeyCode::KeyM,
89 walk_speed: 5.0,
90 run_speed: 15.0,
91 scroll_factor: 0.1,
92 friction: 0.5,
93 pitch: 0.0,
94 yaw: 0.0,
95 velocity: Vec3::ZERO,
96 }
97 }
98}
99
100impl fmt::Display for CameraController {
101 fn fmt(&self, f: &mut fmt::Formatter<'_>) -> fmt::Result {
102 write!(
103 f,
104 "
105Freecam Controls:
106 Mouse\t- Move camera orientation
107 Scroll\t- Adjust movement speed
108 {:?}\t- Hold to grab cursor
109 {:?}\t- Toggle cursor grab
110 {:?} & {:?}\t- Fly forward & backwards
111 {:?} & {:?}\t- Fly sideways left & right
112 {:?} & {:?}\t- Fly up & down
113 {:?}\t- Fly faster while held",
114 self.mouse_key_cursor_grab,
115 self.keyboard_key_toggle_cursor_grab,
116 self.key_forward,
117 self.key_back,
118 self.key_left,
119 self.key_right,
120 self.key_up,
121 self.key_down,
122 self.key_run,
123 )
124 }
125}
126
127fn run_camera_controller(
128 time: Res<Time>,
129 mut windows: Query<&mut Window>,
130 accumulated_mouse_motion: Res<AccumulatedMouseMotion>,
131 accumulated_mouse_scroll: Res<AccumulatedMouseScroll>,
132 mouse_button_input: Res<ButtonInput<MouseButton>>,
133 key_input: Res<ButtonInput<KeyCode>>,
134 mut toggle_cursor_grab: Local<bool>,
135 mut mouse_cursor_grab: Local<bool>,
136 mut query: Query<(&mut Transform, &mut CameraController), With<Camera>>,
137) {
138 let dt = time.delta_secs();
139
140 let Ok((mut transform, mut controller)) = query.single_mut() else {
141 return;
142 };
143
144 if !controller.initialized {
145 let (yaw, pitch, _roll) = transform.rotation.to_euler(EulerRot::YXZ);
146 controller.yaw = yaw;
147 controller.pitch = pitch;
148 controller.initialized = true;
149 info!("{}", *controller);
150 }
151 if !controller.enabled {
152 return;
153 }
154
155 let mut scroll = 0.0;
156
157 let amount = match accumulated_mouse_scroll.unit {
158 MouseScrollUnit::Line => accumulated_mouse_scroll.delta.y,
159 MouseScrollUnit::Pixel => accumulated_mouse_scroll.delta.y / 16.0,
160 };
161 scroll += amount;
162 controller.walk_speed += scroll * controller.scroll_factor * controller.walk_speed;
163 controller.run_speed = controller.walk_speed * 3.0;
164
165 let mut axis_input = Vec3::ZERO;
167 if key_input.pressed(controller.key_forward) {
168 axis_input.z += 1.0;
169 }
170 if key_input.pressed(controller.key_back) {
171 axis_input.z -= 1.0;
172 }
173 if key_input.pressed(controller.key_right) {
174 axis_input.x += 1.0;
175 }
176 if key_input.pressed(controller.key_left) {
177 axis_input.x -= 1.0;
178 }
179 if key_input.pressed(controller.key_up) {
180 axis_input.y += 1.0;
181 }
182 if key_input.pressed(controller.key_down) {
183 axis_input.y -= 1.0;
184 }
185
186 let mut cursor_grab_change = false;
187 if key_input.just_pressed(controller.keyboard_key_toggle_cursor_grab) {
188 *toggle_cursor_grab = !*toggle_cursor_grab;
189 cursor_grab_change = true;
190 }
191 if mouse_button_input.just_pressed(controller.mouse_key_cursor_grab) {
192 *mouse_cursor_grab = true;
193 cursor_grab_change = true;
194 }
195 if mouse_button_input.just_released(controller.mouse_key_cursor_grab) {
196 *mouse_cursor_grab = false;
197 cursor_grab_change = true;
198 }
199 let cursor_grab = *mouse_cursor_grab || *toggle_cursor_grab;
200
201 if axis_input != Vec3::ZERO {
203 let max_speed = if key_input.pressed(controller.key_run) {
204 controller.run_speed
205 } else {
206 controller.walk_speed
207 };
208 controller.velocity = axis_input.normalize() * max_speed;
209 } else {
210 let friction = controller.friction.clamp(0.0, 1.0);
211 controller.velocity *= 1.0 - friction;
212 if controller.velocity.length_squared() < 1e-6 {
213 controller.velocity = Vec3::ZERO;
214 }
215 }
216 let forward = *transform.forward();
217 let right = *transform.right();
218 transform.translation += controller.velocity.x * dt * right
219 + controller.velocity.y * dt * Vec3::Y
220 + controller.velocity.z * dt * forward;
221
222 if cursor_grab_change {
224 if cursor_grab {
225 for mut window in &mut windows {
226 if !window.focused {
227 continue;
228 }
229
230 window.cursor_options.grab_mode = CursorGrabMode::Locked;
231 window.cursor_options.visible = false;
232 }
233 } else {
234 for mut window in &mut windows {
235 window.cursor_options.grab_mode = CursorGrabMode::None;
236 window.cursor_options.visible = true;
237 }
238 }
239 }
240
241 if accumulated_mouse_motion.delta != Vec2::ZERO && cursor_grab {
243 controller.pitch = (controller.pitch
245 - accumulated_mouse_motion.delta.y * RADIANS_PER_DOT * controller.sensitivity)
246 .clamp(-PI / 2., PI / 2.);
247 controller.yaw -=
248 accumulated_mouse_motion.delta.x * RADIANS_PER_DOT * controller.sensitivity;
249 transform.rotation = Quat::from_euler(EulerRot::ZYX, 0.0, controller.yaw, controller.pitch);
250 }
251}