From 0a9740c18f9ae5338e29e4fbf49c55c34ffdf429 Mon Sep 17 00:00:00 2001 From: Antony Date: Thu, 9 Jan 2025 13:11:44 -0500 Subject: [PATCH] Make sprite picking opt-in (#17225) # Objective Fixes #16903. ## Solution - Make sprite picking opt-in by requiring a new `SpritePickingCamera` component for cameras and usage of a new `Pickable` component for entities. - Update the `sprite_picking` example to reflect these changes. - Some reflection cleanup (I hope that's ok). ## Testing Ran the `sprite_picking` example ## Open Questions ## Migration Guide The sprite picking backend is now strictly opt-in using the `SpritePickingCamera` and `Pickable` components. You should add the `Pickable` component any entities that you want sprite picking to be enabled for, and mark their respective cameras with `SpritePickingCamera`. --- crates/bevy_picking/src/lib.rs | 9 ++++-- crates/bevy_sprite/src/picking_backend.rs | 38 ++++++++++++++++++++--- examples/picking/sprite_picking.rs | 2 +- 3 files changed, 40 insertions(+), 9 deletions(-) diff --git a/crates/bevy_picking/src/lib.rs b/crates/bevy_picking/src/lib.rs index 8bf57e1af08c3..0bb7c8167691b 100644 --- a/crates/bevy_picking/src/lib.rs +++ b/crates/bevy_picking/src/lib.rs @@ -188,9 +188,12 @@ pub mod prelude { }; } -/// An optional component that overrides default picking behavior for an entity, allowing you to -/// make an entity non-hoverable, or allow items below it to be hovered. See the documentation on -/// the fields for more details. +/// An optional component that marks an entity as usable by a backend, and overrides default +/// picking behavior for an entity. +/// +/// This allows you to make an entity non-hoverable, or allow items below it to be hovered. +/// +/// See the documentation on the fields for more details. #[derive(Component, Debug, Clone, Reflect, PartialEq, Eq)] #[reflect(Component, Default, Debug, PartialEq)] pub struct PickingBehavior { diff --git a/crates/bevy_sprite/src/picking_backend.rs b/crates/bevy_sprite/src/picking_backend.rs index ab7fbcddef14b..e70066006ab1b 100644 --- a/crates/bevy_sprite/src/picking_backend.rs +++ b/crates/bevy_sprite/src/picking_backend.rs @@ -15,8 +15,14 @@ use bevy_render::prelude::*; use bevy_transform::prelude::*; use bevy_window::PrimaryWindow; +/// A component that marks cameras that should be used in the [`SpritePickingPlugin`]. +#[derive(Debug, Clone, Default, Component, Reflect)] +#[reflect(Debug, Default, Component)] +pub struct SpritePickingCamera; + /// How should the [`SpritePickingPlugin`] handle picking and how should it handle transparent pixels #[derive(Debug, Clone, Copy, Reflect)] +#[reflect(Debug)] pub enum SpritePickingMode { /// Even if a sprite is picked on a transparent pixel, it should still count within the backend. /// Only consider the rect of a given sprite. @@ -30,6 +36,12 @@ pub enum SpritePickingMode { #[derive(Resource, Reflect)] #[reflect(Resource, Default)] pub struct SpritePickingSettings { + /// When set to `true` sprite picking will only consider cameras marked with + /// [`SpritePickingCamera`] and entities marked with [`PickingBehavior`]. `false` by default. + /// + /// This setting is provided to give you fine-grained control over which cameras and entities + /// should be used by the sprite picking backend at runtime. + pub require_markers: bool, /// Should the backend count transparent pixels as part of the sprite for picking purposes or should it use the bounding box of the sprite alone. /// /// Defaults to an inclusive alpha threshold of 0.1 @@ -39,6 +51,7 @@ pub struct SpritePickingSettings { impl Default for SpritePickingSettings { fn default() -> Self { Self { + require_markers: false, picking_mode: SpritePickingMode::AlphaThreshold(0.1), } } @@ -50,13 +63,24 @@ pub struct SpritePickingPlugin; impl Plugin for SpritePickingPlugin { fn build(&self, app: &mut App) { app.init_resource::() + .register_type::<( + SpritePickingCamera, + SpritePickingMode, + SpritePickingSettings, + )>() .add_systems(PreUpdate, sprite_picking.in_set(PickSet::Backend)); } } fn sprite_picking( pointers: Query<(&PointerId, &PointerLocation)>, - cameras: Query<(Entity, &Camera, &GlobalTransform, &Projection)>, + cameras: Query<( + Entity, + &Camera, + &GlobalTransform, + &Projection, + Has, + )>, primary_window: Query>, images: Res>, texture_atlas_layout: Res>, @@ -73,7 +97,8 @@ fn sprite_picking( let mut sorted_sprites: Vec<_> = sprite_query .iter() .filter_map(|(entity, sprite, transform, picking_behavior, vis)| { - if !transform.affine().is_nan() && vis.get() { + let marker_requirement = !settings.require_markers || picking_behavior.is_some(); + if !transform.affine().is_nan() && vis.get() && marker_requirement { Some((entity, sprite, transform, picking_behavior)) } else { None @@ -92,11 +117,14 @@ fn sprite_picking( pointer_location.location().map(|loc| (pointer, loc)) }) { let mut blocked = false; - let Some((cam_entity, camera, cam_transform, Projection::Orthographic(cam_ortho))) = + let Some((cam_entity, camera, cam_transform, Projection::Orthographic(cam_ortho), _)) = cameras .iter() - .filter(|(_, camera, _, _)| camera.is_active) - .find(|(_, camera, _, _)| { + .filter(|(_, camera, _, _, cam_can_pick)| { + let marker_requirement = !settings.require_markers || *cam_can_pick; + camera.is_active && marker_requirement + }) + .find(|(_, camera, _, _, _)| { camera .target .normalize(primary_window) diff --git a/examples/picking/sprite_picking.rs b/examples/picking/sprite_picking.rs index 417f590cf81df..27939169a8341 100644 --- a/examples/picking/sprite_picking.rs +++ b/examples/picking/sprite_picking.rs @@ -55,7 +55,7 @@ fn setup(mut commands: Commands, asset_server: Res) { let i = (anchor_index % 3) as f32; let j = (anchor_index / 3) as f32; - // spawn black square behind sprite to show anchor point + // Spawn black square behind sprite to show anchor point commands .spawn(( Sprite::from_color(Color::BLACK, sprite_size),