Skip to content

Commit a80469b

Browse files
authored
Touch support implementation (#696)
Adds a basic touch input system
1 parent 5df6804 commit a80469b

File tree

8 files changed

+250
-1
lines changed

8 files changed

+250
-1
lines changed

CHANGELOG.md

+2
Original file line numberDiff line numberDiff line change
@@ -3,6 +3,7 @@
33
## Unreleased
44

55
### Added
6+
- [Touch Input][696]
67
- [Do not depend on spirv on wasm32 target][689]
78
- [Another fast compile flag for macOS][552]
89

@@ -15,6 +16,7 @@
1516
- 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.
1617
- Despawning an entity multiple times causes a debug-level log message to be emitted instead of a panic [649] [651]
1718

19+
[696]: https://github.com/bevyengine/bevy/pull/696
1820
[689]: https://github.com/bevyengine/bevy/pull/689
1921
[552]: https://github.com/bevyengine/bevy/pull/552
2022
[616]: https://github.com/bevyengine/bevy/pull/616

Cargo.toml

+8
Original file line numberDiff line numberDiff line change
@@ -230,6 +230,14 @@ path = "examples/input/keyboard_input_events.rs"
230230
name = "gamepad_input"
231231
path = "examples/input/gamepad_input.rs"
232232

233+
[[example]]
234+
name = "touch_input"
235+
path = "examples/input/touch_input.rs"
236+
237+
[[example]]
238+
name = "touch_input_highlevel"
239+
path = "examples/input/touch_input_highlevel.rs"
240+
233241
[[example]]
234242
name = "scene"
235243
path = "examples/scene/scene.rs"

crates/bevy_input/src/lib.rs

+9-1
Original file line numberDiff line numberDiff line change
@@ -4,6 +4,7 @@ mod input;
44
pub mod keyboard;
55
pub mod mouse;
66
pub mod system;
7+
pub mod touch;
78

89
pub use axis::*;
910
pub use input::*;
@@ -23,6 +24,7 @@ pub mod prelude {
2324
use bevy_app::prelude::*;
2425
use keyboard::{keyboard_input_system, KeyCode, KeyboardInput};
2526
use mouse::{mouse_button_input_system, MouseButton, MouseButtonInput, MouseMotion, MouseWheel};
27+
use touch::{touch_screen_input_system, TouchInput, Touches};
2628

2729
use bevy_ecs::IntoQuerySystem;
2830
use gamepad::{GamepadAxis, GamepadButton, GamepadEvent};
@@ -50,6 +52,12 @@ impl Plugin for InputPlugin {
5052
.add_event::<GamepadEvent>()
5153
.init_resource::<Input<GamepadButton>>()
5254
.init_resource::<Axis<GamepadAxis>>()
53-
.init_resource::<Axis<GamepadButton>>();
55+
.init_resource::<Axis<GamepadButton>>()
56+
.add_event::<TouchInput>()
57+
.init_resource::<Touches>()
58+
.add_system_to_stage(
59+
bevy_app::stage::EVENT_UPDATE,
60+
touch_screen_input_system.system(),
61+
);
5462
}
5563
}

crates/bevy_input/src/touch.rs

+147
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,147 @@
1+
use bevy_app::{EventReader, Events};
2+
use bevy_ecs::{Local, Res, ResMut};
3+
use bevy_math::Vec2;
4+
use bevy_utils::{HashMap, HashSet};
5+
6+
/// A touch input event
7+
#[derive(Debug, Clone)]
8+
pub struct TouchInput {
9+
pub phase: TouchPhase,
10+
pub position: Vec2,
11+
///
12+
/// ## Platform-specific
13+
///
14+
/// Unique identifier of a finger.
15+
pub id: u64,
16+
}
17+
18+
/// Describes touch-screen input state.
19+
#[derive(Debug, Hash, PartialEq, Eq, Clone, Copy)]
20+
#[cfg_attr(feature = "serialize", derive(serde::Serialize, serde::Deserialize))]
21+
pub enum TouchPhase {
22+
Started,
23+
Moved,
24+
Ended,
25+
Cancelled,
26+
}
27+
28+
#[derive(Default)]
29+
pub struct TouchSystemState {
30+
touch_event_reader: EventReader<TouchInput>,
31+
}
32+
33+
#[derive(Debug, Clone)]
34+
pub struct Touch {
35+
pub id: u64,
36+
pub start_position: Vec2,
37+
pub previous_position: Vec2,
38+
pub position: Vec2,
39+
}
40+
41+
impl Touch {
42+
pub fn delta(&self) -> Vec2 {
43+
self.position - self.previous_position
44+
}
45+
46+
pub fn distance(&self) -> Vec2 {
47+
self.position - self.start_position
48+
}
49+
}
50+
51+
#[derive(Default)]
52+
pub struct Touches {
53+
active_touches: HashMap<u64, Touch>,
54+
just_pressed: HashSet<u64>,
55+
just_released: HashSet<u64>,
56+
just_cancelled: HashSet<u64>,
57+
}
58+
59+
impl Touches {
60+
pub fn iter(&self) -> impl Iterator<Item = &Touch> + '_ {
61+
self.active_touches.values()
62+
}
63+
64+
pub fn just_pressed(&self, id: u64) -> bool {
65+
self.just_pressed.contains(&id)
66+
}
67+
68+
pub fn iter_just_pressed(&self) -> impl Iterator<Item = &Touch> + '_ {
69+
self.just_pressed
70+
.iter()
71+
.map(move |id| self.active_touches.get(id).unwrap())
72+
}
73+
74+
pub fn just_released(&self, id: u64) -> bool {
75+
self.just_released.contains(&id)
76+
}
77+
78+
pub fn iter_just_released(&self) -> impl Iterator<Item = &Touch> + '_ {
79+
self.just_released
80+
.iter()
81+
.map(move |id| self.active_touches.get(id).unwrap())
82+
}
83+
84+
pub fn just_cancelled(&self, id: u64) -> bool {
85+
self.just_cancelled.contains(&id)
86+
}
87+
88+
pub fn iter_just_cancelled(&self) -> impl Iterator<Item = &Touch> + '_ {
89+
self.just_cancelled
90+
.iter()
91+
.map(move |id| self.active_touches.get(id).unwrap())
92+
}
93+
}
94+
95+
/// Updates the Touches resource with the latest TouchInput events
96+
pub fn touch_screen_input_system(
97+
mut state: Local<TouchSystemState>,
98+
mut touch_state: ResMut<Touches>,
99+
touch_input_events: Res<Events<TouchInput>>,
100+
) {
101+
touch_state.just_pressed.clear();
102+
103+
let released_touch_ids: HashSet<_> = touch_state.just_released.iter().cloned().collect();
104+
let cancelled_touch_ids: HashSet<_> = touch_state.just_released.iter().cloned().collect();
105+
106+
touch_state.just_released.clear();
107+
touch_state.just_cancelled.clear();
108+
109+
for released_id in released_touch_ids {
110+
touch_state.active_touches.remove(&released_id);
111+
}
112+
113+
for cancelled_id in cancelled_touch_ids {
114+
touch_state.active_touches.remove(&cancelled_id);
115+
}
116+
117+
for event in state.touch_event_reader.iter(&touch_input_events) {
118+
let active_touch = touch_state.active_touches.get(&event.id);
119+
match event.phase {
120+
TouchPhase::Started => {
121+
touch_state.active_touches.insert(
122+
event.id,
123+
Touch {
124+
id: event.id,
125+
start_position: event.position,
126+
previous_position: event.position,
127+
position: event.position,
128+
},
129+
);
130+
touch_state.just_pressed.insert(event.id);
131+
}
132+
TouchPhase::Moved => {
133+
let old_touch = active_touch.unwrap();
134+
let mut new_touch = old_touch.clone();
135+
new_touch.previous_position = new_touch.position;
136+
new_touch.position = event.position;
137+
touch_state.active_touches.insert(event.id, new_touch);
138+
}
139+
TouchPhase::Ended => {
140+
touch_state.just_released.insert(event.id);
141+
}
142+
TouchPhase::Cancelled => {
143+
touch_state.just_cancelled.insert(event.id);
144+
}
145+
};
146+
}
147+
}

