From 4023bc0786101adfb461efbe0415bba50d194bc3 Mon Sep 17 00:00:00 2001 From: Sergey Minakov Date: Wed, 7 Oct 2020 14:59:46 +0300 Subject: [PATCH] Touch support implementation Adds a basic touch input system Co-authored-by: Michael Hills --- CHANGELOG.md | 2 + Cargo.toml | 4 ++ crates/bevy_input/src/lib.rs | 10 ++- crates/bevy_input/src/touch.rs | 99 +++++++++++++++++++++++++++++ crates/bevy_winit/src/converters.rs | 15 +++++ crates/bevy_winit/src/lib.rs | 6 ++ examples/input/touch_input.rs | 40 ++++++++++++ 7 files changed, 175 insertions(+), 1 deletion(-) create mode 100644 crates/bevy_input/src/touch.rs create mode 100644 examples/input/touch_input.rs diff --git a/CHANGELOG.md b/CHANGELOG.md index c2586c5a52354..802880ea0bea9 100644 --- a/CHANGELOG.md +++ b/CHANGELOG.md @@ -3,6 +3,7 @@ ## Unreleased ### Added +- [Touch Input][] - [Do not depend on spirv on wasm32 target][689] - [Another fast compile flag for macOS][552] @@ -15,6 +16,7 @@ - Individual color-components must now be accessed through setters and getters: `.r`, `.g`, `.b`, `.a`, `.set_r`, `.set_g`, `.set_b`, `.set_a`, and the corresponding methods with the `*_linear` suffix. - Despawning an entity multiple times causes a debug-level log message to be emitted instead of a panic [649] [651] +[]: [689]: https://github.com/bevyengine/bevy/pull/689 [552]: https://github.com/bevyengine/bevy/pull/552 [616]: https://github.com/bevyengine/bevy/pull/616 diff --git a/Cargo.toml b/Cargo.toml index 9be0b0bd6e7b7..2b53e634505a7 100644 --- a/Cargo.toml +++ b/Cargo.toml @@ -230,6 +230,10 @@ path = "examples/input/keyboard_input_events.rs" name = "gamepad_input" path = "examples/input/gamepad_input.rs" +[[example]] +name = "touch_input" +path = "examples/input/touch_input.rs" + [[example]] name = "scene" path = "examples/scene/scene.rs" diff --git a/crates/bevy_input/src/lib.rs b/crates/bevy_input/src/lib.rs index 7bf2bbb2cf3be..1e0a901878d5a 100644 --- a/crates/bevy_input/src/lib.rs +++ b/crates/bevy_input/src/lib.rs @@ -4,6 +4,7 @@ mod input; pub mod keyboard; pub mod mouse; pub mod system; +pub mod touch; pub use axis::*; pub use input::*; @@ -23,6 +24,7 @@ pub mod prelude { use bevy_app::prelude::*; use keyboard::{keyboard_input_system, KeyCode, KeyboardInput}; use mouse::{mouse_button_input_system, MouseButton, MouseButtonInput, MouseMotion, MouseWheel}; +use touch::{touch_screen_input_system, TouchInput, TouchInputState}; use bevy_ecs::IntoQuerySystem; use gamepad::{GamepadAxis, GamepadButton, GamepadEvent}; @@ -50,6 +52,12 @@ impl Plugin for InputPlugin { .add_event::() .init_resource::>() .init_resource::>() - .init_resource::>(); + .init_resource::>() + .add_event::() + .init_resource::() + .add_system_to_stage( + bevy_app::stage::EVENT_UPDATE, + touch_screen_input_system.system(), + ); } } diff --git a/crates/bevy_input/src/touch.rs b/crates/bevy_input/src/touch.rs new file mode 100644 index 0000000000000..8254d18df3835 --- /dev/null +++ b/crates/bevy_input/src/touch.rs @@ -0,0 +1,99 @@ +use bevy_app::{EventReader, Events}; +use bevy_ecs::{Local, Res, ResMut}; +use bevy_math::Vec2; +use std::collections::{HashMap, HashSet}; + +/// A touch input event +#[derive(Debug, Clone)] +pub struct TouchInput { + pub phase: TouchPhase, + pub position: Vec2, + /// + /// ## Platform-specific + /// + /// Unique identifier of a finger. + pub id: u64, +} + +/// 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)] +pub struct ActiveTouch { + start_position: Vec2, + previous_position: Vec2, + current_position: Vec2, +} + +impl ActiveTouch { + pub fn delta(&self) -> Vec2 { + self.current_position - self.previous_position + } + + pub fn distance(&self) -> Vec2 { + self.current_position - self.start_position + } +} + +#[derive(Default)] +pub struct TouchInputState { + pub active_touches: HashMap, + pub just_pressed: HashSet, + pub just_released: HashSet, + pub just_cancelled: HashSet, +} + +/// Updates the TouchInputState resource with the latest TouchInput events +pub fn touch_screen_input_system( + mut state: Local, + mut touch_state: ResMut, + touch_input_events: Res>, +) { + touch_state.just_pressed.clear(); + touch_state.just_released.clear(); + touch_state.just_cancelled.clear(); + + for event in state.touch_event_reader.iter(&touch_input_events) { + let active_touch = touch_state.active_touches.get(&event.id); + match event.phase { + TouchPhase::Started => { + touch_state.active_touches.insert( + event.id, + ActiveTouch { + start_position: event.position, + previous_position: event.position, + current_position: event.position, + }, + ); + touch_state.just_pressed.insert(event.id); + } + TouchPhase::Moved => { + let old_touch = active_touch.unwrap(); + let mut new_touch = old_touch.clone(); + new_touch.previous_position = new_touch.current_position; + new_touch.current_position = event.position; + touch_state.active_touches.insert(event.id, new_touch); + } + TouchPhase::Ended => { + touch_state.active_touches.remove(&event.id); + touch_state.just_released.insert(event.id); + } + TouchPhase::Cancelled => { + touch_state.active_touches.remove(&event.id); + touch_state.just_cancelled.insert(event.id); + } + }; + } +} diff --git a/crates/bevy_winit/src/converters.rs b/crates/bevy_winit/src/converters.rs index db8cc15c2efa9..8bc04e20b55f7 100644 --- a/crates/bevy_winit/src/converters.rs +++ b/crates/bevy_winit/src/converters.rs @@ -1,7 +1,9 @@ use bevy_input::{ keyboard::{ElementState, KeyCode, KeyboardInput}, mouse::MouseButton, + touch::{TouchInput, TouchPhase}, }; +use bevy_math::Vec2; pub fn convert_keyboard_input(keyboard_input: &winit::event::KeyboardInput) -> KeyboardInput { KeyboardInput { @@ -27,6 +29,19 @@ pub fn convert_mouse_button(mouse_button: winit::event::MouseButton) -> MouseBut } } +pub fn convert_touch_input(touch_input: winit::event::Touch) -> TouchInput { + TouchInput { + phase: match touch_input.phase { + winit::event::TouchPhase::Started => TouchPhase::Started, + winit::event::TouchPhase::Moved => TouchPhase::Moved, + winit::event::TouchPhase::Ended => TouchPhase::Ended, + winit::event::TouchPhase::Cancelled => TouchPhase::Cancelled, + }, + position: Vec2::new(touch_input.location.x as f32, touch_input.location.y as f32), + id: touch_input.id, + } +} + pub fn convert_virtual_key_code(virtual_key_code: winit::event::VirtualKeyCode) -> KeyCode { match virtual_key_code { winit::event::VirtualKeyCode::Key1 => KeyCode::Key1, diff --git a/crates/bevy_winit/src/lib.rs b/crates/bevy_winit/src/lib.rs index 5b47d1c9d9b87..9b6b24c1c70b2 100644 --- a/crates/bevy_winit/src/lib.rs +++ b/crates/bevy_winit/src/lib.rs @@ -4,6 +4,7 @@ mod winit_windows; use bevy_input::{ keyboard::KeyboardInput, mouse::{MouseButtonInput, MouseMotion, MouseScrollUnit, MouseWheel}, + touch::TouchInput, }; pub use winit_config::*; pub use winit_windows::*; @@ -258,6 +259,11 @@ pub fn winit_runner(mut app: App) { }); } }, + WindowEvent::Touch(touch) => { + let mut touch_input_events = + app.resources.get_mut::>().unwrap(); + touch_input_events.send(converters::convert_touch_input(touch)); + } _ => {} }, event::Event::DeviceEvent { ref event, .. } => { diff --git a/examples/input/touch_input.rs b/examples/input/touch_input.rs new file mode 100644 index 0000000000000..013249ed4f155 --- /dev/null +++ b/examples/input/touch_input.rs @@ -0,0 +1,40 @@ +use bevy::{input::touch::*, prelude::*}; + +fn main() { + App::build() + .add_default_plugins() + .init_resource::() + .add_system(touch_system.system()) + .run(); +} + +#[derive(Default)] +struct TouchHandlerState { + touch_event_reader: EventReader, +} + +fn touch_system( + mut state: ResMut, + touch_events: Res>, + touch_input_state: Res, +) { + for event in state.touch_event_reader.iter(&touch_events) { + if touch_input_state.just_pressed.contains(&event.id) { + println!( + "just pressed touch with id: {:?}, at: {:?}", + event.id, event.position + ); + } + + if touch_input_state.just_released.contains(&event.id) { + println!( + "just released touch with id: {:?}, at: {:?}", + event.id, event.position + ); + } + + if touch_input_state.just_cancelled.contains(&event.id) { + println!("cancelled touch with id: {:?}", event.id); + } + } +}