1use fyrox::{
25 core::{
26 algebra::{UnitQuaternion, UnitVector3, Vector3},
27 impl_component_provider,
28 math::curve::{Curve, CurveKey, CurveKeyKind},
29 math::Vector3Ext,
30 reflect::prelude::*,
31 uuid_provider,
32 variable::InheritableVariable,
33 visitor::prelude::*,
34 },
35 event::{DeviceEvent, ElementState, Event, WindowEvent},
36 gui::{key::KeyBinding, message::KeyCode},
37 script::{ScriptContext, ScriptTrait},
38 utils,
39};
40use std::ops::Range;
41
42#[derive(Visit, Reflect, Debug, Clone)]
46pub struct FlyingCameraController {
47 #[reflect(description = "Current yaw of the camera pivot (in radians).")]
48 #[visit(optional)]
49 pub yaw: InheritableVariable<f32>,
50
51 #[reflect(description = "Current pitch of the camera (in radians).")]
52 #[visit(optional)]
53 pub pitch: InheritableVariable<f32>,
54
55 #[reflect(description = "Maximum speed of the camera.")]
56 #[visit(optional)]
57 pub speed: InheritableVariable<f32>,
58
59 #[reflect(description = "Mouse sensitivity.")]
60 #[visit(optional)]
61 pub sensitivity: InheritableVariable<f32>,
62
63 #[reflect(description = "Angular limit of the pitch of the camera (in radians).")]
64 #[visit(optional)]
65 pub pitch_limit: InheritableVariable<Range<f32>>,
66
67 #[reflect(description = "A key, that corresponds to forward movement.")]
71 #[visit(optional)]
72 pub move_forward_key: InheritableVariable<KeyBinding>,
73
74 #[reflect(description = "A key, that corresponds to backward movement.")]
75 #[visit(optional)]
76 pub move_backward_key: InheritableVariable<KeyBinding>,
77
78 #[reflect(description = "A key, that corresponds to left movement.")]
79 #[visit(optional)]
80 pub move_left_key: InheritableVariable<KeyBinding>,
81
82 #[reflect(description = "A key, that corresponds to right movement.")]
83 #[visit(optional)]
84 pub move_right_key: InheritableVariable<KeyBinding>,
85
86 #[reflect(
87 description = "A curve, that defines a how speed of the camera changes when accelerating to the \
88 max speed."
89 )]
90 #[visit(optional)]
91 pub acceleration_curve: InheritableVariable<Curve>,
92
93 #[reflect(
94 description = "A curve, that defines a how speed of the camera changes when decelerating to the \
95 zero speed."
96 )]
97 #[visit(optional)]
98 pub deceleration_curve: InheritableVariable<Curve>,
99
100 #[reflect(
101 description = "Amount of time (in seconds) during which the camera will accelerate to the max speed.",
102 min_value = 0.0
103 )]
104 #[visit(optional)]
105 pub acceleration_time: InheritableVariable<f32>,
106
107 #[reflect(
108 description = "Amount of time (in seconds) during which the camera will decelerate to the zero speed.",
109 min_value = 0.0
110 )]
111 #[visit(optional)]
112 pub deceleration_time: InheritableVariable<f32>,
113
114 #[reflect(
115 description = "A coefficient, that defines how fast the camera will respond to pressed keys.",
116 min_value = 0.01,
117 max_value = 1.0
118 )]
119 #[visit(optional)]
120 pub reactivity: InheritableVariable<f32>,
121
122 #[reflect(hidden)]
123 #[visit(optional)]
124 pub velocity: InheritableVariable<Vector3<f32>>,
125
126 #[reflect(hidden)]
127 #[visit(optional)]
128 pub target_velocity: InheritableVariable<Vector3<f32>>,
129
130 #[reflect(hidden)]
131 #[visit(skip)]
132 pub acceleration_coeff: f32,
133
134 #[reflect(hidden)]
135 #[visit(skip)]
136 pub move_forward: bool,
137
138 #[reflect(hidden)]
139 #[visit(skip)]
140 pub move_backward: bool,
141
142 #[reflect(hidden)]
143 #[visit(skip)]
144 pub move_left: bool,
145
146 #[reflect(hidden)]
147 #[visit(skip)]
148 pub move_right: bool,
149}
150
151impl Default for FlyingCameraController {
152 fn default() -> Self {
153 Self {
154 yaw: Default::default(),
155 pitch: Default::default(),
156 speed: 5.0.into(),
157 sensitivity: 0.7.into(),
158 pitch_limit: ((-89.9f32).to_radians()..89.9f32.to_radians()).into(),
159 move_forward_key: KeyBinding::Some(KeyCode::KeyW).into(),
160 move_backward_key: KeyBinding::Some(KeyCode::KeyS).into(),
161 move_left_key: KeyBinding::Some(KeyCode::KeyA).into(),
162 move_right_key: KeyBinding::Some(KeyCode::KeyD).into(),
163 acceleration_curve: Curve::from(vec![
164 CurveKey::new(
165 0.0,
166 0.0,
167 CurveKeyKind::Cubic {
168 left_tangent: 0.0,
169 right_tangent: 0.0,
170 },
171 ),
172 CurveKey::new(
173 1.0,
174 1.0,
175 CurveKeyKind::Cubic {
176 left_tangent: 0.0,
177 right_tangent: 0.0,
178 },
179 ),
180 ])
181 .into(),
182 deceleration_curve: Curve::from(vec![
183 CurveKey::new(
184 0.0,
185 0.0,
186 CurveKeyKind::Cubic {
187 left_tangent: 0.0,
188 right_tangent: 0.0,
189 },
190 ),
191 CurveKey::new(
192 1.0,
193 1.0,
194 CurveKeyKind::Cubic {
195 left_tangent: 0.0,
196 right_tangent: 0.0,
197 },
198 ),
199 ])
200 .into(),
201 acceleration_time: 0.25.into(),
202 deceleration_time: 1.0.into(),
203 velocity: Default::default(),
204 target_velocity: Default::default(),
205 acceleration_coeff: 0.0,
206 reactivity: 0.3.into(),
207 move_forward: false,
208 move_backward: false,
209 move_left: false,
210 move_right: false,
211 }
212 }
213}
214
215impl_component_provider!(FlyingCameraController);
216uuid_provider!(FlyingCameraController = "8d9e2feb-8c61-482c-8ba4-b0b13b201113");
217
218impl ScriptTrait for FlyingCameraController {
219 fn on_os_event(&mut self, event: &Event<()>, context: &mut ScriptContext) {
220 match event {
221 Event::WindowEvent {
222 event: WindowEvent::KeyboardInput { event, .. },
223 ..
224 } => {
225 for (binding, state) in [
226 (&self.move_forward_key, &mut self.move_forward),
227 (&self.move_backward_key, &mut self.move_backward),
228 (&self.move_left_key, &mut self.move_left),
229 (&self.move_right_key, &mut self.move_right),
230 ] {
231 if let KeyBinding::Some(key_code) = **binding {
232 if utils::translate_key_from_ui(key_code) == event.physical_key {
233 *state = event.state == ElementState::Pressed;
234 }
235 }
236 }
237 }
238 Event::DeviceEvent {
239 event: DeviceEvent::MouseMotion { delta, .. },
240 ..
241 } => {
242 let speed = *self.sensitivity * context.dt;
243 *self.yaw -= (delta.0 as f32) * speed;
244 *self.pitch = (*self.pitch + delta.1 as f32 * speed)
245 .max(self.pitch_limit.start)
246 .min(self.pitch_limit.end);
247 }
248 _ => {}
249 }
250 }
251
252 fn on_update(&mut self, context: &mut ScriptContext) {
253 let mut new_velocity = Vector3::default();
254
255 let this = &mut context.scene.graph[context.handle];
256
257 if self.move_forward {
258 new_velocity += this.look_vector();
259 }
260 if self.move_backward {
261 new_velocity -= this.look_vector();
262 }
263 if self.move_left {
264 new_velocity += this.side_vector();
265 }
266 if self.move_right {
267 new_velocity -= this.side_vector();
268 }
269
270 if let Some(new_normalized_velocity) = new_velocity.try_normalize(f32::EPSILON) {
271 self.acceleration_coeff = (self.acceleration_coeff
272 + context.dt / self.acceleration_time.max(context.dt))
273 .min(1.0);
274 *self.target_velocity = new_normalized_velocity.scale(
275 *self.speed
276 * self.acceleration_curve.value_at(self.acceleration_coeff)
277 * context.dt,
278 );
279 } else {
280 self.acceleration_coeff = (self.acceleration_coeff
281 - context.dt / self.deceleration_time.max(context.dt))
282 .max(0.0);
283 if let Some(normalized_velocity) = self.target_velocity.try_normalize(f32::EPSILON) {
284 *self.target_velocity = normalized_velocity.scale(
285 *self.speed
286 * self.deceleration_curve.value_at(self.acceleration_coeff)
287 * context.dt,
288 );
289 } else {
290 *self.target_velocity = Vector3::zeros();
291 }
292 }
293
294 self.velocity
295 .follow(&self.target_velocity, *self.reactivity);
296
297 let yaw = UnitQuaternion::from_axis_angle(&Vector3::y_axis(), *self.yaw);
298 this.local_transform_mut()
299 .set_rotation(
300 UnitQuaternion::from_axis_angle(
301 &UnitVector3::new_normalize(yaw * Vector3::x()),
302 *self.pitch,
303 ) * yaw,
304 )
305 .offset(*self.velocity);
306 }
307}