use bevy::{input::mouse::AccumulatedMouseMotion, prelude::*, window::CursorGrabMode}; const MOVEMENT_SPEED: f32 = 0.15; const MOUSE_SENSITIVITY: Vec2 = Vec2::new(0.002, 0.002); pub struct CameraControllerPlugin; /// Marks an entity as being the camera controlled by this plugin. #[derive(Component)] pub struct ControlledCamera; impl Plugin for CameraControllerPlugin { fn build(&self, app: &mut App) { app.add_systems( Update, ( capture_mouse, camera_mouse_rotation, camera_keyboard_translation, ) .chain(), ); } } fn capture_mouse( mut window: Single<&mut Window>, mouse: Res>, key: Res>, ) { if mouse.just_pressed(MouseButton::Left) { set_mouse_grabbed(&mut window, true); } if key.just_pressed(KeyCode::Escape) { set_mouse_grabbed(&mut window, false); } // Ungrab the mouse if window doesn't have focus. if !window.focused { set_mouse_grabbed(&mut window, false); } } fn camera_mouse_rotation( window: Single<&Window>, mut camera: Query<&mut Transform, With>, mouse_motion: Res, ) { // Mouse must be grabbed by the window for this system to run. if !mouse_grabbed(&window) { return; } // We also need to have exactly one camera with the `ControlledCamera` component. let Ok(mut transform) = camera.get_single_mut() else { return; }; let delta = mouse_motion.delta; if delta != Vec2::ZERO { let delta_yaw = -delta.x * MOUSE_SENSITIVITY.x; let delta_pitch = -delta.y * MOUSE_SENSITIVITY.y; let rot_yaw = Quat::from_axis_angle(Vec3::Y, delta_yaw); let rot_pitch = Quat::from_axis_angle(Vec3::X, delta_pitch); transform.rotation = rot_yaw * transform.rotation * rot_pitch; } } fn camera_keyboard_translation( window: Single<&Window>, mut camera: Query<&mut Transform, With>, key: Res>, ) { // Mouse must be grabbed by the window for this system to run. if !mouse_grabbed(&window) { return; } // We also need to have exactly one camera with the `ControlledCamera` component. let Ok(mut transform) = camera.get_single_mut() else { return; }; let mut input = Vec2::ZERO; if key.pressed(KeyCode::KeyD) { input.x += 1.; } if key.pressed(KeyCode::KeyA) { input.x -= 1.; } if key.pressed(KeyCode::KeyW) { input.y += 1.; } if key.pressed(KeyCode::KeyS) { input.y -= 1.; } let mut movement = input * MOVEMENT_SPEED; if key.pressed(KeyCode::ShiftLeft) { movement *= 4.; } if movement.x != 0. { let translation = transform.right() * movement.x; transform.translation += translation; } if movement.y != 0. { let translation = transform.forward() * movement.y; transform.translation += translation; } } fn mouse_grabbed(window: &Window) -> bool { window.cursor_options.grab_mode == CursorGrabMode::Locked } fn set_mouse_grabbed(window: &mut Window, value: bool) { window.cursor_options.visible = !value; window.cursor_options.grab_mode = if value { CursorGrabMode::Locked } else { CursorGrabMode::None }; }