From 3224b0bc80da9dee85a04aaf99ea5290ce0c101f Mon Sep 17 00:00:00 2001 From: ickshonpe Date: Thu, 13 Feb 2025 13:11:59 +0000 Subject: [PATCH 1/5] * Removed the winit touch handle from `PointerId::Touch`. * Removed the `deactivate_touch_pointers` system. * `touch_pick_event` local `touch_cache` hashmap also tracks the entity id of touch pointers as well as the intial touch state. * `touch_pick_event` despawns touch point entities when the touch ends or is cancelled. --- crates/bevy_picking/src/events.rs | 1 + crates/bevy_picking/src/input.rs | 105 ++++++++--------------------- crates/bevy_picking/src/pointer.rs | 34 +++++++--- 3 files changed, 52 insertions(+), 88 deletions(-) diff --git a/crates/bevy_picking/src/events.rs b/crates/bevy_picking/src/events.rs index 243208ca8ac06..544f1223db02c 100644 --- a/crates/bevy_picking/src/events.rs +++ b/crates/bevy_picking/src/events.rs @@ -549,6 +549,7 @@ pub fn pointer_events( pointer_id, location, action, + .. } in input_events.read().cloned() { match action { diff --git a/crates/bevy_picking/src/input.rs b/crates/bevy_picking/src/input.rs index eeb4adac15d3a..560b60761cdf1 100644 --- a/crates/bevy_picking/src/input.rs +++ b/crates/bevy_picking/src/input.rs @@ -20,7 +20,7 @@ use bevy_input::{ ButtonState, }; use bevy_math::Vec2; -use bevy_platform_support::collections::{HashMap, HashSet}; +use bevy_platform_support::collections::HashMap; use bevy_reflect::prelude::*; use bevy_render::camera::RenderTarget; use bevy_window::{PrimaryWindow, WindowEvent, WindowRef}; @@ -88,10 +88,6 @@ impl Plugin for PointerInputPlugin { .chain() .in_set(PickSet::Input), ) - .add_systems( - Last, - deactivate_touch_pointers.run_if(PointerInputPlugin::is_touch_enabled), - ) .register_type::() .register_type::(); } @@ -185,14 +181,13 @@ pub fn touch_pick_events( mut window_events: EventReader, primary_window: Query>, // Locals - mut touch_cache: Local>, + mut touch_cache: Local>, // Output mut commands: Commands, mut pointer_events: EventWriter, ) { for window_event in window_events.read() { if let WindowEvent::TouchInput(touch) = window_event { - let pointer = PointerId::Touch(touch.id); let location = Location { target: match RenderTarget::Window(WindowRef::Entity(touch.window)) .normalize(primary_window.get_single().ok()) @@ -202,78 +197,34 @@ pub fn touch_pick_events( }, position: touch.position, }; - match touch.phase { - TouchPhase::Started => { - debug!("Spawning pointer {:?}", pointer); - commands.spawn((pointer, PointerLocation::new(location.clone()))); - - pointer_events.send(PointerInput::new( - pointer, - location, - PointerAction::Press(PointerButton::Primary), - )); - - touch_cache.insert(touch.id, *touch); - } - TouchPhase::Moved => { - // Send a move event only if it isn't the same as the last one - if let Some(last_touch) = touch_cache.get(&touch.id) { - if last_touch == touch { - continue; - } - pointer_events.send(PointerInput::new( - pointer, - location, - PointerAction::Move { - delta: touch.position - last_touch.position, - }, - )); - } - touch_cache.insert(touch.id, *touch); - } - TouchPhase::Ended => { - pointer_events.send(PointerInput::new( - pointer, - location, - PointerAction::Release(PointerButton::Primary), - )); - touch_cache.remove(&touch.id); - } - TouchPhase::Canceled => { - pointer_events.send(PointerInput::new( - pointer, - location, - PointerAction::Cancel, - )); - touch_cache.remove(&touch.id); - } - } - } - } -} + let pointer_id = PointerId::Touch; + let (start, pointer_entity) = *touch_cache.entry(touch.id).or_insert_with(|| { + let pointer_entity = commands + .spawn((pointer_id, PointerLocation::new(location.clone()))) + .id(); + debug!("Spawning touch pointer {:?}", pointer_entity); + (*touch, pointer_entity) + }); + + pointer_events.send(PointerInput::new_with_entity( + pointer_id, + pointer_entity, + location, + match touch.phase { + TouchPhase::Started => PointerAction::Press(PointerButton::Primary), + TouchPhase::Moved => PointerAction::Move { + delta: touch.position - start.position, + }, + TouchPhase::Ended => PointerAction::Release(PointerButton::Primary), + TouchPhase::Canceled => PointerAction::Cancel, + }, + )); -/// Deactivates unused touch pointers. -/// -/// Because each new touch gets assigned a new ID, we need to remove the pointers associated with -/// touches that are no longer active. -pub fn deactivate_touch_pointers( - mut commands: Commands, - mut despawn_list: Local>, - pointers: Query<(Entity, &PointerId)>, - mut touches: EventReader, -) { - for touch in touches.read() { - if let TouchPhase::Ended | TouchPhase::Canceled = touch.phase { - for (entity, pointer) in &pointers { - if pointer.get_touch_id() == Some(touch.id) { - despawn_list.insert((entity, *pointer)); - } + if matches!(touch.phase, TouchPhase::Ended | TouchPhase::Canceled) { + touch_cache.remove(&touch.id); + debug!("Despawning {:?} pointer {:?}", pointer_id, pointer_entity); + commands.entity(pointer_entity).despawn(); } } } - // A hash set is used to prevent despawning the same entity twice. - for (entity, pointer) in despawn_list.drain() { - debug!("Despawning pointer {:?}", pointer); - commands.entity(entity).despawn(); - } } diff --git a/crates/bevy_picking/src/pointer.rs b/crates/bevy_picking/src/pointer.rs index dcaaf6060b3f2..f2295312bf544 100644 --- a/crates/bevy_picking/src/pointer.rs +++ b/crates/bevy_picking/src/pointer.rs @@ -33,8 +33,8 @@ pub enum PointerId { /// The mouse pointer. #[default] Mouse, - /// A touch input, usually numbered by window touch events from `winit`. - Touch(u64), + /// A touch input. + Touch, /// A custom, uniquely identified pointer. Useful for mocking inputs or implementing a software /// controlled cursor. #[reflect(ignore)] @@ -44,7 +44,7 @@ pub enum PointerId { impl PointerId { /// Returns true if the pointer is a touch input. pub fn is_touch(&self) -> bool { - matches!(self, PointerId::Touch(_)) + matches!(self, PointerId::Touch) } /// Returns true if the pointer is the mouse. pub fn is_mouse(&self) -> bool { @@ -54,14 +54,6 @@ impl PointerId { pub fn is_custom(&self) -> bool { matches!(self, PointerId::Custom(_)) } - /// Returns the touch id if the pointer is a touch input. - pub fn get_touch_id(&self) -> Option { - if let PointerId::Touch(id) = self { - Some(*id) - } else { - None - } - } } /// Holds a list of entities this pointer is currently interacting with, sorted from nearest to @@ -270,6 +262,8 @@ pub enum PointerAction { pub struct PointerInput { /// The id of the pointer. pub pointer_id: PointerId, + /// The pointer entity + pub pointer_entity: Option, /// The location of the pointer. For [`PointerAction::Move`], this is the location after the movement. pub location: Location, /// The action that the event describes. @@ -283,6 +277,24 @@ impl PointerInput { pub fn new(pointer_id: PointerId, location: Location, action: PointerAction) -> PointerInput { PointerInput { pointer_id, + pointer_entity: None, + location, + action, + } + } + + /// Creates a new pointer input event. + /// + /// Note that `location` refers to the position of the pointer *after* the event occurred. + pub fn new_with_entity( + pointer_id: PointerId, + pointer_entity: Entity, + location: Location, + action: PointerAction, + ) -> PointerInput { + PointerInput { + pointer_id, + pointer_entity: Some(pointer_entity), location, action, } From 6c1abee8d16f4f3ed127dc3451af32a1c6700879 Mon Sep 17 00:00:00 2001 From: ickshonpe Date: Thu, 13 Feb 2025 15:44:11 +0000 Subject: [PATCH 2/5] clean up --- crates/bevy_picking/src/input.rs | 7 +++---- 1 file changed, 3 insertions(+), 4 deletions(-) diff --git a/crates/bevy_picking/src/input.rs b/crates/bevy_picking/src/input.rs index 560b60761cdf1..cc8a99aba8783 100644 --- a/crates/bevy_picking/src/input.rs +++ b/crates/bevy_picking/src/input.rs @@ -197,17 +197,16 @@ pub fn touch_pick_events( }, position: touch.position, }; - let pointer_id = PointerId::Touch; let (start, pointer_entity) = *touch_cache.entry(touch.id).or_insert_with(|| { let pointer_entity = commands - .spawn((pointer_id, PointerLocation::new(location.clone()))) + .spawn((PointerId::Touch, PointerLocation::new(location.clone()))) .id(); debug!("Spawning touch pointer {:?}", pointer_entity); (*touch, pointer_entity) }); pointer_events.send(PointerInput::new_with_entity( - pointer_id, + PointerId::Touch, pointer_entity, location, match touch.phase { @@ -222,7 +221,7 @@ pub fn touch_pick_events( if matches!(touch.phase, TouchPhase::Ended | TouchPhase::Canceled) { touch_cache.remove(&touch.id); - debug!("Despawning {:?} pointer {:?}", pointer_id, pointer_entity); + debug!("Despawning touch pointer {:?}", pointer_entity); commands.entity(pointer_entity).despawn(); } } From 8513e44dc8115ace4c8cb545d65fca124e50bfbc Mon Sep 17 00:00:00 2001 From: ickshonpe Date: Thu, 13 Feb 2025 20:15:11 +0000 Subject: [PATCH 3/5] Clean up, undid some over enthusiatic refactors. --- crates/bevy_picking/src/input.rs | 93 +++++++++++++++++++++++--------- 1 file changed, 68 insertions(+), 25 deletions(-) diff --git a/crates/bevy_picking/src/input.rs b/crates/bevy_picking/src/input.rs index cc8a99aba8783..47c49a8c1183f 100644 --- a/crates/bevy_picking/src/input.rs +++ b/crates/bevy_picking/src/input.rs @@ -197,32 +197,75 @@ pub fn touch_pick_events( }, position: touch.position, }; - let (start, pointer_entity) = *touch_cache.entry(touch.id).or_insert_with(|| { - let pointer_entity = commands - .spawn((PointerId::Touch, PointerLocation::new(location.clone()))) - .id(); - debug!("Spawning touch pointer {:?}", pointer_entity); - (*touch, pointer_entity) - }); - - pointer_events.send(PointerInput::new_with_entity( - PointerId::Touch, - pointer_entity, - location, - match touch.phase { - TouchPhase::Started => PointerAction::Press(PointerButton::Primary), - TouchPhase::Moved => PointerAction::Move { - delta: touch.position - start.position, - }, - TouchPhase::Ended => PointerAction::Release(PointerButton::Primary), - TouchPhase::Canceled => PointerAction::Cancel, - }, - )); - if matches!(touch.phase, TouchPhase::Ended | TouchPhase::Canceled) { - touch_cache.remove(&touch.id); - debug!("Despawning touch pointer {:?}", pointer_entity); - commands.entity(pointer_entity).despawn(); + match touch.phase { + TouchPhase::Started => { + let (_, pointer) = *touch_cache.entry(touch.id).or_insert_with(|| { + let pointer = commands + .spawn((PointerId::Touch, PointerLocation::new(location.clone()))) + .id(); + debug!( + "Spawning touch finger: {:?}, pointer:{:?}", + touch.id, pointer + ); + (*touch, pointer) + }); + + pointer_events.send(PointerInput::new_with_entity( + PointerId::Touch, + pointer, + location, + PointerAction::Press(PointerButton::Primary), + )); + } + TouchPhase::Moved => { + if let Some((last_touch, pointer)) = touch_cache.get_mut(&touch.id) { + if *last_touch == *touch { + continue; + } + pointer_events.send(PointerInput::new_with_entity( + PointerId::Touch, + *pointer, + location, + PointerAction::Move { + delta: touch.position - last_touch.position, + }, + )); + *last_touch = *touch; + } + } + TouchPhase::Ended => { + if let Some((last_touch, pointer)) = touch_cache.remove(&touch.id) { + debug!( + "Despawning touch finger {:?}, entity {:?}", + last_touch.id, pointer + ); + commands.entity(pointer).despawn(); + + pointer_events.send(PointerInput::new_with_entity( + PointerId::Touch, + pointer, + location, + PointerAction::Release(PointerButton::Primary), + )); + } + } + TouchPhase::Canceled => { + if let Some((last_touch, pointer)) = touch_cache.remove(&touch.id) { + debug!( + "Despawning touch finger {:?}, entity {:?}", + last_touch.id, pointer + ); + commands.entity(pointer).despawn(); + + pointer_events.send(PointerInput::new_with_entity( + PointerId::Touch, + pointer, + location, + PointerAction::Cancel, + )); + } + } } } } From 7dee191f0d796e734cd83ecc3c79750f554065a7 Mon Sep 17 00:00:00 2001 From: ickshonpe Date: Thu, 13 Feb 2025 20:21:51 +0000 Subject: [PATCH 4/5] use insert instead of entry on touch_cache --- crates/bevy_picking/src/input.rs | 23 +++++++++++------------ 1 file changed, 11 insertions(+), 12 deletions(-) diff --git a/crates/bevy_picking/src/input.rs b/crates/bevy_picking/src/input.rs index 47c49a8c1183f..f26830cf72a57 100644 --- a/crates/bevy_picking/src/input.rs +++ b/crates/bevy_picking/src/input.rs @@ -200,16 +200,15 @@ pub fn touch_pick_events( match touch.phase { TouchPhase::Started => { - let (_, pointer) = *touch_cache.entry(touch.id).or_insert_with(|| { - let pointer = commands - .spawn((PointerId::Touch, PointerLocation::new(location.clone()))) - .id(); - debug!( - "Spawning touch finger: {:?}, pointer:{:?}", - touch.id, pointer - ); - (*touch, pointer) - }); + let pointer = commands + .spawn((PointerId::Touch, PointerLocation::new(location.clone()))) + .id(); + touch_cache.insert(touch.id, (*touch, pointer)); + + debug!( + "Spawned touch, finger: {:?}, pointer:{:?}", + touch.id, pointer + ); pointer_events.send(PointerInput::new_with_entity( PointerId::Touch, @@ -237,7 +236,7 @@ pub fn touch_pick_events( TouchPhase::Ended => { if let Some((last_touch, pointer)) = touch_cache.remove(&touch.id) { debug!( - "Despawning touch finger {:?}, entity {:?}", + "Despawning touch, finger {:?}, entity {:?}", last_touch.id, pointer ); commands.entity(pointer).despawn(); @@ -253,7 +252,7 @@ pub fn touch_pick_events( TouchPhase::Canceled => { if let Some((last_touch, pointer)) = touch_cache.remove(&touch.id) { debug!( - "Despawning touch finger {:?}, entity {:?}", + "Despawning touch, finger {:?}, entity {:?}", last_touch.id, pointer ); commands.entity(pointer).despawn(); From 3f9547fa11b73904e09a0e3e31fe6dcbd7337de6 Mon Sep 17 00:00:00 2001 From: ickshonpe Date: Thu, 13 Feb 2025 20:31:48 +0000 Subject: [PATCH 5/5] clean up --- crates/bevy_picking/src/input.rs | 8 ++++---- 1 file changed, 4 insertions(+), 4 deletions(-) diff --git a/crates/bevy_picking/src/input.rs b/crates/bevy_picking/src/input.rs index f26830cf72a57..c534f72cf87c9 100644 --- a/crates/bevy_picking/src/input.rs +++ b/crates/bevy_picking/src/input.rs @@ -234,10 +234,10 @@ pub fn touch_pick_events( } } TouchPhase::Ended => { - if let Some((last_touch, pointer)) = touch_cache.remove(&touch.id) { + if let Some((_, pointer)) = touch_cache.remove(&touch.id) { debug!( "Despawning touch, finger {:?}, entity {:?}", - last_touch.id, pointer + touch.id, pointer ); commands.entity(pointer).despawn(); @@ -250,10 +250,10 @@ pub fn touch_pick_events( } } TouchPhase::Canceled => { - if let Some((last_touch, pointer)) = touch_cache.remove(&touch.id) { + if let Some((_, pointer)) = touch_cache.remove(&touch.id) { debug!( "Despawning touch, finger {:?}, entity {:?}", - last_touch.id, pointer + touch.id, pointer ); commands.entity(pointer).despawn();