diff --git a/Cargo.toml b/Cargo.toml index a3d3a2ab63e51..b6d8eb11a7c84 100644 --- a/Cargo.toml +++ b/Cargo.toml @@ -4129,6 +4129,18 @@ description = "Demonstrates picking debug overlay" category = "Picking" wasm = true +[[example]] +name = "parent_picking" +path = "examples/picking/parent_picking.rs" +doc-scrape-examples = true +required-features = ["bevy_mesh_picking_backend"] + +[package.metadata.example.parent_picking] +name = "Parent Picking" +description = "Demonstrates parent picking meshes" +category = "Picking" +wasm = true + [[example]] name = "animation_masks" path = "examples/animation/animation_masks.rs" diff --git a/crates/bevy_picking/src/events.rs b/crates/bevy_picking/src/events.rs index 29405099f66ad..6574cf47bdd12 100644 --- a/crates/bevy_picking/src/events.rs +++ b/crates/bevy_picking/src/events.rs @@ -63,7 +63,7 @@ use crate::{ #[reflect(Component, Debug, Clone)] pub struct Pointer { /// The original target of this picking event, before bubbling - pub target: Entity, + pub original_target: Entity, /// The pointer that triggered this event pub pointer_id: PointerId, /// The location of the pointer during this event @@ -136,7 +136,7 @@ impl Pointer { /// Construct a new `Pointer` event. pub fn new(id: PointerId, location: Location, target: Entity, event: E) -> Self { Self { - target, + original_target: target, pointer_id: id, pointer_location: location, event, diff --git a/examples/README.md b/examples/README.md index 060683f96d891..e431b349e2ba2 100644 --- a/examples/README.md +++ b/examples/README.md @@ -409,6 +409,7 @@ Example | Description Example | Description --- | --- [Mesh Picking](../examples/picking/mesh_picking.rs) | Demonstrates picking meshes +[Parent Picking](../examples/picking/parent_picking.rs) | Demonstrates parent picking meshes [Picking Debug Tools](../examples/picking/debug_picking.rs) | Demonstrates picking debug overlay [Showcases simple picking events and usage](../examples/picking/simple_picking.rs) | Demonstrates how to use picking events to spawn simple objects [Sprite Picking](../examples/picking/sprite_picking.rs) | Demonstrates picking sprites and sprite atlases diff --git a/examples/ecs/error_handling.rs b/examples/ecs/error_handling.rs index d326d7d4aae81..8359d392a5b34 100644 --- a/examples/ecs/error_handling.rs +++ b/examples/ecs/error_handling.rs @@ -136,7 +136,7 @@ fn fallible_observer( mut step: Local, ) -> Result { let mut transform = world - .get_mut::(trigger.target) + .get_mut::(trigger.target()) .ok_or("No transform found.")?; *step = if transform.translation.x > 3. { diff --git a/examples/picking/parent_picking.rs b/examples/picking/parent_picking.rs new file mode 100644 index 0000000000000..0158f0198fd4b --- /dev/null +++ b/examples/picking/parent_picking.rs @@ -0,0 +1,93 @@ +//! A simple 3D scene to demonstrate parent picking. +//! +//! Entity hierarchies can be built using a [`ChildOf`] component. By observing for +//! [`Trigger>`] events on the parent entity, picking events can be collected +//! for the entire tree, giving both the leaf and root nodes selected. + +use bevy::prelude::*; + +fn main() { + App::new() + .add_plugins(DefaultPlugins) + .add_plugins(MeshPickingPlugin) + .add_systems(Startup, setup) + .add_systems(PreUpdate, close_on_esc) + .run(); +} + +/// set up a simple 3D scene +fn setup( + mut commands: Commands, + mut meshes: ResMut>, + mut materials: ResMut>, +) { + let parent_id = commands + .spawn(( + Name::new("Parent"), + Transform::IDENTITY, + Visibility::Visible, + )) + .observe(on_pointer_over_debug) + .id(); + + // circular base + commands.spawn(( + Name::new("Base"), + Mesh3d(meshes.add(Circle::new(4.0))), + MeshMaterial3d(materials.add(Color::WHITE)), + Transform::from_rotation(Quat::from_rotation_x(-std::f32::consts::FRAC_PI_2)), + ChildOf(parent_id), + )); + + // cube + commands.spawn(( + Name::new("Cube"), + Mesh3d(meshes.add(Cuboid::new(1.0, 1.0, 1.0))), + MeshMaterial3d(materials.add(Color::srgb_u8(124, 144, 255))), + Transform::from_xyz(0.0, 0.5, 0.0), + ChildOf(parent_id), + )); + + // light + commands.spawn(( + PointLight { + shadows_enabled: true, + ..default() + }, + Transform::from_xyz(4.0, 8.0, 4.0), + )); + + // camera + commands.spawn(( + Camera3d::default(), + Transform::from_xyz(-2.5, 4.5, 9.0).looking_at(Vec3::ZERO, Vec3::Y), + )); +} + +fn on_pointer_over_debug(trigger: Trigger>, query: Query<&Name>) { + if let Ok(name) = query.get(trigger.target()) { + println!("root = trigger.target() = {:?}", name); + } + + // NOTE: trigger.original_target = trigger.event().original_target + // this is due to [`impl Deref for Pointer`] + if let Ok(name) = query.get(trigger.original_target) { + println!("leaf = trigger.original_target = {:?}", name); + } +} + +fn close_on_esc( + mut commands: Commands, + focused_windows: Query<(Entity, &Window)>, + input: Res>, +) { + for (window, focus) in focused_windows.iter() { + if !focus.focused { + continue; + } + + if input.just_pressed(KeyCode::Escape) { + commands.entity(window).despawn(); + } + } +} diff --git a/examples/ui/directional_navigation.rs b/examples/ui/directional_navigation.rs index b6f4a0d0514e0..f98f478ce0125 100644 --- a/examples/ui/directional_navigation.rs +++ b/examples/ui/directional_navigation.rs @@ -382,7 +382,7 @@ fn interact_with_focused_button( if let Some(focused_entity) = input_focus.0 { commands.trigger_targets( Pointer:: { - target: focused_entity, + original_target: focused_entity, // We're pretending that we're a mouse pointer_id: PointerId::Mouse, // This field isn't used, so we're just setting it to a placeholder value diff --git a/release-content/migration-guides/parent_picking.md b/release-content/migration-guides/parent_picking.md new file mode 100644 index 0000000000000..1ec19bd2a65cf --- /dev/null +++ b/release-content/migration-guides/parent_picking.md @@ -0,0 +1,10 @@ +--- +title: Parent Picking +pull_requests: [18982] +--- + +`target` on `Trigger>` has been renamed to `original_target`. + +Previously `.target()` and `.target` would refer to two separate entities, the root and the leaf of the entity hierarchy. + +See `examples/picking/parent_picking.rs` for a demonstration of this.