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.UiPickingPlugininbevy_ui— UI rectangle picking.SpritePickingPlugininbevy_sprite— 2D sprite picking.MeshPickingPlugininbevy_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:
- Pointer state updates from raw input.
- Each registered backend computes hits for each pointer. Backends return
(Entity, HitData)lists with their own depth metric. - The core ranks the hits across backends (priority + depth).
- State changes (pointer moves on/off an entity, button changes while over) generate high-level events.
- 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
Pickableopt-in component for some backends, orPickingBehavior::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
MeshPickingPluginis added (it isn't inDefaultPluginsautomatically) and the mesh has aRayCastBackfacessetting compatible with your normals.
See also
bevy_picking— core.bevy_input— pointer source events.bevy_ui,bevy_sprite,bevy_pbr— backends.bevy_dev_tools— debug picking visualization.
Built by Factory AutoWiki from public repository content. It is a generated preview for codebase exploration, not source-maintained documentation.