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