diff --git a/src/free_camera.rs b/src/free_camera.rs index 78b316e..23f0249 100644 --- a/src/free_camera.rs +++ b/src/free_camera.rs @@ -60,12 +60,82 @@ pub fn camera_free_look( } // Update the current camera state's internal yaw and pitch. - let delta = accumulated_mouse_motion.delta * camera.sensitivity; + let motion = accumulated_mouse_motion.delta * camera.sensitivity; let (min, max) = camera.pitch_limit.clone().into_inner(); - camera.yaw = (camera.yaw - delta.x).rem_euclid(TAU); // keep within 0°..360° - camera.pitch = (camera.pitch - delta.y).clamp(min, max); + camera.yaw = (camera.yaw - motion.x).rem_euclid(TAU); // keep within 0°..360° + camera.pitch = (camera.pitch - motion.y).clamp(min, max); // Override the camera transform's rotation. transform.rotation = Quat::from_euler(EulerRot::ZYX, 0.0, camera.yaw, camera.pitch); } } + +// TODO: Make it possible to attach this to any entity, such as the player, +// with the camera being a child. This will require the noclip system +// to know which entity is the camera. + +#[derive(Component, Debug)] +pub struct CameraNoClip { + /// The speed at which the camera moves. + pub speed: f32, + + /// [`KeyCode`] for forward (relative to camera) translation. + pub key_forward: KeyCode, + /// [`KeyCode`] for backward (relative to camera) translation. + pub key_back: KeyCode, + /// [`KeyCode`] for right (relative to camera) translation. + pub key_right: KeyCode, + /// [`KeyCode`] for left (relative to camera) translation. + pub key_left: KeyCode, + /// [`KeyCode`] for up (global +Y) translation. + pub key_up: KeyCode, + /// [`KeyCode`] for down (global -Y) translation. + pub key_down: KeyCode, +} + +impl Default for CameraNoClip { + fn default() -> Self { + Self { + speed: 5.0, + key_forward: KeyCode::KeyW, + key_back: KeyCode::KeyS, + key_right: KeyCode::KeyD, + key_left: KeyCode::KeyA, + key_up: KeyCode::Space, + key_down: KeyCode::ShiftLeft, + } + } +} + +pub fn noclip_controller( + time: Res>, + cursor: Single<&mut CursorOptions>, + key_input: Res>, + camera: Single<(&mut Transform, &mut CameraNoClip)>, +) { + let (mut transform, noclip) = camera.into_inner(); + if cursor.grab_mode != CursorGrabMode::Locked { + return; + } + + #[rustfmt::skip] + let movement = { + let mut movement = Vec3::ZERO; + if key_input.pressed(noclip.key_forward) { movement.z += 1.0; } + if key_input.pressed(noclip.key_back ) { movement.z -= 1.0; } + if key_input.pressed(noclip.key_right ) { movement.x += 1.0; } + if key_input.pressed(noclip.key_left ) { movement.x -= 1.0; } + if key_input.pressed(noclip.key_up ) { movement.y += 1.0; } + if key_input.pressed(noclip.key_down ) { movement.y -= 1.0; } + movement.clamp_length_max(1.0) * noclip.speed + }; + + if movement != Vec3::ZERO { + let dt = time.delta_secs(); + let forward = transform.forward(); + let right = transform.right(); + transform.translation += movement.x * dt * right; + transform.translation += movement.y * dt * Vec3::Y; + transform.translation += movement.z * dt * forward; + } +} diff --git a/src/main.rs b/src/main.rs index a92c6a5..69eef41 100644 --- a/src/main.rs +++ b/src/main.rs @@ -7,7 +7,7 @@ fn main() { App::new() .add_plugins(DefaultPlugins) .add_systems(Startup, setup) - .add_systems(Update, camera_free_look) + .add_systems(Update, (camera_free_look, noclip_controller).chain()) .run(); } @@ -41,5 +41,6 @@ fn setup( Camera3d::default(), Transform::from_xyz(-2.5, 4.5, 9.0).looking_at(Vec3::ZERO, Vec3::Y), CameraFreeLook::default(), + CameraNoClip::default(), )); }