@ -4,6 +4,53 @@ use bevy::input::mouse::AccumulatedMouseMotion;
use bevy ::prelude ::* ;
use bevy ::window ::{ CursorGrabMode , CursorOptions } ;
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 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.
let center = window . resolution . size ( ) / 2. ;
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 ;
}
}
pub fn is_cursor_grabbed ( cursor : Single < & CursorOptions > ) -> bool {
cursor . grab_mode = = CursorGrabMode ::Locked
}
#[ derive(Component, Debug) ]
pub struct CameraFreeLook {
/// The mouse sensitivity, in radians per pixel.
@ -32,49 +79,27 @@ impl Default for CameraFreeLook {
}
}
pub fn camera_free_ look (
pub fn camera_look (
accumulated_mouse_motion : Res < AccumulatedMouseMotion > ,
mut mouse_button_input : ResMut < ButtonInput < MouseButton > > ,
key_input : Res < ButtonInput < KeyCode > > ,
window : Single < ( & Window , & mut CursorOptions ) > ,
camera : Single < ( & mut Transform , & mut CameraFreeLook ) > ,
crosshair : Single < & mut Visibility , With < Crosshair > > ,
) {
let ( window , mut cursor ) = window . into_inner ( ) ;
let ( mut transform , mut look ) = camera . into_inner ( ) ;
let mut crosshair_visibility = crosshair . into_inner ( ) ;
if ! window . focused | | key_input . just_pressed ( KeyCode ::Escape ) {
* crosshair_visibility = Visibility ::Hidden ;
cursor . grab_mode = CursorGrabMode ::None ;
cursor . visible = true ;
}
if mouse_button_input . any_just_pressed ( [ MouseButton ::Left , MouseButton ::Right ] ) {
* crosshair_visibility = Visibility ::Inherited ;
cursor . grab_mode = CursorGrabMode ::Locked ;
cursor . visible = false ;
// To prevent other systems (such as placement) from seeing
// the mouse buttons being pressed, clear the state here.
mouse_button_input . clear ( ) ;
// Ensure the yaw and pitch are initialized once
// from the camera transform's current rotation.
if ! look . initialized {
( look . yaw , look . pitch , _ ) = transform . rotation . to_euler ( EulerRot ::YXZ ) ;
look . initialized = true ;
}
if cursor . grab_mode = = CursorGrabMode ::Locked {
// Ensure the yaw and pitch are initialized once
// from the camera transform's current rotation.
if ! look . initialized {
( look . yaw , look . pitch , _ ) = transform . rotation . to_euler ( EulerRot ::YXZ ) ;
look . initialized = true ;
}
// Update the current camera state's internal yaw and pitch.
let motion = accumulated_mouse_motion . delta * look . sensitivity ;
let ( min , max ) = look . pitch_limit . clone ( ) . into_inner ( ) ;
look . yaw = ( look . yaw - motion . x ) . rem_euclid ( TAU ) ; // keep within 0°..360°
look . pitch = ( look . pitch - motion . y ) . clamp ( min , max ) ;
// Update the current camera state's internal yaw and pitch.
let motion = accumulated_mouse_motion . delta * look . sensitivity ;
let ( min , max ) = look . pitch_limit . clone ( ) . into_inner ( ) ;
look . yaw = ( look . yaw - motion . x ) . rem_euclid ( TAU ) ; // keep within 0°..360°
look . pitch = ( look . pitch - motion . y ) . clamp ( min , max ) ;
// Override the camera transform's rotation.
transform . rotation = Quat ::from_euler ( EulerRot ::ZYX , 0.0 , look . yaw , look . pitch ) ;
}
// Override the camera transform's rotation.
transform . rotation = Quat ::from_euler ( EulerRot ::ZYX , 0.0 , look . yaw , look . pitch ) ;
}
// TODO: Make it possible to attach this to any entity, such as the player,
@ -116,14 +141,10 @@ impl Default for CameraNoClip {
pub fn noclip_controller (
time : Res < Time < Real > > ,
cursor : Single < & mut CursorOptions > ,
key_input : Res < ButtonInput < KeyCode > > ,
camera : Single < ( & mut Transform , & mut CameraNoClip ) > ,
camera : Single < ( & mut Transform , & CameraNoClip ) > ,
) {
let ( mut transform , noclip ) = camera . into_inner ( ) ;
if cursor . grab_mode ! = CursorGrabMode ::Locked {
return ;
}
#[ rustfmt::skip ]
let movement = {