Fix and refactor cursor grabbing

- Fix `grab_mode` not updating on the web when pressing escape by
  adding `bevy_fix_cursor_unlock_web` dependency and its plugin
- Fix cursor not being centered when ungrabbed on X11
- Move some functionality to `update_crosshair_visibility`
main
copygirl 6 days ago
parent 209576bc18
commit 10e5e3523d
  1. 13
      Cargo.lock
  2. 1
      Cargo.toml
  3. 55
      src/camera.rs
  4. 7
      src/main.rs

13
Cargo.lock generated

@ -328,6 +328,7 @@ name = "bevy-bloxel-classic"
version = "0.1.0"
dependencies = [
"bevy",
"bevy_fix_cursor_unlock_web",
]
[[package]]
@ -661,6 +662,18 @@ dependencies = [
"encase_derive_impl",
]
[[package]]
name = "bevy_fix_cursor_unlock_web"
version = "0.2.0"
source = "registry+https://github.com/rust-lang/crates.io-index"
checksum = "c436b85a08404677c0b5fd6364fa142a6e86d30dc8c89fe134a8e621a80fc43f"
dependencies = [
"bevy_app",
"bevy_ecs",
"bevy_window",
"web-sys",
]
[[package]]
name = "bevy_gilrs"
version = "0.17.2"

@ -13,3 +13,4 @@ opt-level = 3
[dependencies]
bevy = "0.17"
bevy_fix_cursor_unlock_web = "0.2"

@ -8,47 +8,51 @@ pub fn cursor_grab(
mut mouse_button_input: ResMut<ButtonInput<MouseButton>>,
key_input: Res<ButtonInput<KeyCode>>,
window: Single<(&mut Window, &mut CursorOptions)>,
crosshair: Single<&mut Visibility, With<Crosshair>>,
mut request_center_cursor: Local<bool>,
) {
let (mut window, mut cursor) = window.into_inner();
let mut crosshair_visibility = crosshair.into_inner();
let is_grabbed = cursor.grab_mode == CursorGrabMode::Locked;
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 {
*crosshair_visibility = Visibility::Inherited;
cursor.grab_mode = CursorGrabMode::Locked;
cursor.visible = false;
// HACK: It appears setting the cursor position in the same frame we're
// locking it is not possible, so we're delaying it by a frame.
*request_center_cursor = true;
// To prevent other systems (such as `place_break_blocks`)
// from seeing the mouse button inputs, clear the state here.
mouse_button_input.clear();
}
if is_grabbed && *request_center_cursor {
// Set the cursor position to the middle of the window,
// so when it is ungrabbed again it'll reappear there.
if is_grabbed && request_ungrab && !request_grab {
cursor.grab_mode = CursorGrabMode::None;
}
if is_grabbed && !request_ungrab {
// Set the cursor position to the center of the window, so that when
// it is ungrabbed, it will reappear there. Because `is_grabbed` is
// not updated on grab, this block is delayed by one frame.
//
// 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`, and would log an error.
//
// 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 is
// not supported at all, so this would instead log a bunch of errors.
let center = window.resolution.size() / 2.;
#[cfg(not(target_arch = "wasm32"))] // skip on web
window.set_cursor_position(Some(center));
*request_center_cursor = false; // Only do this once.
}
if is_grabbed && request_ungrab && !request_grab {
*crosshair_visibility = Visibility::Hidden;
cursor.grab_mode = CursorGrabMode::None;
cursor.visible = true;
}
// Keep cursor visbility in sync with `grab_mode`.
cursor.visible = cursor.grab_mode == CursorGrabMode::None;
}
pub fn is_cursor_grabbed(cursor: Single<&CursorOptions>) -> bool {
cursor.grab_mode == CursorGrabMode::Locked
cursor.grab_mode != CursorGrabMode::None
}
#[derive(Component, Debug)]
@ -196,3 +200,14 @@ pub fn setup_crosshair(mut commands: Commands, asset_server: Res<AssetServer>) {
)],
));
}
pub fn update_crosshair_visibility(
cursor: Single<&CursorOptions, Changed<CursorOptions>>,
crosshair: Single<&mut Visibility, With<Crosshair>>,
) {
let is_grabbed = cursor.grab_mode != CursorGrabMode::None;
let mut crosshair_visibility = crosshair.into_inner();
*crosshair_visibility = (!is_grabbed || cursor.visible)
.then_some(Visibility::Hidden)
.unwrap_or_default();
}

@ -12,17 +12,20 @@ fn main() {
#[rustfmt::skip]
App::new()
.add_plugins(DefaultPlugins)
// Fixes issue on web where the cursor isn't ungrabbed properly.
.add_plugins(bevy_fix_cursor_unlock_web::FixPointerUnlockPlugin)
.add_systems(Startup, setup_crosshair)
.add_systems(Startup, setup_blocks)
.add_systems(Startup, setup_scene.after(setup_blocks))
.add_systems(Update, cursor_grab)
.add_systems(Update, update_crosshair_visibility.after(cursor_grab))
.add_systems(Update, camera_look.after(cursor_grab).run_if(is_cursor_grabbed))
.add_systems(Update, noclip_controller.after(camera_look).run_if(is_cursor_grabbed))
// This system requires the camera's `GlobalTransform`, so for
// a most up-to-date value, run it right after it's been updated.
// `place_break_blocks` requires the camera's `GlobalTransform`.
// For a most up-to-date value, run it after that's been updated.
.add_systems(PostUpdate, place_break_blocks.after(TransformSystems::Propagate))
.run();

Loading…
Cancel
Save