smooth_bevy_cameras/controllers/
unreal.rs1use crate::{LookAngles, LookTransform, LookTransformBundle, Smoother};
2
3use bevy::{
4 app::prelude::*,
5 ecs::{bundle::Bundle, prelude::*},
6 input::{
7 mouse::{MouseMotion, MouseWheel},
8 prelude::*,
9 },
10 math::prelude::*,
11 prelude::ReflectDefault,
12 reflect::Reflect,
13 time::Time,
14 transform::components::Transform,
15};
16
17#[derive(Default)]
18pub struct UnrealCameraPlugin {
19 pub override_input_system: bool,
20}
21
22impl UnrealCameraPlugin {
23 pub fn new(override_input_system: bool) -> Self {
24 Self {
25 override_input_system,
26 }
27 }
28}
29
30impl Plugin for UnrealCameraPlugin {
31 fn build(&self, app: &mut App) {
32 let app = app
33 .add_systems(PreUpdate, on_controller_enabled_changed)
34 .add_systems(Update, control_system)
35 .add_event::<ControlEvent>();
36 if !self.override_input_system {
37 app.add_systems(Update, default_input_map);
38 }
39 }
40}
41
42#[derive(Bundle)]
43pub struct UnrealCameraBundle {
44 controller: UnrealCameraController,
45 look_transform: LookTransformBundle,
46 transform: Transform,
47}
48
49impl UnrealCameraBundle {
50 pub fn new(controller: UnrealCameraController, eye: Vec3, target: Vec3, up: Vec3) -> Self {
51 let transform = Transform::from_translation(eye).looking_at(target, up);
53
54 Self {
55 controller,
56 look_transform: LookTransformBundle {
57 transform: LookTransform::new(eye, target, up),
58 smoother: Smoother::new(controller.smoothing_weight),
59 },
60 transform,
61 }
62 }
63}
64
65#[derive(Clone, Component, Copy, Debug, Reflect)]
67#[cfg_attr(feature = "serde", derive(serde::Deserialize, serde::Serialize))]
68#[reflect(Component, Default, Debug)]
69pub struct UnrealCameraController {
70 pub enabled: bool,
72
73 pub rotate_sensitivity: Vec2,
75
76 pub mouse_translate_sensitivity: Vec2,
78
79 pub wheel_translate_sensitivity: f32,
81
82 pub keyboard_mvmt_sensitivity: f32,
85
86 pub keyboard_mvmt_wheel_sensitivity: f32,
88
89 pub smoothing_weight: f32,
91}
92
93impl Default for UnrealCameraController {
94 fn default() -> Self {
95 Self {
96 enabled: true,
97 rotate_sensitivity: Vec2::splat(0.2),
98 mouse_translate_sensitivity: Vec2::splat(2.0),
99 wheel_translate_sensitivity: 50.0,
100 keyboard_mvmt_sensitivity: 10.0,
101 keyboard_mvmt_wheel_sensitivity: 5.0,
102 smoothing_weight: 0.7,
103 }
104 }
105}
106
107#[derive(Event)]
108pub enum ControlEvent {
109 Locomotion(Vec2),
110 Rotate(Vec2),
111 TranslateEye(Vec2),
112}
113
114define_on_controller_enabled_changed!(UnrealCameraController);
115
116pub fn default_input_map(
117 mut events: EventWriter<ControlEvent>,
118 mut mouse_wheel_reader: EventReader<MouseWheel>,
119 mut mouse_motion_events: EventReader<MouseMotion>,
120 keyboard: Res<ButtonInput<KeyCode>>,
121 mouse_buttons: Res<ButtonInput<MouseButton>>,
122 mut controllers: Query<&mut UnrealCameraController>,
123) {
124 let mut controller = if let Some(controller) = controllers.iter_mut().find(|c| c.enabled) {
126 controller
127 } else {
128 return;
129 };
130 let UnrealCameraController {
131 rotate_sensitivity: mouse_rotate_sensitivity,
132 mouse_translate_sensitivity,
133 wheel_translate_sensitivity,
134 mut keyboard_mvmt_sensitivity,
135 keyboard_mvmt_wheel_sensitivity,
136 ..
137 } = *controller;
138
139 let left_pressed = mouse_buttons.pressed(MouseButton::Left);
140 let right_pressed = mouse_buttons.pressed(MouseButton::Right);
141 let middle_pressed = mouse_buttons.pressed(MouseButton::Middle);
142
143 let mut cursor_delta = Vec2::ZERO;
144 for event in mouse_motion_events.read() {
145 cursor_delta += event.delta;
146 }
147
148 let mut wheel_delta = 0.0;
149 for event in mouse_wheel_reader.read() {
150 wheel_delta += event.x + event.y;
151 }
152
153 let mut panning_dir = Vec2::ZERO;
154 let mut translation_dir = Vec2::ZERO; for key in keyboard.get_pressed() {
157 match key {
158 KeyCode::KeyE => {
159 panning_dir.y += 1.0;
160 }
161
162 KeyCode::KeyQ => {
163 panning_dir.y -= 1.0;
164 }
165
166 KeyCode::KeyA => {
167 panning_dir.x -= 1.0;
168 }
169
170 KeyCode::KeyD => {
171 panning_dir.x += 1.0;
172 }
173
174 KeyCode::KeyS => {
175 translation_dir.y -= 1.0;
176 }
177
178 KeyCode::KeyW => {
179 translation_dir.y += 1.0;
180 }
181
182 _ => {}
183 }
184 }
185
186 let mut panning = Vec2::ZERO;
187 let mut locomotion = Vec2::ZERO;
188
189 if left_pressed || middle_pressed || right_pressed {
192 panning += keyboard_mvmt_sensitivity * panning_dir;
193
194 if translation_dir.y != 0.0 {
195 locomotion.y += keyboard_mvmt_sensitivity * translation_dir.y;
196 }
197
198 keyboard_mvmt_sensitivity += keyboard_mvmt_wheel_sensitivity * wheel_delta;
199 controller.keyboard_mvmt_sensitivity = keyboard_mvmt_sensitivity.max(0.01);
200 }
201 else if wheel_delta != 0.0 {
203 locomotion.y += wheel_translate_sensitivity * wheel_delta;
204 }
205
206 if middle_pressed || (left_pressed && right_pressed) {
208 panning += mouse_translate_sensitivity * cursor_delta;
209 }
210
211 if left_pressed && !middle_pressed && !right_pressed {
213 locomotion.x = mouse_rotate_sensitivity.x * cursor_delta.x;
214 locomotion.y -= mouse_translate_sensitivity.y * cursor_delta.y;
215 }
216
217 if !left_pressed && !middle_pressed && right_pressed {
218 events.write(ControlEvent::Rotate(
219 mouse_rotate_sensitivity * cursor_delta,
220 ));
221 }
222
223 if panning.length_squared() > 0.0 {
224 events.write(ControlEvent::TranslateEye(panning));
225 }
226
227 if locomotion.length_squared() > 0.0 {
228 events.write(ControlEvent::Locomotion(locomotion));
229 }
230}
231
232pub fn control_system(
233 time: Res<Time>,
234 mut events: EventReader<ControlEvent>,
235 mut cameras: Query<(&UnrealCameraController, &mut LookTransform)>,
236) {
237 let mut transform = if let Some((_, transform)) = cameras.iter_mut().find(|c| c.0.enabled) {
239 transform
240 } else {
241 return;
242 };
243
244 let look_vector = match transform.look_direction() {
245 Some(safe_look_vector) => safe_look_vector,
246 None => return,
247 };
248 let mut look_angles = LookAngles::from_vector(look_vector);
249
250 let dt = time.delta_secs();
251 for event in events.read() {
252 match event {
253 ControlEvent::Locomotion(delta) => {
254 look_angles.add_yaw(dt * -delta.x);
256 transform.eye += dt * delta.y * look_vector;
257 }
258 ControlEvent::Rotate(delta) => {
259 look_angles.add_yaw(dt * -delta.x);
261 look_angles.add_pitch(dt * -delta.y);
262 }
263 ControlEvent::TranslateEye(delta) => {
264 let yaw_rot = Quat::from_axis_angle(Vec3::Y, look_angles.get_yaw());
265 let rot_x = yaw_rot * Vec3::X;
266
267 let up = transform.up;
269 transform.eye -= dt * delta.x * rot_x - dt * delta.y * up;
270 }
271 }
272 }
273
274 look_angles.assert_not_looking_up();
275
276 transform.target = transform.eye + transform.radius() * look_angles.unit_vector();
277}