bevy_picking/
lib.rs

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