⚠️ Beta State

PyBevy is in an early and experimental stage. The API is incomplete, subject to breaking changes without notice, and you should expect bugs. Many features are still under development.

Mouse Input

Handle mouse clicks, motion, and scroll wheel input.

Introduction

The keyboard tutorial covered ButtonInput for key presses. Mouse input works similarly but adds motion and scroll events.

Key types:

  • Res[ButtonInput] with MouseButton: Check click state (aliased as MouseInput).
  • MessageReader[MouseMotion]: Read mouse movement deltas.
  • MessageReader[MouseWheel]: Read scroll wheel events.
  • AccumulatedMouseMotion: Resource with total mouse delta for the frame.
from pybevy.prelude import *
from pybevy.input import MouseButton, MouseInput, MouseMotion, MouseWheel, AccumulatedMouseMotion

Tracking Mouse Clicks

MouseInput (aliased from Res[ButtonInput]) works just like keyboard input: .pressed(), .just_pressed(), .just_released().

@component
class Clickable(Component):
    pass
 
def mouse_click_system(
    mouse: Res[MouseInput],
):
    if mouse.just_pressed(MouseButton.Left()):
        print("Left click!")
    if mouse.just_pressed(MouseButton.Right()):
        print("Right click!")
    if mouse.just_pressed(MouseButton.Middle()):
        print("Middle click!")

Reading Mouse Motion

Mouse motion events give you the raw pixel delta each frame. This is useful for camera look, dragging, and drawing.

@component
class Player(Component):
    pass
 
def setup(commands: Commands, asset_server: AssetServer):
    commands.spawn(Camera2d())
    commands.spawn(
        Sprite(image=asset_server.load_image("branding/icon.png")),
        Player(),
    )
 
def drag_with_mouse(
    mouse: Res[MouseInput],
    motion: Res[AccumulatedMouseMotion],
    query: Query[Mut[Transform], With[Player]],
):
    # Drag sprite while left mouse button is held
    if mouse.pressed(MouseButton.Left()):
        for transform in query:
            transform.translation.x += motion.delta.x
            # Y is inverted: screen Y goes down, world Y goes up
            transform.translation.y -= motion.delta.y

Scroll Wheel for Zooming

Mouse wheel events have x and y components. The y value is the vertical scroll — positive for scroll up, negative for scroll down.

def zoom_camera(
    wheel: MessageReader[MouseWheel],
    query: Query[Mut[Transform], With[Camera2d]],
):
    for event in wheel:
        for transform in query:
            # Scale the camera: scroll up = zoom in, scroll down = zoom out
            zoom_factor = 1.0 - event.y * 0.1
            transform.scale = transform.scale * zoom_factor

Running the App

When you run this, drag the sprite with left click and zoom with the scroll wheel.

@entrypoint
def main(app: App) -> App:
    return (
        app
        .add_plugins(DefaultPlugins)
        .add_systems(Startup, setup)
        .add_systems(Update, (mouse_click_system, drag_with_mouse, zoom_camera))
    )
 
if __name__ == "__main__":
    main().run()