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.
  • Use .just_pressed(…)/.just_released(…) to detect the actual press/release
    • These return true only on the frame update when the press/release happened.
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.