Bevy Version: | 0.14 | (current) |
---|
Mouse
Relevant official examples:
mouse_input
,
mouse_input_events
.
Mouse Buttons
Similar to keyboard input, mouse buttons are available as a
ButtonInput
resource, events, and run
conditions (see list). Use whichever
pattern feels most appropriate to your use case.
Checking Button State
You can check the state of specific mouse buttons using the
ButtonInput<MouseButton>
resource:
- Use
.pressed(…)
/.released(…)
to check if a button is being held down- These return
true
every frame, for as long as the button is in the respective state.
- These return
- Use
.just_pressed(…)
/.just_released(…)
to detect the actual press/release- These return
true
only on the frame update when the press/release happened.
- These return
fn mouse_button_input(
buttons: Res<ButtonInput<MouseButton>>,
) {
if buttons.just_pressed(MouseButton::Left) {
// Left button was pressed
}
if buttons.just_released(MouseButton::Left) {
// Left Button was released
}
if buttons.pressed(MouseButton::Right) {
// Right Button is being held down
}
// we can check multiple at once with `.any_*`
if buttons.any_just_pressed([MouseButton::Left, MouseButton::Middle]) {
// Either the left or the middle (wheel) button was just pressed
}
}
You can also iterate over any buttons that have been pressed or released:
fn mouse_button_iter(
buttons: Res<ButtonInput<MouseButton>>,
) {
for button in buttons.get_pressed() {
println!("{:?} is currently held down", button);
}
for button in buttons.get_just_pressed() {
println!("{:?} was pressed", button);
}
for button in buttons.get_just_released() {
println!("{:?} was released", button);
}
}
Run Conditions
Another workflow is to add run conditions to your systems, so that they only run when the appropriate inputs happen.
It is highly recommended you write your own run conditions, so that you can check for whatever you want, support configurable bindings, etc…
For prototyping, Bevy offers some built-in run conditions:
use bevy::input::common_conditions::*;
app.add_systems(Update, (
handle_middleclick
.run_if(input_just_pressed(MouseButton::Middle)),
handle_drag
.run_if(input_pressed(MouseButton::Left)),
));
Mouse Button Events
Alternatively, you can use MouseButtonInput
events to get
all activity:
use bevy::input::mouse::MouseButtonInput;
fn mouse_button_events(
mut mousebtn_evr: EventReader<MouseButtonInput>,
) {
use bevy::input::ButtonState;
for ev in mousebtn_evr.read() {
match ev.state {
ButtonState::Pressed => {
println!("Mouse button press: {:?}", ev.button);
}
ButtonState::Released => {
println!("Mouse button release: {:?}", ev.button);
}
}
}
}
Mouse Scrolling / Wheel
To detect scrolling input, use MouseWheel
events:
use bevy::input::mouse::MouseWheel;
fn scroll_events(
mut evr_scroll: EventReader<MouseWheel>,
) {
use bevy::input::mouse::MouseScrollUnit;
for ev in evr_scroll.read() {
match ev.unit {
MouseScrollUnit::Line => {
println!("Scroll (line units): vertical: {}, horizontal: {}", ev.y, ev.x);
}
MouseScrollUnit::Pixel => {
println!("Scroll (pixel units): vertical: {}, horizontal: {}", ev.y, ev.x);
}
}
}
}
The MouseScrollUnit
enum is important: it tells you the type of scroll
input. Line
is for hardware with fixed steps, like the wheel on desktop
mice. Pixel
is for hardware with smooth (fine-grained) scrolling, like
laptop touchpads.
You should probably handle each of these differently (with different sensitivity settings), to provide a good experience on both types of hardware.
Note: the Line
unit is not guaranteed to have whole number values/steps!
At least macOS does non-linear scaling / acceleration of
scrolling at the OS level, meaning your app will get weird values for the number
of lines, even when using a regular PC mouse with a fixed-stepping scroll wheel.
Mouse Motion
Use this if you don't care about the exact position of the mouse cursor, but rather you just want to see how much the mouse moved from frame to frame. This is useful for things like controlling a 3D camera.
Use MouseMotion
events. Whenever the mouse is moved, you
will get an event with the delta.
use bevy::input::mouse::MouseMotion;
fn mouse_motion(
mut evr_motion: EventReader<MouseMotion>,
) {
for ev in evr_motion.read() {
println!("Mouse moved: X: {} px, Y: {} px", ev.delta.x, ev.delta.y);
}
}
You might want to grab/lock the mouse inside the game window.
Mouse Cursor Position
Use this if you want to accurately track the position of the pointer / cursor. This is useful for things like clicking and hovering over objects in your game or UI.
You can get the current coordinates of the mouse pointer, from the respective
Window
(if the mouse is currently inside that window):
use bevy::window::PrimaryWindow;
fn cursor_position(
q_windows: Query<&Window, With<PrimaryWindow>>,
) {
// Games typically only have one window (the primary window)
if let Some(position) = q_windows.single().cursor_position() {
println!("Cursor is inside the primary window, at {:?}", position);
} else {
println!("Cursor is not in the game window.");
}
}
To detect when the pointer is moved, use CursorMoved
events
to get the updated coordinates:
fn cursor_events(
mut evr_cursor: EventReader<CursorMoved>,
) {
for ev in evr_cursor.read() {
println!(
"New cursor position: X: {}, Y: {}, in Window ID: {:?}",
ev.position.x, ev.position.y, ev.window
);
}
}
Note that you can only get the position of the mouse inside a window; you cannot get the global position of the mouse in the whole OS Desktop / on the screen as a whole.
The coordinates you get are in "window space". They represent window pixels, and the origin is the top left corner of the window.
They do not relate to your camera or in-game coordinates in any way. See this cookbook example for converting these window cursor coordinates into world-space coordinates.
To track when the mouse cursor enters and leaves your window(s), use
CursorEntered
and CursorLeft
events.