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
|
||||
//! camera.
|
||||
//! - 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)]
|
||||
|
||||
@ -104,7 +106,7 @@ pub fn ui_picking(
|
||||
}
|
||||
|
||||
// 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,
|
||||
// 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
|
||||
.entry((camera_entity, *pointer_id))
|
||||
.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
|
||||
// because it "captures" the interaction.
|
||||
let mut picks = Vec::new();
|
||||
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 {
|
||||
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 an entity has a `Pickable` component, we will use that as the source of truth.
|
||||
|
Loading…
Reference in New Issue
Block a user