parent
7f359336ab
commit
a78d113952
2 changed files with 76 additions and 0 deletions
@ -0,0 +1,71 @@ |
||||
use std::f32::consts::TAU; |
||||
|
||||
use bevy::input::mouse::AccumulatedMouseMotion; |
||||
use bevy::prelude::*; |
||||
use bevy::window::{CursorGrabMode, CursorOptions}; |
||||
|
||||
#[derive(Component, Debug)] |
||||
pub struct CameraFreeLook { |
||||
/// The mouse sensitivity, in radians per pixel.
|
||||
pub sensitivity: Vec2, |
||||
/// How far the camera can be tilted up and down.
|
||||
pub pitch_limit: std::ops::RangeInclusive<f32>, |
||||
|
||||
/// Upon initialization, `pitch` and `yaw` will
|
||||
/// be set from the camera transform's rotation.
|
||||
initialized: bool, |
||||
/// The current yaw (right/left) of the camera, in radians.
|
||||
pub yaw: f32, |
||||
/// The current pitch (tilt) of the camera, in radians.
|
||||
pub pitch: f32, |
||||
} |
||||
|
||||
impl Default for CameraFreeLook { |
||||
fn default() -> Self { |
||||
Self { |
||||
sensitivity: Vec2::splat(0.2).map(f32::to_radians), |
||||
pitch_limit: -(TAU / 4.0)..=(TAU / 4.0), |
||||
initialized: false, |
||||
yaw: 0.0, |
||||
pitch: 0.0, |
||||
} |
||||
} |
||||
} |
||||
|
||||
pub fn camera_free_look( |
||||
window: Single<(&Window, &mut CursorOptions)>, |
||||
accumulated_mouse_motion: Res<AccumulatedMouseMotion>, |
||||
mouse_button_input: Res<ButtonInput<MouseButton>>, |
||||
key_input: Res<ButtonInput<KeyCode>>, |
||||
camera: Single<(&mut Transform, &mut CameraFreeLook)>, |
||||
) { |
||||
let (window, mut cursor) = window.into_inner(); |
||||
let (mut transform, mut camera) = camera.into_inner(); |
||||
|
||||
if !window.focused || key_input.just_pressed(KeyCode::Escape) { |
||||
cursor.grab_mode = CursorGrabMode::None; |
||||
cursor.visible = true; |
||||
} |
||||
if mouse_button_input.any_just_pressed([MouseButton::Left, MouseButton::Right]) { |
||||
cursor.grab_mode = CursorGrabMode::Locked; |
||||
cursor.visible = false; |
||||
} |
||||
|
||||
if cursor.grab_mode == CursorGrabMode::Locked { |
||||
// Ensure the yaw and pitch are initialized once
|
||||
// from the camera transform's current rotation.
|
||||
if !camera.initialized { |
||||
(camera.yaw, camera.pitch, _) = transform.rotation.to_euler(EulerRot::YXZ); |
||||
camera.initialized = true; |
||||
} |
||||
|
||||
// Update the current camera state's internal yaw and pitch.
|
||||
let delta = 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); |
||||
|
||||
// Override the camera transform's rotation.
|
||||
transform.rotation = Quat::from_euler(EulerRot::ZYX, 0.0, camera.yaw, camera.pitch); |
||||
} |
||||
} |
||||
Loading…
Reference in new issue