use bevy_app::{EventReader, Events}; use bevy_ecs::{Local, Res, ResMut}; use bevy_math::Vec2; use bevy_utils::HashMap; use core::ops::DerefMut; /// Represents a touch event /// /// Every time the user touches the screen, a new `Start` event with an unique /// identifier for the finger is generated. When the finger is lifted, an `End` /// event is generated with the same finger id. /// /// After a `Start` event has been emitted, there may be zero or more `Move` /// events when the finger is moved or the touch pressure changes. /// /// The finger id may be reused by the system after an `End` event. The user /// should assume that a new `Start` event received with the same id has nothing /// to do with the old finger and is a new finger. /// /// A `Cancelled` event is emitted when the system has canceled tracking this /// touch, such as when the window loses focus, or on iOS if the user moves the /// device against their face. #[derive(Debug, Clone, Copy, PartialEq)] pub struct TouchInput { pub phase: TouchPhase, pub position: Vec2, /// Describes how hard the screen was pressed. May be `None` if the platform /// does not support pressure sensitivity. /// /// ## Platform-specific /// /// - Only available on **iOS** 9.0+ and **Windows** 8+. pub force: Option, /// Unique identifier of a finger. pub id: u64, } /// Describes the force of a touch event #[derive(Debug, Clone, Copy, PartialEq)] pub enum ForceTouch { /// On iOS, the force is calibrated so that the same number corresponds to /// roughly the same amount of pressure on the screen regardless of the /// device. Calibrated { /// The force of the touch, where a value of 1.0 represents the force of /// an average touch (predetermined by the system, not user-specific). /// /// The force reported by Apple Pencil is measured along the axis of the /// pencil. If you want a force perpendicular to the device, you need to /// calculate this value using the `altitude_angle` value. force: f64, /// The maximum possible force for a touch. /// /// The value of this field is sufficiently high to provide a wide /// dynamic range for values of the `force` field. max_possible_force: f64, /// The altitude (in radians) of the stylus. /// /// A value of 0 radians indicates that the stylus is parallel to the /// surface. The value of this property is Pi/2 when the stylus is /// perpendicular to the surface. altitude_angle: Option, }, /// If the platform reports the force as normalized, we have no way of /// knowing how much pressure 1.0 corresponds to – we know it's the maximum /// amount of force, but as to how much force, you might either have to /// press really really hard, or not hard at all, depending on the device. Normalized(f64), } /// Describes touch-screen input state. #[derive(Debug, Hash, PartialEq, Eq, Clone, Copy)] #[cfg_attr(feature = "serialize", derive(serde::Serialize, serde::Deserialize))] pub enum TouchPhase { Started, Moved, Ended, Cancelled, } #[derive(Default)] pub struct TouchSystemState { touch_event_reader: EventReader, } #[derive(Debug, Clone, Copy)] pub struct Touch { pub id: u64, pub start_position: Vec2, pub start_force: Option, pub previous_position: Vec2, pub previous_force: Option, pub position: Vec2, pub force: Option, } impl Touch { pub fn delta(&self) -> Vec2 { self.position - self.previous_position } pub fn distance(&self) -> Vec2 { self.position - self.start_position } } impl From<&TouchInput> for Touch { fn from(input: &TouchInput) -> Touch { Touch { id: input.id, start_position: input.position, start_force: input.force, previous_position: input.position, previous_force: input.force, position: input.position, force: input.force, } } } #[derive(Debug, Clone, Default)] pub struct Touches { pressed: HashMap, just_pressed: HashMap, just_released: HashMap, just_cancelled: HashMap, } impl Touches { pub fn iter(&self) -> impl Iterator + '_ { self.pressed.values() } pub fn get_pressed(&self, id: u64) -> Option<&Touch> { self.pressed.get(&id) } pub fn just_pressed(&self, id: u64) -> bool { self.just_pressed.contains_key(&id) } pub fn iter_just_pressed(&self) -> impl Iterator + '_ { self.just_pressed .iter() .map(move |(id, _)| self.pressed.get(id).unwrap()) } pub fn get_released(&self, id: u64) -> Option<&Touch> { self.just_released.get(&id) } pub fn just_released(&self, id: u64) -> bool { self.just_released.contains_key(&id) } pub fn iter_just_released(&self) -> impl Iterator + '_ { self.just_released .iter() .map(move |(id, _)| self.pressed.get(id).unwrap()) } pub fn just_cancelled(&self, id: u64) -> bool { self.just_cancelled.contains_key(&id) } pub fn iter_just_cancelled(&self) -> impl Iterator + '_ { self.just_cancelled .iter() .map(move |(id, _)| self.pressed.get(id).unwrap()) } } /// Updates the Touches resource with the latest TouchInput events pub fn touch_screen_input_system( mut state: Local, mut touch_state: ResMut, touch_input_events: Res>, ) { let touch_state = touch_state.deref_mut(); touch_state.just_pressed.clear(); touch_state.just_released.clear(); for event in state.touch_event_reader.iter(&touch_input_events) { match event.phase { TouchPhase::Started => { touch_state.pressed.insert(event.id, event.into()); touch_state.just_pressed.insert(event.id, event.into()); } TouchPhase::Moved => { let mut new_touch = touch_state.pressed.get(&event.id).cloned().unwrap(); new_touch.previous_position = new_touch.position; new_touch.previous_force = new_touch.force; new_touch.position = event.position; new_touch.force = event.force; touch_state.pressed.insert(event.id, new_touch); } TouchPhase::Ended => { touch_state.just_released.insert(event.id, event.into()); touch_state.pressed.remove_entry(&event.id); } TouchPhase::Cancelled => { touch_state.just_cancelled.insert(event.id, event.into()); touch_state.pressed.remove_entry(&event.id); } }; } }