//! This crate provides 'picking' capabilities for the Bevy game engine, allowing pointers to1//! interact with entities using hover, click, and drag events.2//!3//! ## Overview4//!5//! In the simplest case, this plugin allows you to click on things in the scene. However, it also6//! allows you to express more complex interactions, like detecting when a touch input drags a UI7//! element and drops it on a 3d mesh rendered to a different camera.8//!9//! Pointer events bubble up the entity hierarchy and can be used with observers, allowing you to10//! succinctly express rich interaction behaviors by attaching pointer callbacks to entities:11//!12//! ```rust13//! # use bevy_ecs::prelude::*;14//! # use bevy_picking::prelude::*;15//! # #[derive(Component)]16//! # struct MyComponent;17//! # let mut world = World::new();18//! world.spawn(MyComponent)19//! .observe(|mut event: On<Pointer<Click>>| {20//! // Read the underlying pointer event data21//! println!("Pointer {:?} was just clicked!", event.pointer_id);22//! // Stop the event from bubbling up the entity hierarchy23//! event.propagate(false);24//! });25//! ```26//!27//! At its core, this crate provides a robust abstraction for computing picking state regardless of28//! pointing devices, or what you are hit testing against. It is designed to work with any input,29//! including mouse, touch, pens, or virtual pointers controlled by gamepads.30//!31//! ## Expressive Events32//!33//! Although the events in this module (see [`events`]) can be listened to with normal34//! `EventReader`s, using observers is often more expressive, with less boilerplate. This is because35//! observers allow you to attach event handling logic to specific entities, as well as make use of36//! event bubbling.37//!38//! When events are generated, they bubble up the entity hierarchy starting from their target, until39//! they reach the root or bubbling is halted with a call to40//! [`On::propagate`](bevy_ecs::observer::On::propagate). See [`Observer`] for details.41//!42//! This allows you to run callbacks when any children of an entity are interacted with, and leads43//! to succinct, expressive code:44//!45//! ```46//! # use bevy_ecs::prelude::*;47//! # use bevy_transform::prelude::*;48//! # use bevy_picking::prelude::*;49//! # #[derive(BufferedEvent)]50//! # struct Greeting;51//! fn setup(mut commands: Commands) {52//! commands.spawn(Transform::default())53//! // Spawn your entity here, e.g. a `Mesh3d`.54//! // When dragged, mutate the `Transform` component on the dragged target entity:55//! .observe(|event: On<Pointer<Drag>>, mut transforms: Query<&mut Transform>| {56//! let mut transform = transforms.get_mut(event.entity()).unwrap();57//! transform.rotate_local_y(event.delta.x / 50.0);58//! })59//! .observe(|event: On<Pointer<Click>>, mut commands: Commands| {60//! println!("Entity {} goes BOOM!", event.entity());61//! commands.entity(event.entity()).despawn();62//! })63//! .observe(|event: On<Pointer<Over>>, mut events: EventWriter<Greeting>| {64//! events.write(Greeting);65//! });66//! }67//! ```68//!69//! ## Modularity70//!71//! #### Mix and Match Hit Testing Backends72//!73//! The plugin attempts to handle all the hard parts for you, all you need to do is tell it when a74//! pointer is hitting any entities. Multiple backends can be used at the same time! [Use this75//! simple API to write your own backend](crate::backend) in about 100 lines of code.76//!77//! #### Input Agnostic78//!79//! Picking provides a generic Pointer abstraction, which is useful for reacting to many different80//! types of input devices. Pointers can be controlled with anything, whether it's the included81//! mouse or touch inputs, or a custom gamepad input system you write yourself to control a virtual82//! pointer.83//!84//! ## Robustness85//!86//! In addition to these features, this plugin also correctly handles multitouch, multiple windows,87//! multiple cameras, viewports, and render layers. Using this as a library allows you to write a88//! picking backend that can interoperate with any other picking backend.89//!90//! # Getting Started91//!92//! TODO: This section will need to be re-written once more backends are introduced.93//!94//! #### Next Steps95//!96//! To learn more, take a look at the examples in the97//! [examples](https://github.com/bevyengine/bevy/tree/main/examples/picking). You can read the next98//! section to understand how the plugin works.99//!100//! # The Picking Pipeline101//!102//! This plugin is designed to be extremely modular. To do so, it works in well-defined stages that103//! form a pipeline, where events are used to pass data between each stage.104//!105//! #### Pointers ([`pointer`](mod@pointer))106//!107//! The first stage of the pipeline is to gather inputs and update pointers. This stage is108//! ultimately responsible for generating [`PointerInput`](pointer::PointerInput) events. The109//! provided crate does this automatically for mouse, touch, and pen inputs. If you wanted to110//! implement your own pointer, controlled by some other input, you can do that here. The ordering111//! of events within the [`PointerInput`](pointer::PointerInput) stream is meaningful for events112//! with the same [`PointerId`](pointer::PointerId), but not between different pointers.113//!114//! Because pointer positions and presses are driven by these events, you can use them to mock115//! inputs for testing.116//!117//! After inputs are generated, they are then collected to update the current118//! [`PointerLocation`](pointer::PointerLocation) for each pointer.119//!120//! #### Backend ([`backend`])121//!122//! A picking backend only has one job: reading [`PointerLocation`](pointer::PointerLocation)123//! components, and producing [`PointerHits`](backend::PointerHits). You can find all documentation124//! and types needed to implement a backend at [`backend`].125//!126//! You will eventually need to choose which picking backend(s) you want to use. This crate does not127//! supply any backends, and expects you to select some from the other bevy crates or the128//! third-party ecosystem.129//!130//! It's important to understand that you can mix and match backends! For example, you might have a131//! backend for your UI, and one for the 3d scene, with each being specialized for their purpose.132//! Bevy provides some backends out of the box, but you can even write your own. It's been made as133//! easy as possible intentionally; the `bevy_mod_raycast` backend is 50 lines of code.134//!135//! #### Hover ([`hover`])136//!137//! The next step is to use the data from the backends, combine and sort the results, and determine138//! what each cursor is hovering over, producing a [`HoverMap`](`crate::hover::HoverMap`). Note that139//! just because a pointer is over an entity, it is not necessarily *hovering* that entity. Although140//! multiple backends may be reporting that a pointer is hitting an entity, the hover system needs141//! to determine which entities are actually being hovered by this pointer based on the pick depth,142//! order of the backend, and the optional [`Pickable`] component of the entity. In other143//! words, if one entity is in front of another, usually only the topmost one will be hovered.144//!145//! #### Events ([`events`])146//!147//! In the final step, the high-level pointer events are generated, such as events that trigger when148//! a pointer hovers or clicks an entity. These simple events are then used to generate more complex149//! events for dragging and dropping.150//!151//! Because it is completely agnostic to the earlier stages of the pipeline, you can easily extend152//! the plugin with arbitrary backends and input methods, yet still use all the high level features.153154#![deny(missing_docs)]155156extern crate alloc;157158pub mod backend;159pub mod events;160pub mod hover;161pub mod input;162#[cfg(feature = "bevy_mesh_picking_backend")]163pub mod mesh_picking;164pub mod pointer;165pub mod window;166167use bevy_app::{prelude::*, PluginGroupBuilder};168use bevy_ecs::prelude::*;169use bevy_reflect::prelude::*;170use hover::{update_is_directly_hovered, update_is_hovered};171172/// The picking prelude.173///174/// This includes the most common types in this crate, re-exported for your convenience.175pub mod prelude {176#[cfg(feature = "bevy_mesh_picking_backend")]177#[doc(hidden)]178pub use crate::mesh_picking::{179ray_cast::{MeshRayCast, MeshRayCastSettings, RayCastBackfaces, RayCastVisibility},180MeshPickingCamera, MeshPickingPlugin, MeshPickingSettings,181};182#[doc(hidden)]183pub use crate::{184events::*, input::PointerInputPlugin, pointer::PointerButton, DefaultPickingPlugins,185InteractionPlugin, Pickable, PickingPlugin,186};187}188189/// An optional component that marks an entity as usable by a backend, and overrides default190/// picking behavior for an entity.191///192/// This allows you to make an entity non-hoverable, or allow items below it to be hovered.193///194/// See the documentation on the fields for more details.195#[derive(Component, Debug, Clone, Reflect, PartialEq, Eq)]196#[reflect(Component, Default, Debug, PartialEq, Clone)]197pub struct Pickable {198/// Should this entity block entities below it from being picked?199///200/// This is useful if you want picking to continue hitting entities below this one. Normally,201/// only the topmost entity under a pointer can be hovered, but this setting allows the pointer202/// to hover multiple entities, from nearest to farthest, stopping as soon as it hits an entity203/// that blocks lower entities.204///205/// Note that the word "lower" here refers to entities that have been reported as hit by any206/// picking backend, but are at a lower depth than the current one. This is different from the207/// concept of event bubbling, as it works irrespective of the entity hierarchy.208///209/// For example, if a pointer is over a UI element, as well as a 3d mesh, backends will report210/// hits for both of these entities. Additionally, the hits will be sorted by the camera order,211/// so if the UI is drawing on top of the 3d mesh, the UI will be "above" the mesh. When hovering212/// is computed, the UI element will be checked first to see if it this field is set to block213/// lower entities. If it does (default), the hovering system will stop there, and only the UI214/// element will be marked as hovered. However, if this field is set to `false`, both the UI215/// element *and* the mesh will be marked as hovered.216///217/// Entities without the [`Pickable`] component will block by default.218pub should_block_lower: bool,219220/// If this is set to `false` and `should_block_lower` is set to true, this entity will block221/// lower entities from being interacted and at the same time will itself not emit any events.222///223/// Note that the word "lower" here refers to entities that have been reported as hit by any224/// picking backend, but are at a lower depth than the current one. This is different from the225/// concept of event bubbling, as it works irrespective of the entity hierarchy.226///227/// For example, if a pointer is over a UI element, and this field is set to `false`, it will228/// not be marked as hovered, and consequently will not emit events nor will any picking229/// components mark it as hovered. This can be combined with the other field230/// [`Self::should_block_lower`], which is orthogonal to this one.231///232/// Entities without the [`Pickable`] component are hoverable by default.233pub is_hoverable: bool,234}235236impl Pickable {237/// This entity will not block entities beneath it, nor will it emit events.238///239/// If a backend reports this entity as being hit, the picking plugin will completely ignore it.240pub const IGNORE: Self = Self {241should_block_lower: false,242is_hoverable: false,243};244}245246impl Default for Pickable {247fn default() -> Self {248Self {249should_block_lower: true,250is_hoverable: true,251}252}253}254255/// Groups the stages of the picking process under shared labels.256#[derive(Debug, Hash, PartialEq, Eq, Clone, SystemSet)]257pub enum PickingSystems {258/// Produces pointer input events. In the [`First`] schedule.259Input,260/// Runs after input events are generated but before commands are flushed. In the [`First`]261/// schedule.262PostInput,263/// Receives and processes pointer input events. In the [`PreUpdate`] schedule.264ProcessInput,265/// Reads inputs and produces [`backend::PointerHits`]s. In the [`PreUpdate`] schedule.266Backend,267/// Reads [`backend::PointerHits`]s, and updates the hovermap, selection, and highlighting states. In268/// the [`PreUpdate`] schedule.269Hover,270/// Runs after all the [`PickingSystems::Hover`] systems are done, before event listeners are triggered. In the271/// [`PreUpdate`] schedule.272PostHover,273/// Runs after all other picking sets. In the [`PreUpdate`] schedule.274Last,275}276277/// Deprecated alias for [`PickingSystems`].278#[deprecated(since = "0.17.0", note = "Renamed to `PickingSystems`.")]279pub type PickSet = PickingSystems;280281/// One plugin that contains the [`PointerInputPlugin`](input::PointerInputPlugin), [`PickingPlugin`]282/// and the [`InteractionPlugin`], this is probably the plugin that will be most used.283///284/// Note: for any of these plugins to work, they require a picking backend to be active,285/// The picking backend is responsible to turn an input, into a [`PointerHits`](`crate::backend::PointerHits`)286/// that [`PickingPlugin`] and [`InteractionPlugin`] will refine into [`bevy_ecs::observer::On`]s.287#[derive(Default)]288pub struct DefaultPickingPlugins;289290impl PluginGroup for DefaultPickingPlugins {291fn build(self) -> PluginGroupBuilder {292PluginGroupBuilder::start::<Self>()293.add(input::PointerInputPlugin)294.add(PickingPlugin)295.add(InteractionPlugin)296}297}298299#[derive(Copy, Clone, Debug, Resource, Reflect)]300#[reflect(Resource, Default, Debug, Clone)]301/// Controls the behavior of picking302///303/// ## Custom initialization304/// ```305/// # use bevy_app::App;306/// # use bevy_picking::{PickingSettings, PickingPlugin};307/// App::new()308/// .insert_resource(PickingSettings {309/// is_enabled: true,310/// is_input_enabled: false,311/// is_hover_enabled: true,312/// is_window_picking_enabled: false,313/// })314/// // or DefaultPlugins315/// .add_plugins(PickingPlugin);316/// ```317pub struct PickingSettings {318/// Enables and disables all picking features.319pub is_enabled: bool,320/// Enables and disables input collection.321pub is_input_enabled: bool,322/// Enables and disables updating interaction states of entities.323pub is_hover_enabled: bool,324/// Enables or disables picking for window entities.325pub is_window_picking_enabled: bool,326}327328impl PickingSettings {329/// Whether or not input collection systems should be running.330pub fn input_should_run(state: Res<Self>) -> bool {331state.is_input_enabled && state.is_enabled332}333334/// Whether or not systems updating entities' [`PickingInteraction`](hover::PickingInteraction)335/// component should be running.336pub fn hover_should_run(state: Res<Self>) -> bool {337state.is_hover_enabled && state.is_enabled338}339340/// Whether or not window entities should receive pick events.341pub fn window_picking_should_run(state: Res<Self>) -> bool {342state.is_window_picking_enabled && state.is_enabled343}344}345346impl Default for PickingSettings {347fn default() -> Self {348Self {349is_enabled: true,350is_input_enabled: true,351is_hover_enabled: true,352is_window_picking_enabled: true,353}354}355}356357/// This plugin sets up the core picking infrastructure. It receives input events, and provides the shared358/// types used by other picking plugins.359///360/// Behavior of picking can be controlled by modifying [`PickingSettings`].361///362/// [`PickingSettings`] will be initialized with default values if it363/// is not present at the moment this is added to the app.364pub struct PickingPlugin;365366impl Plugin for PickingPlugin {367fn build(&self, app: &mut App) {368app.init_resource::<PickingSettings>()369.init_resource::<pointer::PointerMap>()370.init_resource::<backend::ray::RayMap>()371.add_event::<pointer::PointerInput>()372.add_event::<backend::PointerHits>()373// Rather than try to mark all current and future backends as ambiguous with each other,374// we allow them to send their hits in any order. These are later sorted, so submission375// order doesn't matter. See `PointerHits` docs for caveats.376.allow_ambiguous_resource::<Events<backend::PointerHits>>()377.add_systems(378PreUpdate,379(380pointer::update_pointer_map,381pointer::PointerInput::receive,382backend::ray::RayMap::repopulate.after(pointer::PointerInput::receive),383)384.in_set(PickingSystems::ProcessInput),385)386.add_systems(387PreUpdate,388window::update_window_hits389.run_if(PickingSettings::window_picking_should_run)390.in_set(PickingSystems::Backend),391)392.configure_sets(393First,394(PickingSystems::Input, PickingSystems::PostInput)395.after(bevy_time::TimeSystems)396.after(bevy_ecs::event::EventUpdateSystems)397.chain(),398)399.configure_sets(400PreUpdate,401(402PickingSystems::ProcessInput.run_if(PickingSettings::input_should_run),403PickingSystems::Backend,404PickingSystems::Hover.run_if(PickingSettings::hover_should_run),405PickingSystems::PostHover,406PickingSystems::Last,407)408.chain(),409);410}411}412413/// Generates [`Pointer`](events::Pointer) events and handles event bubbling.414#[derive(Default)]415pub struct InteractionPlugin;416417impl Plugin for InteractionPlugin {418fn build(&self, app: &mut App) {419use events::*;420use hover::{generate_hovermap, update_interactions};421422app.init_resource::<hover::HoverMap>()423.init_resource::<hover::PreviousHoverMap>()424.init_resource::<PointerState>()425.add_event::<Pointer<Cancel>>()426.add_event::<Pointer<Click>>()427.add_event::<Pointer<Press>>()428.add_event::<Pointer<DragDrop>>()429.add_event::<Pointer<DragEnd>>()430.add_event::<Pointer<DragEnter>>()431.add_event::<Pointer<Drag>>()432.add_event::<Pointer<DragLeave>>()433.add_event::<Pointer<DragOver>>()434.add_event::<Pointer<DragStart>>()435.add_event::<Pointer<Move>>()436.add_event::<Pointer<Out>>()437.add_event::<Pointer<Over>>()438.add_event::<Pointer<Release>>()439.add_event::<Pointer<Scroll>>()440.add_systems(441PreUpdate,442(443generate_hovermap,444update_interactions,445(update_is_hovered, update_is_directly_hovered),446pointer_events,447)448.chain()449.in_set(PickingSystems::Hover),450);451}452}453454455