Touch support implementation (#696)

Adds a basic touch input system
This commit is contained in:
Sergey Minakov 2020-10-18 22:24:01 +03:00 committed by GitHub
parent 5df6804daf
commit a80469bd13
No known key found for this signature in database
GPG key ID: 4AEE18F83AFDEB23
8 changed files with 250 additions and 1 deletions

View file

@ -3,6 +3,7 @@
## Unreleased ## Unreleased
### Added ### Added
- [Touch Input][696]
- [Do not depend on spirv on wasm32 target][689] - [Do not depend on spirv on wasm32 target][689]
- [Another fast compile flag for macOS][552] - [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. - 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] - Despawning an entity multiple times causes a debug-level log message to be emitted instead of a panic [649] [651]
[696]: https://github.com/bevyengine/bevy/pull/696
[689]: https://github.com/bevyengine/bevy/pull/689 [689]: https://github.com/bevyengine/bevy/pull/689
[552]: https://github.com/bevyengine/bevy/pull/552 [552]: https://github.com/bevyengine/bevy/pull/552
[616]: https://github.com/bevyengine/bevy/pull/616 [616]: https://github.com/bevyengine/bevy/pull/616

View file

@ -230,6 +230,14 @@ path = "examples/input/keyboard_input_events.rs"
name = "gamepad_input" name = "gamepad_input"
path = "examples/input/gamepad_input.rs" path = "examples/input/gamepad_input.rs"
[[example]]
name = "touch_input"
path = "examples/input/touch_input.rs"
[[example]]
name = "touch_input_highlevel"
path = "examples/input/touch_input_highlevel.rs"
[[example]] [[example]]
name = "scene" name = "scene"
path = "examples/scene/scene.rs" path = "examples/scene/scene.rs"

View file

@ -4,6 +4,7 @@ mod input;
pub mod keyboard; pub mod keyboard;
pub mod mouse; pub mod mouse;
pub mod system; pub mod system;
pub mod touch;
pub use axis::*; pub use axis::*;
pub use input::*; pub use input::*;
@ -23,6 +24,7 @@ pub mod prelude {
use bevy_app::prelude::*; use bevy_app::prelude::*;
use keyboard::{keyboard_input_system, KeyCode, KeyboardInput}; use keyboard::{keyboard_input_system, KeyCode, KeyboardInput};
use mouse::{mouse_button_input_system, MouseButton, MouseButtonInput, MouseMotion, MouseWheel}; use mouse::{mouse_button_input_system, MouseButton, MouseButtonInput, MouseMotion, MouseWheel};
use touch::{touch_screen_input_system, TouchInput, Touches};
use bevy_ecs::IntoQuerySystem; use bevy_ecs::IntoQuerySystem;
use gamepad::{GamepadAxis, GamepadButton, GamepadEvent}; use gamepad::{GamepadAxis, GamepadButton, GamepadEvent};
@ -50,6 +52,12 @@ impl Plugin for InputPlugin {
.add_event::<GamepadEvent>() .add_event::<GamepadEvent>()
.init_resource::<Input<GamepadButton>>() .init_resource::<Input<GamepadButton>>()
.init_resource::<Axis<GamepadAxis>>() .init_resource::<Axis<GamepadAxis>>()
.init_resource::<Axis<GamepadButton>>(); .init_resource::<Axis<GamepadButton>>()
.add_event::<TouchInput>()
.init_resource::<Touches>()
.add_system_to_stage(
bevy_app::stage::EVENT_UPDATE,
touch_screen_input_system.system(),
);
} }
} }

View file

