use bevy::prelude::*; use bevy::input::common_conditions::input_just_pressed; use bevy::picking::pointer::{PointerId, PointerLocation}; use bevy::window::{CursorGrabMode, CursorOptions}; use crate::Screen; pub fn plugin(app: &mut App) { app.add_systems(OnEnter(Screen::Gameplay), add_window_click_observer); app.add_systems(PreUpdate, center_cursor.run_if(is_cursor_grabbed)); app.add_systems( Update, ungrab_cursor.run_if( is_cursor_grabbed.and(input_just_pressed(KeyCode::Escape).or(not(is_window_focused))), ), ); } fn add_window_click_observer(window: Single>, mut commands: Commands) { commands.entity(*window).observe( |mut event: On>, mut cursor: Single<&mut CursorOptions>| { cursor.grab_mode = CursorGrabMode::Locked; cursor.visible = false; event.propagate(false); }, ); } fn ungrab_cursor(mut cursor: Single<&mut CursorOptions>) { cursor.grab_mode = CursorGrabMode::None; cursor.visible = true; } /// Sets the cursor (and pointer) position to the center of the /// window, so that once it is ungrabbed, it will reappear there. fn center_cursor( mut window: Single<&mut Window>, pointers: Query<(&PointerId, &mut PointerLocation)>, ) { let center = window.resolution.size() / 2.; // On Wayland, since the cursor is locked into place, this only needs to be // done once. Unfortunately, for some reason, this doesn't work in the same // frame as setting `grab_mode`, logging an error. This system is scheduled // on `PreUpdate`, so it runs with one frame delay. // // On X11, the cursor can't be locked into place, only confined to the // window bounds, so we repeatedly move the cursor back to the center // while it's grabbed. // // On the web, the cursor can be locked, but setting its position isn't // supported at all, so this would instead log a bunch of errors. #[cfg(not(target_family = "wasm"))] window.set_cursor_position(Some(center)); // Update the `PointerLocation` for the mouse to be at the center // of the window. Otherwise it might use the last known position. for (id, mut pointer) in pointers { if id.is_mouse() { if let Some(location) = pointer.location.as_mut() { location.position = center; } } } } pub fn is_cursor_grabbed(cursor: Single<&CursorOptions>) -> bool { cursor.grab_mode != CursorGrabMode::None } pub fn is_window_focused(window: Single<&Window>) -> bool { window.focused }