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 <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`.
This commit is contained in:
parent
b20e23dd41
commit
0a9740c18f
@ -188,9 +188,12 @@ pub mod prelude {
|
|||||||
};
|
};
|
||||||
}
|
}
|
||||||
|
|
||||||
/// An optional component that overrides default picking behavior for an entity, allowing you to
|
/// An optional component that marks an entity as usable by a backend, and overrides default
|
||||||
/// make an entity non-hoverable, or allow items below it to be hovered. See the documentation on
|
/// picking behavior for an entity.
|
||||||
/// the fields for more details.
|
///
|
||||||
|
/// 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)]
|
#[derive(Component, Debug, Clone, Reflect, PartialEq, Eq)]
|
||||||
#[reflect(Component, Default, Debug, PartialEq)]
|
#[reflect(Component, Default, Debug, PartialEq)]
|
||||||
pub struct PickingBehavior {
|
pub struct PickingBehavior {
|
||||||
|
@ -15,8 +15,14 @@ use bevy_render::prelude::*;
|
|||||||
use bevy_transform::prelude::*;
|
use bevy_transform::prelude::*;
|
||||||
use bevy_window::PrimaryWindow;
|
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
|
/// How should the [`SpritePickingPlugin`] handle picking and how should it handle transparent pixels
|
||||||
#[derive(Debug, Clone, Copy, Reflect)]
|
#[derive(Debug, Clone, Copy, Reflect)]
|
||||||
|
#[reflect(Debug)]
|
||||||
pub enum SpritePickingMode {
|
pub enum SpritePickingMode {
|
||||||
/// Even if a sprite is picked on a transparent pixel, it should still count within the backend.
|
/// 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.
|
/// Only consider the rect of a given sprite.
|
||||||
@ -30,6 +36,12 @@ pub enum SpritePickingMode {
|
|||||||
#[derive(Resource, Reflect)]
|
#[derive(Resource, Reflect)]
|
||||||
#[reflect(Resource, Default)]
|
#[reflect(Resource, Default)]
|
||||||
pub struct SpritePickingSettings {
|
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.
|
/// 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
|
/// Defaults to an inclusive alpha threshold of 0.1
|
||||||
@ -39,6 +51,7 @@ pub struct SpritePickingSettings {
|
|||||||
impl Default for SpritePickingSettings {
|
impl Default for SpritePickingSettings {
|
||||||
fn default() -> Self {
|
fn default() -> Self {
|
||||||
Self {
|
Self {
|
||||||
|
require_markers: false,
|
||||||
picking_mode: SpritePickingMode::AlphaThreshold(0.1),
|
picking_mode: SpritePickingMode::AlphaThreshold(0.1),
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
@ -50,13 +63,24 @@ pub struct SpritePickingPlugin;
|
|||||||
impl Plugin for SpritePickingPlugin {
|
impl Plugin for SpritePickingPlugin {
|
||||||
fn build(&self, app: &mut App) {
|
fn build(&self, app: &mut App) {
|
||||||
app.init_resource::<SpritePickingSettings>()
|
app.init_resource::<SpritePickingSettings>()
|
||||||
|
.register_type::<(
|
||||||
|
SpritePickingCamera,
|
||||||
|
SpritePickingMode,
|
||||||
|
SpritePickingSettings,
|
||||||
|
)>()
|
||||||
.add_systems(PreUpdate, sprite_picking.in_set(PickSet::Backend));
|
.add_systems(PreUpdate, sprite_picking.in_set(PickSet::Backend));
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
fn sprite_picking(
|
fn sprite_picking(
|
||||||
pointers: Query<(&PointerId, &PointerLocation)>,
|
pointers: Query<(&PointerId, &PointerLocation)>,
|
||||||
cameras: Query<(Entity, &Camera, &GlobalTransform, &Projection)>,
|
cameras: Query<(
|
||||||
|
Entity,
|
||||||
|
&Camera,
|
||||||
|
&GlobalTransform,
|
||||||
|
&Projection,
|
||||||
|
Has<SpritePickingCamera>,
|
||||||
|
)>,
|
||||||
primary_window: Query<Entity, With<PrimaryWindow>>,
|
primary_window: Query<Entity, With<PrimaryWindow>>,
|
||||||
images: Res<Assets<Image>>,
|
images: Res<Assets<Image>>,
|
||||||
texture_atlas_layout: Res<Assets<TextureAtlasLayout>>,
|
texture_atlas_layout: Res<Assets<TextureAtlasLayout>>,
|
||||||
@ -73,7 +97,8 @@ fn sprite_picking(
|
|||||||
let mut sorted_sprites: Vec<_> = sprite_query
|
let mut sorted_sprites: Vec<_> = sprite_query
|
||||||
.iter()
|
.iter()
|
||||||
.filter_map(|(entity, sprite, transform, picking_behavior, vis)| {
|
.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))
|
Some((entity, sprite, transform, picking_behavior))
|
||||||
} else {
|
} else {
|
||||||
None
|
None
|
||||||
@ -92,11 +117,14 @@ fn sprite_picking(
|
|||||||
pointer_location.location().map(|loc| (pointer, loc))
|
pointer_location.location().map(|loc| (pointer, loc))
|
||||||
}) {
|
}) {
|
||||||
let mut blocked = false;
|
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
|
cameras
|
||||||
.iter()
|
.iter()
|
||||||
.filter(|(_, camera, _, _)| camera.is_active)
|
.filter(|(_, camera, _, _, cam_can_pick)| {
|
||||||
.find(|(_, camera, _, _)| {
|
let marker_requirement = !settings.require_markers || *cam_can_pick;
|
||||||
|
camera.is_active && marker_requirement
|
||||||
|
})
|
||||||
|
.find(|(_, camera, _, _, _)| {
|
||||||
camera
|
camera
|
||||||
.target
|
.target
|
||||||
.normalize(primary_window)
|
.normalize(primary_window)
|
||||||
|
@ -55,7 +55,7 @@ fn setup(mut commands: Commands, asset_server: Res<AssetServer>) {
|
|||||||
let i = (anchor_index % 3) as f32;
|
let i = (anchor_index % 3) as f32;
|
||||||
let j = (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
|
commands
|
||||||
.spawn((
|
.spawn((
|
||||||
Sprite::from_color(Color::BLACK, sprite_size),
|
Sprite::from_color(Color::BLACK, sprite_size),
|
||||||
|
Loading…
Reference in New Issue
Block a user