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}