parent
1cbf30704d
commit
5467859d6d
2 changed files with 53 additions and 50 deletions
@ -1,63 +1,60 @@ |
|||||||
use bevy::prelude::*; |
use bevy::prelude::*; |
||||||
|
|
||||||
|
use bevy::input::common_conditions::input_just_pressed; |
||||||
use bevy::window::{CursorGrabMode, CursorOptions}; |
use bevy::window::{CursorGrabMode, CursorOptions}; |
||||||
|
|
||||||
use crate::Screen; |
use crate::Screen; |
||||||
|
|
||||||
pub fn plugin(app: &mut App) { |
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( |
app.add_systems( |
||||||
PreUpdate, |
Update, |
||||||
update_cursor_grab.run_if(in_state(Screen::Gameplay)), |
ungrab_cursor.run_if( |
||||||
|
is_cursor_grabbed.and(input_just_pressed(KeyCode::Escape).or(not(is_window_focused))), |
||||||
|
), |
||||||
); |
); |
||||||
} |
} |
||||||
|
|
||||||
fn update_cursor_grab( |
fn add_window_click_observer(window: Single<Entity, With<Window>>, mut commands: Commands) { |
||||||
mut mouse_button_input: ResMut<ButtonInput<MouseButton>>, |
commands.entity(*window).observe( |
||||||
key_input: Res<ButtonInput<KeyCode>>, |
|mut event: On<Pointer<Click>>, mut cursor: Single<&mut CursorOptions>| { |
||||||
window: Single<(&mut Window, &mut CursorOptions)>, |
|
||||||
) { |
|
||||||
let (mut window, mut cursor) = window.into_inner(); |
|
||||||
|
|
||||||
let is_grabbed = cursor.grab_mode != CursorGrabMode::None; |
|
||||||
let request_grab = mouse_button_input.any_just_pressed([MouseButton::Left, MouseButton::Right]); |
|
||||||
let request_ungrab = !window.focused || key_input.just_pressed(KeyCode::Escape); |
|
||||||
|
|
||||||
if !is_grabbed && request_grab && !request_ungrab { |
|
||||||
cursor.grab_mode = CursorGrabMode::Locked; |
cursor.grab_mode = CursorGrabMode::Locked; |
||||||
|
cursor.visible = false; |
||||||
|
event.propagate(false); |
||||||
|
}, |
||||||
|
); |
||||||
|
} |
||||||
|
|
||||||
// To prevent other systems (such as `place_break_blocks`)
|
fn ungrab_cursor(mut cursor: Single<&mut CursorOptions>) { |
||||||
// from seeing the mouse button inputs, clear the state here.
|
|
||||||
mouse_button_input.clear(); |
|
||||||
} |
|
||||||
|
|
||||||
if is_grabbed && request_ungrab && !request_grab { |
|
||||||
cursor.grab_mode = CursorGrabMode::None; |
cursor.grab_mode = CursorGrabMode::None; |
||||||
} |
cursor.visible = true; |
||||||
|
} |
||||||
|
|
||||||
if is_grabbed && !request_ungrab { |
/// Sets the cursor position to the center of the window,
|
||||||
// Set the cursor position to the center of the window, so that when
|
/// so that once it is ungrabbed, it will reappear there.
|
||||||
// it is ungrabbed, it will reappear there. Because `is_grabbed` is
|
fn center_cursor(mut window: Single<&mut Window>) { |
||||||
// not updated on grab, this block is delayed by one frame.
|
let center = window.resolution.size() / 2.; |
||||||
//
|
|
||||||
// On Wayland, since the cursor is locked into place, this only needs
|
// On Wayland, since the cursor is locked into place, this only needs to be
|
||||||
// to be done once. Unfortunately, for some reason this doesn't work
|
// done once. Unfortunately, for some reason, this doesn't work in the same
|
||||||
// in the same frame as setting `grab_mode`, and would log an error.
|
// 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
|
// 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
|
// window bounds, so we repeatedly move the cursor back to the center
|
||||||
// while it's grabbed.
|
// while it's grabbed.
|
||||||
//
|
//
|
||||||
// On the web, the cursor can be locked, but setting its position is
|
// On the web, the cursor can be locked, but setting its position isn't
|
||||||
// not supported at all, so this would instead log a bunch of errors.
|
// supported at all, so this would instead log a bunch of errors.
|
||||||
let center = window.resolution.size() / 2.; |
#[cfg(not(target_family = "wasm"))] |
||||||
#[cfg(not(target_family = "wasm"))] // skip on web
|
|
||||||
window.set_cursor_position(Some(center)); |
window.set_cursor_position(Some(center)); |
||||||
} |
|
||||||
|
|
||||||
// Keep cursor visbility in sync with `grab_mode`.
|
|
||||||
cursor.visible = cursor.grab_mode == CursorGrabMode::None; |
|
||||||
} |
} |
||||||
|
|
||||||
pub fn is_cursor_grabbed(cursor: Single<&CursorOptions>) -> bool { |
pub fn is_cursor_grabbed(cursor: Single<&CursorOptions>) -> bool { |
||||||
cursor.grab_mode != CursorGrabMode::None |
cursor.grab_mode != CursorGrabMode::None |
||||||
} |
} |
||||||
|
|
||||||
|
pub fn is_window_focused(window: Single<&Window>) -> bool { |
||||||
|
window.focused |
||||||
|
} |
||||||
|
|||||||
Loading…
Reference in new issue