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 #[visit(optional)]
49 pub yaw: InheritableVariable<f32>,
50
51 #[visit(optional)]
53 pub pitch: InheritableVariable<f32>,
54
55 #[visit(optional)]
57 pub speed: InheritableVariable<f32>,
58
59 #[visit(optional)]
61 pub sensitivity: InheritableVariable<f32>,
62
63 #[visit(optional)]
65 pub pitch_limit: InheritableVariable<Range<f32>>,
66
67 #[visit(optional)]
72 pub move_forward_key: InheritableVariable<KeyBinding>,
73
74 #[visit(optional)]
76 pub move_backward_key: InheritableVariable<KeyBinding>,
77
78 #[visit(optional)]
80 pub move_left_key: InheritableVariable<KeyBinding>,
81
82 #[visit(optional)]
84 pub move_right_key: InheritableVariable<KeyBinding>,
85
86 #[visit(optional)]
88 pub acceleration_curve: InheritableVariable<Curve>,
89
90 #[visit(optional)]
92 pub deceleration_curve: InheritableVariable<Curve>,
93
94 #[reflect(min_value = 0.0)]
96 #[visit(optional)]
97 pub acceleration_time: InheritableVariable<f32>,
98
99 #[reflect(min_value = 0.0)]
101 #[visit(optional)]
102 pub deceleration_time: InheritableVariable<f32>,
103
104 #[reflect(min_value = 0.01, max_value = 1.0)]
106 #[visit(optional)]
107 pub reactivity: InheritableVariable<f32>,
108
109 #[reflect(hidden)]
110 #[visit(optional)]
111 pub velocity: InheritableVariable<Vector3<f32>>,
112
113 #[reflect(hidden)]
114 #[visit(optional)]
115 pub target_velocity: InheritableVariable<Vector3<f32>>,
116
117 #[reflect(hidden)]
118 #[visit(skip)]
119 pub acceleration_coeff: f32,
120
121 #[reflect(hidden)]
122 #[visit(skip)]
123 pub move_forward: bool,
124
125 #[reflect(hidden)]
126 #[visit(skip)]
127 pub move_backward: bool,
128
129 #[reflect(hidden)]
130 #[visit(skip)]
131 pub move_left: bool,
132
133 #[reflect(hidden)]
134 #[visit(skip)]
135 pub move_right: bool,
136}
137
138impl Default for FlyingCameraController {
139 fn default() -> Self {
140 Self {
141 yaw: Default::default(),
142 pitch: Default::default(),
143 speed: 5.0.into(),
144 sensitivity: 0.7.into(),
145 pitch_limit: ((-89.9f32).to_radians()..89.9f32.to_radians()).into(),
146 move_forward_key: KeyBinding::Some(KeyCode::KeyW).into(),
147 move_backward_key: KeyBinding::Some(KeyCode::KeyS).into(),
148 move_left_key: KeyBinding::Some(KeyCode::KeyA).into(),
149 move_right_key: KeyBinding::Some(KeyCode::KeyD).into(),
150 acceleration_curve: Curve::from(vec![
151 CurveKey::new(
152 0.0,
153 0.0,
154 CurveKeyKind::Cubic {
155 left_tangent: 0.0,
156 right_tangent: 0.0,
157 },
158 ),
159 CurveKey::new(
160 1.0,
161 1.0,
162 CurveKeyKind::Cubic {
163 left_tangent: 0.0,
164 right_tangent: 0.0,
165 },
166 ),
167 ])
168 .into(),
169 deceleration_curve: Curve::from(vec![
170 CurveKey::new(
171 0.0,
172 0.0,
173 CurveKeyKind::Cubic {
174 left_tangent: 0.0,
175 right_tangent: 0.0,
176 },
177 ),
178 CurveKey::new(
179 1.0,
180 1.0,
181 CurveKeyKind::Cubic {
182 left_tangent: 0.0,
183 right_tangent: 0.0,
184 },
185 ),
186 ])
187 .into(),
188 acceleration_time: 0.25.into(),
189 deceleration_time: 1.0.into(),
190 velocity: Default::default(),
191 target_velocity: Default::default(),
192 acceleration_coeff: 0.0,
193 reactivity: 0.3.into(),
194 move_forward: false,
195 move_backward: false,
196 move_left: false,
197 move_right: false,
198 }
199 }
200}
201
202impl_component_provider!(FlyingCameraController);
203uuid_provider!(FlyingCameraController = "8d9e2feb-8c61-482c-8ba4-b0b13b201113");
204
205impl ScriptTrait for FlyingCameraController {
206 fn on_os_event(&mut self, event: &Event<()>, context: &mut ScriptContext) {
207 match event {
208 Event::WindowEvent {
209 event: WindowEvent::KeyboardInput { event, .. },
210 ..
211 } => {
212 for (binding, state) in [
213 (&self.move_forward_key, &mut self.move_forward),
214 (&self.move_backward_key, &mut self.move_backward),
215 (&self.move_left_key, &mut self.move_left),
216 (&self.move_right_key, &mut self.move_right),
217 ] {
218 if let KeyBinding::Some(key_code) = **binding {
219 if utils::translate_key_from_ui(key_code) == event.physical_key {
220 *state = event.state == ElementState::Pressed;
221 }
222 }
223 }
224 }
225 Event::DeviceEvent {
226 event: DeviceEvent::MouseMotion { delta, .. },
227 ..
228 } => {
229 let speed = *self.sensitivity * context.dt;
230 *self.yaw -= (delta.0 as f32) * speed;
231 *self.pitch = (*self.pitch + delta.1 as f32 * speed)
232 .max(self.pitch_limit.start)
233 .min(self.pitch_limit.end);
234 }
235 _ => {}
236 }
237 }
238
239 fn on_update(&mut self, context: &mut ScriptContext) {
240 let mut new_velocity = Vector3::default();
241
242 let this = &mut context.scene.graph[context.handle];
243
244 if self.move_forward {
245 new_velocity += this.look_vector();
246 }
247 if self.move_backward {
248 new_velocity -= this.look_vector();
249 }
250 if self.move_left {
251 new_velocity += this.side_vector();
252 }
253 if self.move_right {
254 new_velocity -= this.side_vector();
255 }
256
257 if let Some(new_normalized_velocity) = new_velocity.try_normalize(f32::EPSILON) {
258 self.acceleration_coeff = (self.acceleration_coeff
259 + context.dt / self.acceleration_time.max(context.dt))
260 .min(1.0);
261 *self.target_velocity = new_normalized_velocity.scale(
262 *self.speed
263 * self.acceleration_curve.value_at(self.acceleration_coeff)
264 * context.dt,
265 );
266 } else {
267 self.acceleration_coeff = (self.acceleration_coeff
268 - context.dt / self.deceleration_time.max(context.dt))
269 .max(0.0);
270 if let Some(normalized_velocity) = self.target_velocity.try_normalize(f32::EPSILON) {
271 *self.target_velocity = normalized_velocity.scale(
272 *self.speed
273 * self.deceleration_curve.value_at(self.acceleration_coeff)
274 * context.dt,
275 );
276 } else {
277 *self.target_velocity = Vector3::zeros();
278 }
279 }
280
281 self.velocity
282 .follow(&self.target_velocity, *self.reactivity);
283
284 let yaw = UnitQuaternion::from_axis_angle(&Vector3::y_axis(), *self.yaw);
285 this.local_transform_mut()
286 .set_rotation(
287 UnitQuaternion::from_axis_angle(
288 &UnitVector3::new_normalize(yaw * Vector3::x()),
289 *self.pitch,
290 ) * yaw,
291 )
292 .offset(*self.velocity);
293 }
294}