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