crates/bevy_winit/src/converters.rs

+15
Original file line numberDiff line numberDiff line change
@@ -1,7 +1,9 @@
11
use bevy_input::{
22
keyboard::{ElementState, KeyCode, KeyboardInput},
33
mouse::MouseButton,
4+
touch::{TouchInput, TouchPhase},
45
};
6+
use bevy_math::Vec2;
57

68
pub fn convert_keyboard_input(keyboard_input: &winit::event::KeyboardInput) -> KeyboardInput {
79
KeyboardInput {
@@ -27,6 +29,19 @@ pub fn convert_mouse_button(mouse_button: winit::event::MouseButton) -> MouseBut
2729
}
2830
}
2931

32+
pub fn convert_touch_input(touch_input: winit::event::Touch) -> TouchInput {
33+
TouchInput {
34+
phase: match touch_input.phase {
35+
winit::event::TouchPhase::Started => TouchPhase::Started,
36+
winit::event::TouchPhase::Moved => TouchPhase::Moved,
37+
winit::event::TouchPhase::Ended => TouchPhase::Ended,
38+
winit::event::TouchPhase::Cancelled => TouchPhase::Cancelled,
39+
},
40+
position: Vec2::new(touch_input.location.x as f32, touch_input.location.y as f32),
41+
id: touch_input.id,
42+
}
43+
}
44+
3045
pub fn convert_virtual_key_code(virtual_key_code: winit::event::VirtualKeyCode) -> KeyCode {
3146
match virtual_key_code {
3247
winit::event::VirtualKeyCode::Key1 => KeyCode::Key1,

crates/bevy_winit/src/lib.rs

+6
Original file line numberDiff line numberDiff line change
@@ -4,6 +4,7 @@ mod winit_windows;
44
use bevy_input::{
55
keyboard::KeyboardInput,
66
mouse::{MouseButtonInput, MouseMotion, MouseScrollUnit, MouseWheel},
7+
touch::TouchInput,
78
};
89
pub use winit_config::*;
910
pub use winit_windows::*;
@@ -258,6 +259,11 @@ pub fn winit_runner(mut app: App) {
258259
});
259260
}
260261
},
262+
WindowEvent::Touch(touch) => {
263+
let mut touch_input_events =
264+
app.resources.get_mut::<Events<TouchInput>>().unwrap();
265+
touch_input_events.send(converters::convert_touch_input(touch));
266+
}
261267
_ => {}
262268
},
263269
event::Event::DeviceEvent { ref event, .. } => {

examples/input/touch_input.rs

+35
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,35 @@
1+
use bevy::{input::touch::*, prelude::*};
2+
3+
fn main() {
4+
App::build()
5+
.add_default_plugins()
6+
.add_system(touch_system.system())
7+
.run();
8+
}
9+
10+
fn touch_system(touches: Res<Touches>) {
11+
for touch in touches.iter() {
12+
println!(
13+
"active touch: {} {} {} {}",
14+
touch.id, touch.position, touch.previous_position, touch.start_position
15+
);
16+
17+
if touches.just_pressed(touch.id) {
18+
println!(
19+
"just pressed touch with id: {:?}, at: {:?}",
20+
touch.id, touch.position
21+
);
22+
}
23+
24+
if touches.just_released(touch.id) {
25+
println!(
26+
"just released touch with id: {:?}, at: {:?}",
27+
touch.id, touch.position
28+
);
29+
}
30+
31+
if touches.just_cancelled(touch.id) {
32+
println!("cancelled touch with id: {:?}", touch.id);
33+
}
34+
}
35+
}
+28
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,28 @@
1+
use bevy::{input::touch::*, prelude::*};
2+
3+
fn main() {
4+
App::build()
5+
.add_default_plugins()
6+
.add_system(touch_system.system())
7+
.run();
8+
}
9+
10+
fn touch_system(touches: Res<Touches>) {
11+
for touch in touches.iter_just_pressed() {
12+
println!(
13+
"just pressed touch with id: {:?}, at: {:?}",
14+
touch.id, touch.position
15+
);
16+
}
17+
18+
for touch in touches.iter_just_released() {
19+
println!(
20+
"just released touch with id: {:?}, at: {:?}",
21+
touch.id, touch.position
22+
);
23+
}
24+
25+
for touch in touches.iter_just_cancelled() {
26+
println!("cancelled touch with id: {:?}", touch.id);
27+
}
28+
}

0 commit comments

Comments
 (0)