Skip to content
New issue

Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.

By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.

Already on GitHub? Sign in to your account

Touch support implementation #696

Merged
merged 1 commit into from
Oct 18, 2020
Merged
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension


Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
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);
}
}