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..c534f72cf87c9 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,75 @@ pub fn touch_pick_events( }, position: touch.position, }; + match touch.phase { TouchPhase::Started => { - debug!("Spawning pointer {:?}", pointer); - commands.spawn((pointer, PointerLocation::new(location.clone()))); + 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( + pointer_events.send(PointerInput::new_with_entity( + PointerId::Touch, 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 { + if let Some((last_touch, pointer)) = touch_cache.get_mut(&touch.id) { + if *last_touch == *touch { continue; } - pointer_events.send(PointerInput::new( - pointer, + pointer_events.send(PointerInput::new_with_entity( + PointerId::Touch, + *pointer, location, PointerAction::Move { delta: touch.position - last_touch.position, }, )); + *last_touch = *touch; } - touch_cache.insert(touch.id, *touch); } TouchPhase::Ended => { - pointer_events.send(PointerInput::new( - pointer, - location, - PointerAction::Release(PointerButton::Primary), - )); - touch_cache.remove(&touch.id); + if let Some((_, pointer)) = touch_cache.remove(&touch.id) { + debug!( + "Despawning touch, finger {:?}, entity {:?}", + touch.id, pointer + ); + commands.entity(pointer).despawn(); + + pointer_events.send(PointerInput::new_with_entity( + PointerId::Touch, + pointer, + location, + PointerAction::Release(PointerButton::Primary), + )); + } } TouchPhase::Canceled => { - pointer_events.send(PointerInput::new( - pointer, - location, - PointerAction::Cancel, - )); - touch_cache.remove(&touch.id); - } - } - } - } -} + if let Some((_, pointer)) = touch_cache.remove(&touch.id) { + debug!( + "Despawning touch, finger {:?}, entity {:?}", + touch.id, pointer + ); + commands.entity(pointer).despawn(); -/// 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)); + pointer_events.send(PointerInput::new_with_entity( + PointerId::Touch, + pointer, + location, + PointerAction::Cancel, + )); + } } } } } - // 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, }