Skip to content

Commit

Permalink
Make sprite picking opt-in (#17225)
Browse files Browse the repository at this point in the history
# 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

<del>
   <ul>
    <li>Is the name `SpritePickable` appropriate?</li>
    <li>Should `SpritePickable` be in `bevy_sprite::prelude?</li>
  </ul> 
</del>

## 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`.
  • Loading branch information
chompaa authored Jan 9, 2025
1 parent b20e23d commit 0a9740c
Show file tree
Hide file tree
Showing 3 changed files with 40 additions and 9 deletions.
9 changes: 6 additions & 3 deletions crates/bevy_picking/src/lib.rs
Original file line number Diff line number Diff line change
Expand Up @@ -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 {
Expand Down
38 changes: 33 additions & 5 deletions crates/bevy_sprite/src/picking_backend.rs
Original file line number Diff line number Diff line change
Expand Up @@ -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.
Expand All @@ -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
Expand All @@ -39,6 +51,7 @@ pub struct SpritePickingSettings {
impl Default for SpritePickingSettings {
fn default() -> Self {
Self {
require_markers: false,
picking_mode: SpritePickingMode::AlphaThreshold(0.1),
}
}
Expand All @@ -50,13 +63,24 @@ pub struct SpritePickingPlugin;
impl Plugin for SpritePickingPlugin {
fn build(&self, app: &mut App) {
app.init_resource::<SpritePickingSettings>()
.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<SpritePickingCamera>,
)>,
primary_window: Query<Entity, With<PrimaryWindow>>,
images: Res<Assets<Image>>,
texture_atlas_layout: Res<Assets<TextureAtlasLayout>>,
Expand All @@ -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
Expand All @@ -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)
Expand Down
2 changes: 1 addition & 1 deletion examples/picking/sprite_picking.rs
Original file line number Diff line number Diff line change
Expand Up @@ -55,7 +55,7 @@ fn setup(mut commands: Commands, asset_server: Res<AssetServer>) {
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),
Expand Down

0 comments on commit 0a9740c

Please sign in to comment.