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 trigger: Trigger<Pointer<Click>>| {
21//!         println!("I was just clicked!");
22//!         // Get the underlying pointer event data
23//!         let click_event: &Pointer<Click> = trigger.event();
24//!         // Stop the event from bubbling up the entity hierarchy
25//!         trigger.propagate(false);
26//!     });
27//! ```
28//!
29//! At its core, this crate provides a robust abstraction for computing picking state regardless of
30//! pointing devices, or what you are hit testing against. It is designed to work with any input,
31//! including mouse, touch, pens, or virtual pointers controlled by gamepads.
32//!
33//! ## Expressive Events
34//!
35//! Although the events in this module (see [`events`]) can be listened to with normal
36//! `EventReader`s, using observers is often more expressive, with less boilerplate. This is because
37//! observers allow you to attach event handling logic to specific entities, as well as make use of
38//! event bubbling.
39//!
40//! When events are generated, they bubble up the entity hierarchy starting from their target, until
41//! they reach the root or bubbling is halted with a call to
42//! [`Trigger::propagate`](bevy_ecs::observer::Trigger::propagate). See [`Observer`] for details.
43//!
44//! This allows you to run callbacks when any children of an entity are interacted with, and leads
45//! to succinct, expressive code:
46//!
47//! ```
48//! # use bevy_ecs::prelude::*;
49//! # use bevy_transform::prelude::*;
50//! # use bevy_picking::prelude::*;
51//! # #[derive(Event)]
52//! # struct Greeting;
53//! fn setup(mut commands: Commands) {
54//!     commands.spawn(Transform::default())
55//!         // Spawn your entity here, e.g. a Mesh.
56//!         // When dragged, mutate the `Transform` component on the dragged target entity:
57//!         .observe(|trigger: Trigger<Pointer<Drag>>, mut transforms: Query<&mut Transform>| {
58//!             let mut transform = transforms.get_mut(trigger.target()).unwrap();
59//!             let drag = trigger.event();
60//!             transform.rotate_local_y(drag.delta.x / 50.0);
61//!         })
62//!         .observe(|trigger: Trigger<Pointer<Click>>, mut commands: Commands| {
63//!             println!("Entity {} goes BOOM!", trigger.target());
64//!             commands.entity(trigger.target()).despawn();
65//!         })
66//!         .observe(|trigger: Trigger<Pointer<Over>>, mut events: EventWriter<Greeting>| {
67//!             events.write(Greeting);
68//!         });
69//! }
70//! ```
71//!
72//! ## Modularity
73//!
74//! #### Mix and Match Hit Testing Backends
75//!
76//! The plugin attempts to handle all the hard parts for you, all you need to do is tell it when a
77//! pointer is hitting any entities. Multiple backends can be used at the same time! [Use this
78//! simple API to write your own backend](crate::backend) in about 100 lines of code.
79//!
80//! #### Input Agnostic
81//!
82//! Picking provides a generic Pointer abstraction, which is useful for reacting to many different
83//! types of input devices. Pointers can be controlled with anything, whether it's the included
84//! mouse or touch inputs, or a custom gamepad input system you write yourself to control a virtual
85//! pointer.
86//!
87//! ## Robustness
88//!
89//! In addition to these features, this plugin also correctly handles multitouch, multiple windows,
90//! multiple cameras, viewports, and render layers. Using this as a library allows you to write a
91//! picking backend that can interoperate with any other picking backend.
92//!
93//! # Getting Started
94//!
95//! TODO: This section will need to be re-written once more backends are introduced.
96//!
97//! #### Next Steps
98//!
99//! To learn more, take a look at the examples in the
100//! [examples](https://github.com/bevyengine/bevy/tree/main/examples/picking). You can read the next
101//! section to understand how the plugin works.
102//!
103//! # The Picking Pipeline
104//!
105//! This plugin is designed to be extremely modular. To do so, it works in well-defined stages that
106//! form a pipeline, where events are used to pass data between each stage.
107//!
108//! #### Pointers ([`pointer`](mod@pointer))
109//!
110//! The first stage of the pipeline is to gather inputs and update pointers. This stage is
111//! ultimately responsible for generating [`PointerInput`](pointer::PointerInput) events. The
112//! provided crate does this automatically for mouse, touch, and pen inputs. If you wanted to
113//! implement your own pointer, controlled by some other input, you can do that here. The ordering
114//! of events within the [`PointerInput`](pointer::PointerInput) stream is meaningful for events
115//! with the same [`PointerId`](pointer::PointerId), but not between different pointers.
116//!
117//! Because pointer positions and presses are driven by these events, you can use them to mock
118//! inputs for testing.
119//!
120//! After inputs are generated, they are then collected to update the current
121//! [`PointerLocation`](pointer::PointerLocation) for each pointer.
122//!
123//! #### Backend ([`backend`])
124//!
125//! A picking backend only has one job: reading [`PointerLocation`](pointer::PointerLocation)
126//! components, and producing [`PointerHits`](backend::PointerHits). You can find all documentation
127//! and types needed to implement a backend at [`backend`].
128//!
129//! You will eventually need to choose which picking backend(s) you want to use. This crate does not
130//! supply any backends, and expects you to select some from the other bevy crates or the
131//! third-party ecosystem.
132//!
133//! It's important to understand that you can mix and match backends! For example, you might have a
134//! backend for your UI, and one for the 3d scene, with each being specialized for their purpose.
135//! Bevy provides some backends out of the box, but you can even write your own. It's been made as
136//! easy as possible intentionally; the `bevy_mod_raycast` backend is 50 lines of code.
137//!
138//! #### Hover ([`hover`])
139//!
140//! The next step is to use the data from the backends, combine and sort the results, and determine
141//! what each cursor is hovering over, producing a [`HoverMap`](`crate::hover::HoverMap`). Note that
142//! just because a pointer is over an entity, it is not necessarily *hovering* that entity. Although
143//! multiple backends may be reporting that a pointer is hitting an entity, the hover system needs
144//! to determine which entities are actually being hovered by this pointer based on the pick depth,
145//! order of the backend, and the optional [`Pickable`] component of the entity. In other
146//! words, if one entity is in front of another, usually only the topmost one will be hovered.
147//!
148//! #### Events ([`events`])
149//!
150//! In the final step, the high-level pointer events are generated, such as events that trigger when
151//! a pointer hovers or clicks an entity. These simple events are then used to generate more complex
152//! events for dragging and dropping.
153//!
154//! Because it is completely agnostic to the earlier stages of the pipeline, you can easily extend
155//! the plugin with arbitrary backends and input methods, yet still use all the high level features.
156
157#![deny(missing_docs)]
158
159extern crate alloc;
160
161pub mod backend;
162pub mod events;
163pub mod hover;
164pub mod input;
165#[cfg(feature = "bevy_mesh_picking_backend")]
166pub mod mesh_picking;
167pub mod pointer;
168pub mod window;
169
170use bevy_app::{prelude::*, PluginGroupBuilder};
171use bevy_ecs::prelude::*;
172use bevy_reflect::prelude::*;
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 = "bevy_mesh_picking_backend")]
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::*, input::PointerInputPlugin, pointer::PointerButton, DefaultPickingPlugins,
187        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, 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 PickSet {
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 [`PickSet::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 [`crate::backend::PointerHits`]
284/// that [`PickingPlugin`] and [`InteractionPlugin`] will refine into [`bevy_ecs::observer::Trigger`]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::default())
292            .add(PickingPlugin::default())
293            .add(InteractionPlugin)
294    }
295}
296
297/// This plugin sets up the core picking infrastructure. It receives input events, and provides the shared
298/// types used by other picking plugins.
299///
300/// This plugin contains several settings, and is added to the world as a resource after initialization. You
301/// can configure picking settings at runtime through the resource.
302#[derive(Copy, Clone, Debug, Resource, Reflect)]
303#[reflect(Resource, Default, Debug, Clone)]
304pub struct PickingPlugin {
305    /// Enables and disables all picking features.
306    pub is_enabled: bool,
307    /// Enables and disables input collection.
308    pub is_input_enabled: bool,
309    /// Enables and disables updating interaction states of entities.
310    pub is_hover_enabled: bool,
311    /// Enables or disables picking for window entities.
312    pub is_window_picking_enabled: bool,
313}
314
315impl PickingPlugin {
316    /// Whether or not input collection systems should be running.
317    pub fn input_should_run(state: Res<Self>) -> bool {
318        state.is_input_enabled && state.is_enabled
319    }
320
321    /// Whether or not systems updating entities' [`PickingInteraction`](hover::PickingInteraction)
322    /// component should be running.
323    pub fn hover_should_run(state: Res<Self>) -> bool {
324        state.is_hover_enabled && state.is_enabled
325    }
326
327    /// Whether or not window entities should receive pick events.
328    pub fn window_picking_should_run(state: Res<Self>) -> bool {
329        state.is_window_picking_enabled && state.is_enabled
330    }
331}
332
333impl Default for PickingPlugin {
334    fn default() -> Self {
335        Self {
336            is_enabled: true,
337            is_input_enabled: true,
338            is_hover_enabled: true,
339            is_window_picking_enabled: true,
340        }
341    }
342}
343
344impl Plugin for PickingPlugin {
345    fn build(&self, app: &mut App) {
346        app.insert_resource(*self)
347            .init_resource::<pointer::PointerMap>()
348            .init_resource::<backend::ray::RayMap>()
349            .add_event::<pointer::PointerInput>()
350            .add_event::<backend::PointerHits>()
351            // Rather than try to mark all current and future backends as ambiguous with each other,
352            // we allow them to send their hits in any order. These are later sorted, so submission
353            // order doesn't matter. See `PointerHits` docs for caveats.
354            .allow_ambiguous_resource::<Events<backend::PointerHits>>()
355            .add_systems(
356                PreUpdate,
357                (
358                    pointer::update_pointer_map,
359                    pointer::PointerInput::receive,
360                    backend::ray::RayMap::repopulate.after(pointer::PointerInput::receive),
361                )
362                    .in_set(PickSet::ProcessInput),
363            )
364            .add_systems(
365                PreUpdate,
366                window::update_window_hits
367                    .run_if(Self::window_picking_should_run)
368                    .in_set(PickSet::Backend),
369            )
370            .configure_sets(
371                First,
372                (PickSet::Input, PickSet::PostInput)
373                    .after(bevy_time::TimeSystem)
374                    .after(bevy_ecs::event::EventUpdates)
375                    .chain(),
376            )
377            .configure_sets(
378                PreUpdate,
379                (
380                    PickSet::ProcessInput.run_if(Self::input_should_run),
381                    PickSet::Backend,
382                    PickSet::Hover.run_if(Self::hover_should_run),
383                    PickSet::PostHover,
384                    PickSet::Last,
385                )
386                    .chain(),
387            )
388            .register_type::<Self>()
389            .register_type::<Pickable>()
390            .register_type::<hover::PickingInteraction>()
391            .register_type::<pointer::PointerId>()
392            .register_type::<pointer::PointerLocation>()
393            .register_type::<pointer::PointerPress>()
394            .register_type::<pointer::PointerInteraction>()
395            .register_type::<backend::ray::RayId>();
396    }
397}
398
399/// Generates [`Pointer`](events::Pointer) events and handles event bubbling.
400#[derive(Default)]
401pub struct InteractionPlugin;
402
403impl Plugin for InteractionPlugin {
404    fn build(&self, app: &mut App) {
405        use events::*;
406        use hover::{generate_hovermap, update_interactions};
407
408        app.init_resource::<hover::HoverMap>()
409            .init_resource::<hover::PreviousHoverMap>()
410            .init_resource::<PointerState>()
411            .add_event::<Pointer<Cancel>>()
412            .add_event::<Pointer<Click>>()
413            .add_event::<Pointer<Pressed>>()
414            .add_event::<Pointer<DragDrop>>()
415            .add_event::<Pointer<DragEnd>>()
416            .add_event::<Pointer<DragEnter>>()
417            .add_event::<Pointer<Drag>>()
418            .add_event::<Pointer<DragLeave>>()
419            .add_event::<Pointer<DragOver>>()
420            .add_event::<Pointer<DragStart>>()
421            .add_event::<Pointer<Move>>()
422            .add_event::<Pointer<Out>>()
423            .add_event::<Pointer<Over>>()
424            .add_event::<Pointer<Released>>()
425            .add_event::<Pointer<Scroll>>()
426            .add_systems(
427                PreUpdate,
428                (generate_hovermap, update_interactions, pointer_events)
429                    .chain()
430                    .in_set(PickSet::Hover),
431            );
432    }
433}