Open-Source Wikis

/

Bevy

/

Features

/

Picking

bevyengine/bevy

Picking

Bevy's picking system: pointers (mouse, touch, gamepad) → hits on entities → events. Pluggable backends per domain (UI, sprite, mesh).

Pieces

  • bevy_picking — core: pointer state, hit dispatch, events.
  • UiPickingPlugin in bevy_ui — UI rectangle picking.
  • SpritePickingPlugin in bevy_sprite — 2D sprite picking.
  • MeshPickingPlugin in bevy_pbr — 3D ray-cast mesh picking.

Flow

sequenceDiagram
    participant Input as bevy_input
    participant Pointers as Pointer entities
    participant Backends as Picking backends
    participant Hover as Hover map
    participant Obs as Observers

    Input->>Pointers: cursor moved / touch / button
    Pointers->>Backends: ray + screen pos
    Backends->>Hover: ranked HitData
    Hover->>Obs: Pointer<Click> / Pointer<Drag> / ...

Each frame:

  1. Pointer state updates from raw input.
  2. Each registered backend computes hits for each pointer. Backends return (Entity, HitData) lists with their own depth metric.
  3. The core ranks the hits across backends (priority + depth).
  4. State changes (pointer moves on/off an entity, button changes while over) generate high-level events.
  5. Events are dispatched as Pointer<E> events through the observer system, where each entity in the hover stack can react.

Listening to picks

use bevy::prelude::*;
use bevy::picking::events::{Pointer, Click, Over, Out};

fn setup(mut commands: Commands) {
    commands
        .spawn((
            Sprite::default(),
            Transform::default(),
        ))
        .observe(|trigger: Trigger<Pointer<Click>>| {
            println!("Clicked entity {:?}", trigger.target());
        })
        .observe(|trigger: Trigger<Pointer<Over>>| {
            println!("Hovered entity {:?}", trigger.target());
        });
}

Observers are attached per-entity; the picking core handles event propagation.

Built-in events

Event When
Pointer<Over> Pointer enters the entity.
Pointer<Out> Pointer leaves the entity.
Pointer<Move> Pointer moves while over.
Pointer<Down> Mouse/touch button pressed while over.
Pointer<Up> Mouse/touch button released while over.
Pointer<Click> Down + Up on the same entity within a threshold.
Pointer<DragStart> / Drag / DragEnd Drag lifecycle.
Pointer<DragOver> / DragEnter / DragLeave / DragDrop Drag-target events.
Pointer<Scroll> Scroll wheel while over.
Pointer<Cancel> Pointer was canceled (e.g., touch interrupted).

Backends

UI picking

Tests the cursor position against laid-out UI rectangles in screen space. Honors ZIndex and the implicit stack order.

Sprite picking

Tests the cursor against sprite bounding boxes in world space, taking into account transforms and the camera projection. Honors transparency thresholds for pixel-precise picking when Pickable::pixel_perfect is set.

Mesh picking

Casts a ray from the cursor through the camera and intersects mesh triangles. Default backend uses BVH-accelerated ray-cast. Per-mesh RayCastSettings control which entities are considered.

Custom backends

Implement PickingBackend, register a system that fills PickingBackendOutput, and call app.add_plugins(MyBackendPlugin). The core takes care of ranking and dispatch. Per-backend priority lets you control which backend wins when multiple report hits at the same screen position.

Common pitfalls

  • No events firing — the entity needs the relevant Pickable opt-in component for some backends, or PickingBehavior::default() to confirm it accepts hits.
  • UI eats clicks — UI nodes block picking by default. Set PickingBehavior { is_hoverable: true, should_block_lower: false, .. } to let clicks pass through.
  • 3D picks miss — ensure MeshPickingPlugin is added (it isn't in DefaultPlugins automatically) and the mesh has a RayCastBackfaces setting compatible with your normals.

See also

Built by Factory AutoWiki from public repository content. It is a generated preview for codebase exploration, not source-maintained documentation.

Picking – Bevy wiki | Factory