@ -0,0 +1,147 @@
use bevy_app::{EventReader, Events};
use bevy_ecs::{Local, Res, ResMut};
use bevy_math::Vec2;
use bevy_utils::{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<TouchInput>,
}
#[derive(Debug, Clone)]
pub struct Touch {
pub id: u64,
pub start_position: Vec2,
pub previous_position: Vec2,
pub position: Vec2,
}
impl Touch {
pub fn delta(&self) -> Vec2 {
self.position - self.previous_position
}
pub fn distance(&self) -> Vec2 {
self.position - self.start_position
}
}
#[derive(Default)]
pub struct Touches {
active_touches: HashMap<u64, Touch>,
just_pressed: HashSet<u64>,
just_released: HashSet<u64>,
just_cancelled: HashSet<u64>,
}
impl Touches {
pub fn iter(&self) -> impl Iterator<Item = &Touch> + '_ {
self.active_touches.values()
}
pub fn just_pressed(&self, id: u64) -> bool {
self.just_pressed.contains(&id)
}
pub fn iter_just_pressed(&self) -> impl Iterator<Item = &Touch> + '_ {
self.just_pressed
.iter()
.map(move |id| self.active_touches.get(id).unwrap())
}
pub fn just_released(&self, id: u64) -> bool {
self.just_released.contains(&id)
}
pub fn iter_just_released(&self) -> impl Iterator<Item = &Touch> + '_ {
self.just_released
.iter()
.map(move |id| self.active_touches.get(id).unwrap())
}
pub fn just_cancelled(&self, id: u64) -> bool {
self.just_cancelled.contains(&id)
}
pub fn iter_just_cancelled(&self) -> impl Iterator<Item = &Touch> + '_ {
self.just_cancelled
.iter()
.map(move |id| self.active_touches.get(id).unwrap())
}
}
/// Updates the Touches resource with the latest TouchInput events
pub fn touch_screen_input_system(
mut state: Local<TouchSystemState>,
mut touch_state: ResMut<Touches>,
touch_input_events: Res<Events<TouchInput>>,
) {
touch_state.just_pressed.clear();
let released_touch_ids: HashSet<_> = touch_state.just_released.iter().cloned().collect();
let cancelled_touch_ids: HashSet<_> = touch_state.just_released.iter().cloned().collect();
touch_state.just_released.clear();
touch_state.just_cancelled.clear();
for released_id in released_touch_ids {
touch_state.active_touches.remove(&released_id);
}
for cancelled_id in cancelled_touch_ids {
touch_state.active_touches.remove(&cancelled_id);
}
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,
Touch {
id: event.id,
start_position: event.position,
previous_position: event.position,
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.position;
new_touch.position = event.position;
touch_state.active_touches.insert(event.id, new_touch);
}
TouchPhase::Ended => {
touch_state.just_released.insert(event.id);
}
TouchPhase::Cancelled => {
touch_state.just_cancelled.insert(event.id);
}
};
}
}

View file

@ -1,7 +1,9 @@
use bevy_input::{ use bevy_input::{
keyboard::{ElementState, KeyCode, KeyboardInput}, keyboard::{ElementState, KeyCode, KeyboardInput},
mouse::MouseButton, mouse::MouseButton,
touch::{TouchInput, TouchPhase},
}; };
use bevy_math::Vec2;
pub fn convert_keyboard_input(keyboard_input: &winit::event::KeyboardInput) -> KeyboardInput { pub fn convert_keyboard_input(keyboard_input: &winit::event::KeyboardInput) -> 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 { pub fn convert_virtual_key_code(virtual_key_code: winit::event::VirtualKeyCode) -> KeyCode {
match virtual_key_code { match virtual_key_code {
winit::event::VirtualKeyCode::Key1 => KeyCode::Key1, winit::event::VirtualKeyCode::Key1 => KeyCode::Key1,

View file

@ -4,6 +4,7 @@ mod winit_windows;
use bevy_input::{ use bevy_input::{
keyboard::KeyboardInput, keyboard::KeyboardInput,
mouse::{MouseButtonInput, MouseMotion, MouseScrollUnit, MouseWheel}, mouse::{MouseButtonInput, MouseMotion, MouseScrollUnit, MouseWheel},
touch::TouchInput,
}; };
pub use winit_config::*; pub use winit_config::*;
pub use winit_windows::*; 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::<Events<TouchInput>>().unwrap();
touch_input_events.send(converters::convert_touch_input(touch));
}
_ => {} _ => {}
}, },
event::Event::DeviceEvent { ref event, .. } => { event::Event::DeviceEvent { ref event, .. } => {

View file

@ -0,0 +1,35 @@
use bevy::{input::touch::*, prelude::*};
fn main() {
App::build()
.add_default_plugins()
.add_system(touch_system.system())
.run();
}
fn touch_system(touches: Res<Touches>) {
for touch in touches.iter() {
println!(
"active touch: {} {} {} {}",
touch.id, touch.position, touch.previous_position, touch.start_position
);
if touches.just_pressed(touch.id) {
println!(
"just pressed touch with id: {:?}, at: {:?}",
touch.id, touch.position
);
}
if touches.just_released(touch.id) {
println!(
"just released touch with id: {:?}, at: {:?}",
touch.id, touch.position
);
}
if touches.just_cancelled(touch.id) {
println!("cancelled touch with id: {:?}", touch.id);
}
}
}

View file

@ -0,0 +1,28 @@
use bevy::{input::touch::*, prelude::*};
fn main() {
App::build()
.add_default_plugins()
.add_system(touch_system.system())
.run();
}
fn touch_system(touches: Res<Touches>) {
for touch in touches.iter_just_pressed() {
println!(
"just pressed touch with id: {:?}, at: {:?}",
touch.id, touch.position
);
}
for touch in touches.iter_just_released() {
println!(
"just released touch with id: {:?}, at: {:?}",
touch.id, touch.position
);
}
for touch in touches.iter_just_cancelled() {
println!("cancelled touch with id: {:?}", touch.id);
}
}