Skip to main content

bevy_picking/
lib.rs

1//! This crate provides 'picking' capabilities for the Bevy game engine, allowing pointers to
2//! interact with entities using hover, click, and drag events.
3//!
4//! ## Overview
5//!
6//! In the simplest case, this plugin allows you to click on things in the scene. However, it also
7//! allows you to express more complex interactions, like detecting when a touch input drags a UI
8//! element and drops it on a 3d mesh rendered to a different camera.
9//!
10//! Pointer events bubble up the entity hierarchy and can be used with observers, allowing you to
11//! succinctly express rich interaction behaviors by attaching pointer callbacks to entities:
12//!
13//! ```rust
14//! # use bevy_ecs::prelude::*;
15//! # use bevy_picking::prelude::*;
16//! # #[derive(Component)]
17//! # struct MyComponent;
18//! # let mut world = World::new();
19//! world.spawn(MyComponent)
20//!     .observe(|mut click: On<PointerClick>| {
21//!         // Read the underlying pointer event data
22//!         println!("Pointer {:?} was just clicked!", click.pointer.id);
23//!         // Stop the event from bubbling up the entity hierarchy
24//!         click.propagate(false);
25//!     });
26//! ```
27//!
28//! At its core, this crate provides a robust abstraction for computing picking state regardless of
29//! pointing devices, or what you are hit testing against. It is designed to work with any input,
30//! including mouse, touch, pens, or virtual pointers controlled by gamepads.
31//!
32//! ## Expressive Events
33//!
34//! Although the events in this module (see [`events`]) can be listened to with normal
35//! `MessageReader`s, using observers is often more expressive, with less boilerplate. This is because
36//! observers allow you to attach event handling logic to specific entities, as well as make use of
37//! event bubbling.
38//!
39//! When events are generated, they bubble up the entity hierarchy starting from their target, until
40//! they reach the root or bubbling is halted with a call to
41//! [`On::propagate`](bevy_ecs::observer::On::propagate). See [`Observer`] for details.
42//!
43//! This allows you to run callbacks when any children of an entity are interacted with, and leads
44//! to succinct, expressive code:
45//!
46//! ```
47//! # use bevy_ecs::prelude::*;
48//! # use bevy_transform::prelude::*;
49//! # use bevy_picking::prelude::*;
50//! # #[derive(Message)]
51//! # struct Greeting;
52//! fn setup(mut commands: Commands) {
53//!     commands.spawn(Transform::default())
54//!         // Spawn your entity here, e.g. a `Mesh3d`.
55//!         // When dragged, mutate the `Transform` component on the dragged target entity:
56//!         .observe(|drag: On<PointerDrag>, mut transforms: Query<&mut Transform>| {
57//!             let mut transform = transforms.get_mut(drag.entity).unwrap();
58//!             transform.rotate_local_y(drag.delta.x / 50.0);
59//!         })
60//!         .observe(|click: On<PointerClick>, mut commands: Commands| {
61//!             println!("Entity {} goes BOOM!", click.entity);
62//!             commands.entity(click.entity).despawn();
63//!         })
64//!         .observe(|over: On<PointerOver>, mut greetings: MessageWriter<Greeting>| {
65//!             greetings.write(Greeting);
66//!         });
67//! }
68//! ```
69//!
70//! ## Modularity
71//!
72//! #### Mix and Match Hit Testing Backends
73//!
74//! The plugin attempts to handle all the hard parts for you, all you need to do is tell it when a
75//! pointer is hitting any entities. Multiple backends can be used at the same time! [Use this
76//! simple API to write your own backend](crate::backend) in about 100 lines of code.
77//!
78//! #### Input Agnostic
79//!
80//! Picking provides a generic Pointer abstraction, which is useful for reacting to many different
81//! types of input devices. Pointers can be controlled with anything, whether it's the included
82//! mouse or touch inputs, or a custom gamepad input system you write yourself to control a virtual
83//! pointer.
84//!
85//! ## Robustness
86//!
87//! In addition to these features, this plugin also correctly handles multitouch, multiple windows,
88//! multiple cameras, viewports, and render layers. Using this as a library allows you to write a
89//! picking backend that can interoperate with any other picking backend.
90//!
91//! # Getting Started
92//!
93//! TODO: This section will need to be re-written once more backends are introduced.
94//!
95//! #### Next Steps
96//!
97//! To learn more, take a look at the examples in the
98//! [examples](https://github.com/bevyengine/bevy/tree/main/examples/picking). You can read the next
99//! section to understand how the plugin works.
100//!
101//! # The Picking Pipeline
102//!
103//! This plugin is designed to be extremely modular. To do so, it works in well-defined stages that
104//! form a pipeline, where events are used to pass data between each stage.
105//!
106//! #### Pointers ([`pointer`](mod@pointer))
107//!
108//! The first stage of the pipeline is to gather inputs and update pointers. This stage is
109//! ultimately responsible for generating [`PointerInput`](pointer::PointerInput) events. The
110//! provided crate does this automatically for mouse, touch, and pen inputs. If you wanted to
111//! implement your own pointer, controlled by some other input, you can do that here. The ordering
112//! of events within the [`PointerInput`](pointer::PointerInput) stream is meaningful for events
113//! with the same [`PointerId`](pointer::PointerId), but not between different pointers.
114//!
115//! Because pointer positions and presses are driven by these events, you can use them to mock
116//! inputs for testing.
117//!
118//! After inputs are generated, they are then collected to update the current
119//! [`PointerLocation`](pointer::PointerLocation) for each pointer.
120//!
121//! #### Backend ([`backend`])
122//!
123//! A picking backend only has one job: reading [`PointerLocation`](pointer::PointerLocation)
124//! components, and producing [`PointerHits`](backend::PointerHits). You can find all documentation
125//! and types needed to implement a backend at [`backend`].
126//!
127//! You will eventually need to choose which picking backend(s) you want to use. This crate does not
128//! supply any backends, and expects you to select some from the other bevy crates or the
129//! third-party ecosystem.
130//!
131//! It's important to understand that you can mix and match backends! For example, you might have a
132//! backend for your UI, and one for the 3d scene, with each being specialized for their purpose.
133//! Bevy provides some backends out of the box, but you can even write your own. It's been made as
134//! easy as possible intentionally; the `bevy_mod_raycast` backend is 50 lines of code.
135//!
136//! #### Hover ([`hover`])
137//!
138//! The next step is to use the data from the backends, combine and sort the results, and determine
139//! what each cursor is hovering over, producing a [`HoverMap`](`crate::hover::HoverMap`). Note that
140//! just because a pointer is over an entity, it is not necessarily *hovering* that entity. Although
141//! multiple backends may be reporting that a pointer is hitting an entity, the hover system needs
142//! to determine which entities are actually being hovered by this pointer based on the pick depth,
143//! order of the backend, and the optional [`Pickable`] component of the entity. In other
144//! words, if one entity is in front of another, usually only the topmost one will be hovered.
145//!
146//! #### Events ([`events`])
147//!
148//! In the final step, the high-level pointer events are generated, such as events that trigger when
149//! a pointer hovers or clicks an entity. These simple events are then used to generate more complex
150//! events for dragging and dropping.
151//!
152//! Because it is completely agnostic to the earlier stages of the pipeline, you can easily extend
153//! the plugin with arbitrary backends and input methods, yet still use all the high level features.
154
155extern crate alloc;
156
157use core::time::Duration;
158
159pub mod backend;
160pub mod cursor;
161pub mod events;
162pub mod hover;
163pub mod input;
164#[cfg(feature = "mesh_picking")]
165pub mod mesh_picking;
166pub mod pointer;
167pub mod window;
168
169use bevy_app::{prelude::*, PluginGroupBuilder};
170use bevy_ecs::prelude::*;
171use bevy_reflect::prelude::*;
172use hover::{update_is_directly_hovered, update_is_hovered};
173
174/// The picking prelude.
175///
176/// This includes the most common types in this crate, re-exported for your convenience.
177pub mod prelude {
178    #[cfg(feature = "mesh_picking")]
179    #[doc(hidden)]
180    pub use crate::mesh_picking::{
181        ray_cast::{MeshRayCast, MeshRayCastSettings, RayCastBackfaces, RayCastVisibility},
182        MeshPickingCamera, MeshPickingPlugin, MeshPickingSettings,
183    };
184    #[doc(hidden)]
185    pub use crate::{
186        events::*, hover::PointerCaptureMap, input::PointerInputPlugin, pointer::PointerButton,
187        DefaultPickingPlugins, InteractionPlugin, Pickable, PickingPlugin,
188    };
189}
190
191/// An optional component that marks an entity as usable by a backend, and overrides default
192/// picking behavior for an entity.
193///
194/// This allows you to make an entity non-hoverable, or allow items below it to be hovered.
195///
196/// See the documentation on the fields for more details.
197#[derive(Component, Debug, Clone, Copy, Reflect, PartialEq, Eq)]
198#[reflect(Component, Default, Debug, PartialEq, Clone)]
199pub struct Pickable {
200    /// Should this entity block entities below it from being picked?
201    ///
202    /// This is useful if you want picking to continue hitting entities below this one. Normally,
203    /// only the topmost entity under a pointer can be hovered, but this setting allows the pointer
204    /// to hover multiple entities, from nearest to farthest, stopping as soon as it hits an entity
205    /// that blocks lower entities.
206    ///
207    /// Note that the word "lower" here refers to entities that have been reported as hit by any
208    /// picking backend, but are at a lower depth than the current one. This is different from the
209    /// concept of event bubbling, as it works irrespective of the entity hierarchy.
210    ///
211    /// For example, if a pointer is over a UI element, as well as a 3d mesh, backends will report
212    /// hits for both of these entities. Additionally, the hits will be sorted by the camera order,
213    /// so if the UI is drawing on top of the 3d mesh, the UI will be "above" the mesh. When hovering
214    /// is computed, the UI element will be checked first to see if it this field is set to block
215    /// lower entities. If it does (default), the hovering system will stop there, and only the UI
216    /// element will be marked as hovered. However, if this field is set to `false`, both the UI
217    /// element *and* the mesh will be marked as hovered.
218    ///
219    /// Entities without the [`Pickable`] component will block by default.
220    pub should_block_lower: bool,
221
222    /// If this is set to `false` and `should_block_lower` is set to true, this entity will block
223    /// lower entities from being interacted and at the same time will itself not emit any events.
224    ///
225    /// Note that the word "lower" here refers to entities that have been reported as hit by any
226    /// picking backend, but are at a lower depth than the current one. This is different from the
227    /// concept of event bubbling, as it works irrespective of the entity hierarchy.
228    ///
229    /// For example, if a pointer is over a UI element, and this field is set to `false`, it will
230    /// not be marked as hovered, and consequently will not emit events nor will any picking
231    /// components mark it as hovered. This can be combined with the other field
232    /// [`Self::should_block_lower`], which is orthogonal to this one.
233    ///
234    /// Entities without the [`Pickable`] component are hoverable by default.
235    pub is_hoverable: bool,
236}
237
238impl Pickable {
239    /// This entity will not block entities beneath it, nor will it emit events.
240    ///
241    /// If a backend reports this entity as being hit, the picking plugin will completely ignore it.
242    pub const IGNORE: Self = Self {
243        should_block_lower: false,
244        is_hoverable: false,
245    };
246}
247
248impl Default for Pickable {
249    fn default() -> Self {
250        Self {
251            should_block_lower: true,
252            is_hoverable: true,
253        }
254    }
255}
256
257/// Groups the stages of the picking process under shared labels.
258#[derive(Debug, Hash, PartialEq, Eq, Clone, SystemSet)]
259pub enum PickingSystems {
260    /// Produces pointer input events. In the [`First`] schedule.
261    Input,
262    /// Runs after input events are generated but before commands are flushed. In the [`First`]
263    /// schedule.
264    PostInput,
265    /// Receives and processes pointer input events. In the [`PreUpdate`] schedule.
266    ProcessInput,
267    /// Reads inputs and produces [`backend::PointerHits`]s. In the [`PreUpdate`] schedule.
268    Backend,
269    /// Reads [`backend::PointerHits`]s, and updates the hovermap, selection, and highlighting states. In
270    /// the [`PreUpdate`] schedule.
271    Hover,
272    /// Runs after all the [`PickingSystems::Hover`] systems are done, before event listeners are triggered. In the
273    /// [`PreUpdate`] schedule.
274    PostHover,
275    /// Runs after all other picking sets. In the [`PreUpdate`] schedule.
276    Last,
277}
278
279/// One plugin that contains the [`PointerInputPlugin`](input::PointerInputPlugin), [`PickingPlugin`]
280/// and the [`InteractionPlugin`], this is probably the plugin that will be most used.
281///
282/// Note: for any of these plugins to work, they require a picking backend to be active,
283/// The picking backend is responsible to turn an input, into a [`PointerHits`](`crate::backend::PointerHits`)
284/// that [`PickingPlugin`] and [`InteractionPlugin`] will refine into [`bevy_ecs::observer::On`]s.
285#[derive(Default)]
286pub struct DefaultPickingPlugins;
287
288impl PluginGroup for DefaultPickingPlugins {
289    fn build(self) -> PluginGroupBuilder {
290        PluginGroupBuilder::start::<Self>()
291            .add(input::PointerInputPlugin)
292            .add(PickingPlugin)
293            .add(InteractionPlugin)
294    }
295}
296
297#[derive(Copy, Clone, Debug, Resource, Reflect)]
298#[reflect(Resource, Default, Debug, Clone)]
299/// Controls the behavior of picking
300///
301/// ## Custom initialization
302/// ```
303/// # use bevy_app::App;
304/// # use bevy_picking::{PickingSettings, PickingPlugin};
305/// # use core::time::Duration;
306/// App::new()
307///     .insert_resource(PickingSettings {
308///         is_enabled: true,
309///         is_input_enabled: false,
310///         is_hover_enabled: true,
311///         is_window_picking_enabled: false,
312///         multi_click_interval: Duration::from_millis(450),
313///     })
314///     // or DefaultPlugins
315///     .add_plugins(PickingPlugin);
316/// ```
317pub struct PickingSettings {
318    /// Enables and disables all picking features.
319    pub is_enabled: bool,
320    /// Enables and disables input collection.
321    pub is_input_enabled: bool,
322    /// Enables and disables updating interaction states of entities.
323    pub is_hover_enabled: bool,
324    /// Enables or disables picking for window entities.
325    pub is_window_picking_enabled: bool,
326    /// Maximum time between presses or clicks for them to count as consecutive.
327    pub multi_click_interval: Duration,
328}
329
330impl PickingSettings {
331    /// Whether or not input collection systems should be running.
332    pub fn input_should_run(state: Res<Self>) -> bool {
333        state.is_input_enabled && state.is_enabled
334    }
335
336    /// Whether or not systems updating entities' [`PickingInteraction`](hover::PickingInteraction)
337    /// component should be running.
338    pub fn hover_should_run(state: Res<Self>) -> bool {
339        state.is_hover_enabled && state.is_enabled
340    }
341
342    /// Whether or not window entities should receive pick events.
343    pub fn window_picking_should_run(state: Res<Self>) -> bool {
344        state.is_window_picking_enabled && state.is_enabled
345    }
346}
347
348impl Default for PickingSettings {
349    fn default() -> Self {
350        Self {
351            is_enabled: true,
352            is_input_enabled: true,
353            is_hover_enabled: true,
354            is_window_picking_enabled: true,
355            multi_click_interval: Duration::from_millis(500),
356        }
357    }
358}
359
360/// This plugin sets up the core picking infrastructure. It receives input events, and provides the shared
361/// types used by other picking plugins.
362///
363/// Behavior of picking can be controlled by modifying [`PickingSettings`].
364///
365/// [`PickingSettings`] will be initialized with default values if it
366/// is not present at the moment this is added to the app.
367pub struct PickingPlugin;
368
369impl Plugin for PickingPlugin {
370    fn build(&self, app: &mut App) {
371        app.init_resource::<PickingSettings>()
372            .init_resource::<pointer::PointerMap>()
373            .init_resource::<backend::ray::RayMap>()
374            .add_message::<pointer::PointerInput>()
375            .add_message::<backend::PointerHits>()
376            // Rather than try to mark all current and future backends as ambiguous with each other,
377            // we allow them to send their hits in any order. These are later sorted, so submission
378            // order doesn't matter. See `PointerHits` docs for caveats.
379            .allow_ambiguous_resource::<Messages<backend::PointerHits>>()
380            .add_systems(
381                PreUpdate,
382                (
383                    pointer::PointerInput::receive,
384                    backend::ray::RayMap::repopulate.after(pointer::PointerInput::receive),
385                )
386                    .in_set(PickingSystems::ProcessInput),
387            )
388            .add_systems(
389                PreUpdate,
390                window::update_window_hits
391                    .run_if(PickingSettings::window_picking_should_run)
392                    .in_set(PickingSystems::Backend),
393            )
394            .configure_sets(
395                First,
396                (PickingSystems::Input, PickingSystems::PostInput)
397                    .after(bevy_time::TimeSystems)
398                    .after(bevy_ecs::message::MessageUpdateSystems)
399                    .chain(),
400            )
401            .configure_sets(
402                PreUpdate,
403                (
404                    PickingSystems::ProcessInput.run_if(PickingSettings::input_should_run),
405                    PickingSystems::Backend,
406                    PickingSystems::Hover.run_if(PickingSettings::hover_should_run),
407                    PickingSystems::PostHover,
408                    PickingSystems::Last,
409                )
410                    .chain(),
411            );
412    }
413}
414
415/// Generates [`Pointer`](events::Pointer) events and handles event bubbling.
416#[derive(Default)]
417pub struct InteractionPlugin;
418
419impl Plugin for InteractionPlugin {
420    fn build(&self, app: &mut App) {
421        use events::*;
422        use hover::{generate_hovermap, update_interactions};
423
424        app.init_resource::<hover::HoverMap>()
425            .init_resource::<hover::PreviousHoverMap>()
426            .init_resource::<PickingSettings>()
427            .init_resource::<hover::PointerCaptureMap>()
428            .init_resource::<PointerState>()
429            .add_message::<PointerCancel>()
430            .add_message::<PointerClick>()
431            .add_message::<PointerPress>()
432            .add_message::<PointerDragDrop>()
433            .add_message::<PointerDragEnd>()
434            .add_message::<PointerDragEnter>()
435            .add_message::<PointerDrag>()
436            .add_message::<PointerDragLeave>()
437            .add_message::<PointerDragOver>()
438            .add_message::<PointerDragStart>()
439            .add_message::<PointerMove>()
440            .add_message::<PointerOut>()
441            .add_message::<PointerOver>()
442            .add_message::<PointerLeave>()
443            .add_message::<PointerEnter>()
444            .add_message::<PointerRelease>()
445            .add_message::<PointerScroll>()
446            .add_systems(
447                PreUpdate,
448                (
449                    generate_hovermap,
450                    update_interactions,
451                    (update_is_hovered, update_is_directly_hovered),
452                    pointer_events,
453                )
454                    .chain()
455                    .in_set(PickingSystems::Hover),
456            );
457    }
458}