Add relative position reporting to UI picking (#17681)
# Objective Add position reporting to `HitData` sent from the UI picking backend. ## Solution Add the computed normalized relative cursor position to `hit_data` alongside the `Entity`. The position reported in `HitData` is normalized relative to the node, with `(0.,0.,0.)` at the top left and `(1., 1., 0.)` in the bottom right. Coordinates are relative to the entire node, not just the visible region. `HitData` needs a `Vec3` so I just extended with 0.0. I considered inserting the `depth` here but thought it would be redundant. I also considered putting the screen space position in the `normal` field of `HitData`, but that would require renaming of the field or a separate data structure. ## Testing Tested with mouse on X11 with entities that have `Node` components. --- ## Showcase ```rs // Get click position relative to node fn hit_position(trigger: Trigger<Pointer<Click>>) { let hit_pos = trigger.event.hit.position.expect("no position"); info!("{}", hit_pos); } ``` --------- Co-authored-by: Alice Cecile <alice.i.cecile@gmail.com>
This commit is contained in:
parent
fd67ca7eb0
commit
5eff6e80e1
@ -18,6 +18,8 @@
|
|||||||
//! - `bevy_ui` can render on any camera with a flag, it is special, and is not tied to a particular
|
//! - `bevy_ui` can render on any camera with a flag, it is special, and is not tied to a particular
|
||||||
//! camera.
|
//! camera.
|
||||||
//! - To correctly sort picks, the order of `bevy_ui` is set to be the camera order plus 0.5.
|
//! - To correctly sort picks, the order of `bevy_ui` is set to be the camera order plus 0.5.
|
||||||
|
//! - The position reported in `HitData` is normalized relative to the node, with `(0.,0.,0.)` at the top
|
||||||
|
//! left and `(1., 1., 0.)` in the bottom right. Coordinates are relative to the entire node, not just the visible region.
|
||||||
|
|
||||||
#![deny(missing_docs)]
|
#![deny(missing_docs)]
|
||||||
|
|
||||||
@ -104,7 +106,7 @@ pub fn ui_picking(
|
|||||||
}
|
}
|
||||||
|
|
||||||
// The list of node entities hovered for each (camera, pointer) combo
|
// The list of node entities hovered for each (camera, pointer) combo
|
||||||
let mut hit_nodes = HashMap::<(Entity, PointerId), Vec<Entity>>::default();
|
let mut hit_nodes = HashMap::<(Entity, PointerId), Vec<(Entity, Vec2)>>::default();
|
||||||
|
|
||||||
// prepare an iterator that contains all the nodes that have the cursor in their rect,
|
// prepare an iterator that contains all the nodes that have the cursor in their rect,
|
||||||
// from the top node to the bottom one. this will also reset the interaction to `None`
|
// from the top node to the bottom one. this will also reset the interaction to `None`
|
||||||
@ -167,23 +169,28 @@ pub fn ui_picking(
|
|||||||
hit_nodes
|
hit_nodes
|
||||||
.entry((camera_entity, *pointer_id))
|
.entry((camera_entity, *pointer_id))
|
||||||
.or_default()
|
.or_default()
|
||||||
.push(*node_entity);
|
.push((*node_entity, relative_cursor_position));
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
for ((camera, pointer), hovered_nodes) in hit_nodes.iter() {
|
for ((camera, pointer), hovered) in hit_nodes.iter() {
|
||||||
// As soon as a node with a `Block` focus policy is detected, the iteration will stop on it
|
// As soon as a node with a `Block` focus policy is detected, the iteration will stop on it
|
||||||
// because it "captures" the interaction.
|
// because it "captures" the interaction.
|
||||||
let mut picks = Vec::new();
|
let mut picks = Vec::new();
|
||||||
let mut depth = 0.0;
|
let mut depth = 0.0;
|
||||||
|
|
||||||
for node in node_query.iter_many(hovered_nodes) {
|
for (hovered_node, position) in hovered {
|
||||||
|
let node = node_query.get(*hovered_node).unwrap();
|
||||||
|
|
||||||
let Some(camera_entity) = node.target_camera.camera() else {
|
let Some(camera_entity) = node.target_camera.camera() else {
|
||||||
continue;
|
continue;
|
||||||
};
|
};
|
||||||
|
|
||||||
picks.push((node.entity, HitData::new(camera_entity, depth, None, None)));
|
picks.push((
|
||||||
|
node.entity,
|
||||||
|
HitData::new(camera_entity, depth, Some(position.extend(0.0)), None),
|
||||||
|
));
|
||||||
|
|
||||||
if let Some(pickable) = node.pickable {
|
if let Some(pickable) = node.pickable {
|
||||||
// If an entity has a `Pickable` component, we will use that as the source of truth.
|
// If an entity has a `Pickable` component, we will use that as the source of truth.
|
||||||
|
Loading…
Reference in New Issue
Block a user