Skip to content

Commit

Permalink
Touch support implementation (bevyengine#696)
Browse files Browse the repository at this point in the history
Adds a basic touch input system
  • Loading branch information
naithar authored and joshuajbouw committed Oct 24, 2020
1 parent 14376ad commit d02fd38
Show file tree
Hide file tree
Showing 8 changed files with 250 additions and 1 deletion.
2 changes: 2 additions & 0 deletions CHANGELOG.md
Original file line number Diff line number Diff line change
Expand Up @@ -3,6 +3,7 @@
## Unreleased

### Added
- [Touch Input][696]
- [Do not depend on spirv on wasm32 target][689]
- [Another fast compile flag for macOS][552]

Expand All @@ -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]

[696]: https://github.com/bevyengine/bevy/pull/696
[689]: https://github.com/bevyengine/bevy/pull/689
[552]: https://github.com/bevyengine/bevy/pull/552
[616]: https://github.com/bevyengine/bevy/pull/616
Expand Down
8 changes: 8 additions & 0 deletions Cargo.toml
Original file line number Diff line number Diff line change
Expand Up @@ -230,6 +230,14 @@ 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 = "touch_input_highlevel"
path = "examples/input/touch_input_highlevel.rs"

[[example]]
name = "scene"
path = "examples/scene/scene.rs"
Expand Down
10 changes: 9 additions & 1 deletion crates/bevy_input/src/lib.rs
Original file line number Diff line number Diff line change
Expand Up @@ -4,6 +4,7 @@ mod input;
pub mod keyboard;
pub mod mouse;
pub mod system;
pub mod touch;

pub use axis::*;
pub use input::*;
Expand All @@ -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, Touches};

use bevy_ecs::IntoQuerySystem;
use gamepad::{GamepadAxis, GamepadButton, GamepadEvent};
Expand Down Expand Up @@ -50,6 +52,12 @@ impl Plugin for InputPlugin {
.add_event::<GamepadEvent>()
.init_resource::<Input<GamepadButton>>()
.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(),
);
}
}
147 changes: 147 additions & 0 deletions crates/bevy_input/src/touch.rs
Original file line number Diff line number Diff line change
@@ -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);
}
};
}
}
15 changes: 15 additions & 0 deletions crates/bevy_winit/src/converters.rs
Original file line number Diff line number Diff line change
@@ -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 {
Expand All @@ -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,
Expand Down
6 changes: 6 additions & 0 deletions crates/bevy_winit/src/lib.rs
Original file line number Diff line number Diff line change
Expand Up @@ -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::*;
Expand Down Expand Up @@ -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, .. } => {
Expand Down
35 changes: 35 additions & 0 deletions examples/input/touch_input.rs
Original file line number Diff line number Diff line change
@@ -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);
}
}
}
28 changes: 28 additions & 0 deletions examples/input/touch_input_highlevel.rs
Original file line number Diff line number Diff line change
@@ -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);
}
}

0 comments on commit d02fd38

Please sign in to comment.