|
| 1 | +use bevy_app::{EventReader, Events}; |
| 2 | +use bevy_ecs::{Local, Res, ResMut}; |
| 3 | +use bevy_math::Vec2; |
| 4 | +use bevy_utils::{HashMap, HashSet}; |
| 5 | + |
| 6 | +/// A touch input event |
| 7 | +#[derive(Debug, Clone)] |
| 8 | +pub struct TouchInput { |
| 9 | + pub phase: TouchPhase, |
| 10 | + pub position: Vec2, |
| 11 | + /// |
| 12 | + /// ## Platform-specific |
| 13 | + /// |
| 14 | + /// Unique identifier of a finger. |
| 15 | + pub id: u64, |
| 16 | +} |
| 17 | + |
| 18 | +/// Describes touch-screen input state. |
| 19 | +#[derive(Debug, Hash, PartialEq, Eq, Clone, Copy)] |
| 20 | +#[cfg_attr(feature = "serialize", derive(serde::Serialize, serde::Deserialize))] |
| 21 | +pub enum TouchPhase { |
| 22 | + Started, |
| 23 | + Moved, |
| 24 | + Ended, |
| 25 | + Cancelled, |
| 26 | +} |
| 27 | + |
| 28 | +#[derive(Default)] |
| 29 | +pub struct TouchSystemState { |
| 30 | + touch_event_reader: EventReader<TouchInput>, |
| 31 | +} |
| 32 | + |
| 33 | +#[derive(Debug, Clone)] |
| 34 | +pub struct Touch { |
| 35 | + pub id: u64, |
| 36 | + pub start_position: Vec2, |
| 37 | + pub previous_position: Vec2, |
| 38 | + pub position: Vec2, |
| 39 | +} |
| 40 | + |
| 41 | +impl Touch { |
| 42 | + pub fn delta(&self) -> Vec2 { |
| 43 | + self.position - self.previous_position |
| 44 | + } |
| 45 | + |
| 46 | + pub fn distance(&self) -> Vec2 { |
| 47 | + self.position - self.start_position |
| 48 | + } |
| 49 | +} |
| 50 | + |
| 51 | +#[derive(Default)] |
| 52 | +pub struct Touches { |
| 53 | + active_touches: HashMap<u64, Touch>, |
| 54 | + just_pressed: HashSet<u64>, |
| 55 | + just_released: HashSet<u64>, |
| 56 | + just_cancelled: HashSet<u64>, |
| 57 | +} |
| 58 | + |
| 59 | +impl Touches { |
| 60 | + pub fn iter(&self) -> impl Iterator<Item = &Touch> + '_ { |
| 61 | + self.active_touches.values() |
| 62 | + } |
| 63 | + |
| 64 | + pub fn just_pressed(&self, id: u64) -> bool { |
| 65 | + self.just_pressed.contains(&id) |
| 66 | + } |
| 67 | + |
| 68 | + pub fn iter_just_pressed(&self) -> impl Iterator<Item = &Touch> + '_ { |
| 69 | + self.just_pressed |
| 70 | + .iter() |
| 71 | + .map(move |id| self.active_touches.get(id).unwrap()) |
| 72 | + } |
| 73 | + |
| 74 | + pub fn just_released(&self, id: u64) -> bool { |
| 75 | + self.just_released.contains(&id) |
| 76 | + } |
| 77 | + |
| 78 | + pub fn iter_just_released(&self) -> impl Iterator<Item = &Touch> + '_ { |
| 79 | + self.just_released |
| 80 | + .iter() |
| 81 | + .map(move |id| self.active_touches.get(id).unwrap()) |
| 82 | + } |
| 83 | + |
| 84 | + pub fn just_cancelled(&self, id: u64) -> bool { |
| 85 | + self.just_cancelled.contains(&id) |
| 86 | + } |
| 87 | + |
| 88 | + pub fn iter_just_cancelled(&self) -> impl Iterator<Item = &Touch> + '_ { |
| 89 | + self.just_cancelled |
| 90 | + .iter() |
| 91 | + .map(move |id| self.active_touches.get(id).unwrap()) |
| 92 | + } |
| 93 | +} |
| 94 | + |
| 95 | +/// Updates the Touches resource with the latest TouchInput events |
| 96 | +pub fn touch_screen_input_system( |
| 97 | + mut state: Local<TouchSystemState>, |
| 98 | + mut touch_state: ResMut<Touches>, |
| 99 | + touch_input_events: Res<Events<TouchInput>>, |
| 100 | +) { |
| 101 | + touch_state.just_pressed.clear(); |
| 102 | + |
| 103 | + let released_touch_ids: HashSet<_> = touch_state.just_released.iter().cloned().collect(); |
| 104 | + let cancelled_touch_ids: HashSet<_> = touch_state.just_released.iter().cloned().collect(); |
| 105 | + |
| 106 | + touch_state.just_released.clear(); |
| 107 | + touch_state.just_cancelled.clear(); |
| 108 | + |
| 109 | + for released_id in released_touch_ids { |
| 110 | + touch_state.active_touches.remove(&released_id); |
| 111 | + } |
| 112 | + |
| 113 | + for cancelled_id in cancelled_touch_ids { |
| 114 | + touch_state.active_touches.remove(&cancelled_id); |
| 115 | + } |
| 116 | + |
| 117 | + for event in state.touch_event_reader.iter(&touch_input_events) { |
| 118 | + let active_touch = touch_state.active_touches.get(&event.id); |
| 119 | + match event.phase { |
| 120 | + TouchPhase::Started => { |
| 121 | + touch_state.active_touches.insert( |
| 122 | + event.id, |
| 123 | + Touch { |
| 124 | + id: event.id, |
| 125 | + start_position: event.position, |
| 126 | + previous_position: event.position, |
| 127 | + position: event.position, |
| 128 | + }, |
| 129 | + ); |
| 130 | + touch_state.just_pressed.insert(event.id); |
| 131 | + } |
| 132 | + TouchPhase::Moved => { |
| 133 | + let old_touch = active_touch.unwrap(); |
| 134 | + let mut new_touch = old_touch.clone(); |
| 135 | + new_touch.previous_position = new_touch.position; |
| 136 | + new_touch.position = event.position; |
| 137 | + touch_state.active_touches.insert(event.id, new_touch); |
| 138 | + } |
| 139 | + TouchPhase::Ended => { |
| 140 | + touch_state.just_released.insert(event.id); |
| 141 | + } |
| 142 | + TouchPhase::Cancelled => { |
| 143 | + touch_state.just_cancelled.insert(event.id); |
| 144 | + } |
| 145 | + }; |
| 146 | + } |
| 147 | +} |
0 commit comments