Book a Demo!
CoCalc Logo Icon
StoreFeaturesDocsShareSupportNewsAboutPoliciesSign UpSign In
bevyengine
GitHub Repository: bevyengine/bevy
Path: blob/main/crates/bevy_picking/src/pointer.rs
6596 views
1
//! Types and systems for pointer inputs, such as position and buttons.
2
//!
3
//! The picking system is built around the concept of a 'Pointer', which is an
4
//! abstract representation of a user input with a specific screen location. The cursor
5
//! and touch input is provided under [`input`](`crate::input`), but you can also implement
6
//! your own custom pointers by supplying a unique ID.
7
//!
8
//! The purpose of this module is primarily to provide a common interface that can be
9
//! driven by lower-level input devices and consumed by higher-level interaction systems.
10
11
use bevy_camera::Camera;
12
use bevy_camera::NormalizedRenderTarget;
13
use bevy_ecs::prelude::*;
14
use bevy_input::mouse::MouseScrollUnit;
15
use bevy_math::Vec2;
16
use bevy_platform::collections::HashMap;
17
use bevy_reflect::prelude::*;
18
use bevy_window::PrimaryWindow;
19
20
use uuid::Uuid;
21
22
use core::{fmt::Debug, ops::Deref};
23
24
use crate::backend::HitData;
25
26
/// Identifies a unique pointer entity. `Mouse` and `Touch` pointers are automatically spawned.
27
///
28
/// This component is needed because pointers can be spawned and despawned, but they need to have a
29
/// stable ID that persists regardless of the Entity they are associated with.
30
#[derive(Debug, Default, Clone, Copy, Eq, PartialEq, Hash, Component, Reflect)]
31
#[require(PointerLocation, PointerPress, PointerInteraction)]
32
#[reflect(Component, Default, Debug, Hash, PartialEq, Clone)]
33
pub enum PointerId {
34
/// The mouse pointer.
35
#[default]
36
Mouse,
37
/// A touch input, usually numbered by window touch events from `winit`.
38
Touch(u64),
39
/// A custom, uniquely identified pointer. Useful for mocking inputs or implementing a software
40
/// controlled cursor.
41
#[reflect(ignore, clone)]
42
Custom(Uuid),
43
}
44
45
impl PointerId {
46
/// Returns true if the pointer is a touch input.
47
pub fn is_touch(&self) -> bool {
48
matches!(self, PointerId::Touch(_))
49
}
50
/// Returns true if the pointer is the mouse.
51
pub fn is_mouse(&self) -> bool {
52
matches!(self, PointerId::Mouse)
53
}
54
/// Returns true if the pointer is a custom input.
55
pub fn is_custom(&self) -> bool {
56
matches!(self, PointerId::Custom(_))
57
}
58
/// Returns the touch id if the pointer is a touch input.
59
pub fn get_touch_id(&self) -> Option<u64> {
60
if let PointerId::Touch(id) = self {
61
Some(*id)
62
} else {
63
None
64
}
65
}
66
}
67
68
/// Holds a list of entities this pointer is currently interacting with, sorted from nearest to
69
/// farthest.
70
#[derive(Debug, Default, Clone, Component, Reflect)]
71
#[reflect(Component, Default, Debug, Clone)]
72
pub struct PointerInteraction {
73
pub(crate) sorted_entities: Vec<(Entity, HitData)>,
74
}
75
76
impl PointerInteraction {
77
/// Returns the nearest hit entity and data about that intersection.
78
pub fn get_nearest_hit(&self) -> Option<&(Entity, HitData)> {
79
self.sorted_entities.first()
80
}
81
}
82
83
impl Deref for PointerInteraction {
84
type Target = Vec<(Entity, HitData)>;
85
86
fn deref(&self) -> &Self::Target {
87
&self.sorted_entities
88
}
89
}
90
91
/// A resource that maps each [`PointerId`] to their [`Entity`] for easy lookups.
92
#[derive(Debug, Clone, Default, Resource)]
93
pub struct PointerMap {
94
inner: HashMap<PointerId, Entity>,
95
}
96
97
impl PointerMap {
98
/// Get the [`Entity`] of the supplied [`PointerId`].
99
pub fn get_entity(&self, pointer_id: PointerId) -> Option<Entity> {
100
self.inner.get(&pointer_id).copied()
101
}
102
}
103
104
/// Update the [`PointerMap`] resource with the current frame's data.
105
pub fn update_pointer_map(pointers: Query<(Entity, &PointerId)>, mut map: ResMut<PointerMap>) {
106
map.inner.clear();
107
for (entity, id) in &pointers {
108
map.inner.insert(*id, entity);
109
}
110
}
111
112
/// Tracks the state of the pointer's buttons in response to [`PointerInput`] events.
113
#[derive(Debug, Default, Clone, Component, Reflect, PartialEq, Eq)]
114
#[reflect(Component, Default, Debug, PartialEq, Clone)]
115
pub struct PointerPress {
116
primary: bool,
117
secondary: bool,
118
middle: bool,
119
}
120
121
impl PointerPress {
122
/// Returns true if the primary pointer button is pressed.
123
#[inline]
124
pub fn is_primary_pressed(&self) -> bool {
125
self.primary
126
}
127
128
/// Returns true if the secondary pointer button is pressed.
129
#[inline]
130
pub fn is_secondary_pressed(&self) -> bool {
131
self.secondary
132
}
133
134
/// Returns true if the middle (tertiary) pointer button is pressed.
135
#[inline]
136
pub fn is_middle_pressed(&self) -> bool {
137
self.middle
138
}
139
140
/// Returns true if any pointer button is pressed.
141
#[inline]
142
pub fn is_any_pressed(&self) -> bool {
143
self.primary || self.middle || self.secondary
144
}
145
}
146
147
/// The stage of the pointer button press event
148
#[derive(Debug, Clone, Copy, PartialEq, Eq, Reflect)]
149
#[reflect(Clone, PartialEq)]
150
pub enum PressDirection {
151
/// The pointer button was just pressed
152
Pressed,
153
/// The pointer button was just released
154
Released,
155
}
156
157
/// The button that was just pressed or released
158
#[derive(Debug, Clone, Copy, PartialEq, Eq, Hash, Reflect)]
159
#[reflect(Clone, PartialEq)]
160
pub enum PointerButton {
161
/// The primary pointer button
162
Primary,
163
/// The secondary pointer button
164
Secondary,
165
/// The tertiary pointer button
166
Middle,
167
}
168
169
impl PointerButton {
170
/// Iterator over all buttons that a pointer can have.
171
pub fn iter() -> impl Iterator<Item = PointerButton> {
172
[Self::Primary, Self::Secondary, Self::Middle].into_iter()
173
}
174
}
175
176
/// Component that tracks a pointer's current [`Location`].
177
#[derive(Debug, Default, Clone, Component, Reflect, PartialEq)]
178
#[reflect(Component, Default, Debug, PartialEq, Clone)]
179
pub struct PointerLocation {
180
/// The [`Location`] of the pointer. Note that a location is both the target, and the position
181
/// on the target.
182
#[reflect(ignore, clone)]
183
pub location: Option<Location>,
184
}
185
186
impl PointerLocation {
187
///Returns a [`PointerLocation`] associated with the given location
188
pub fn new(location: Location) -> Self {
189
Self {
190
location: Some(location),
191
}
192
}
193
194
/// Returns `Some(&`[`Location`]`)` if the pointer is active, or `None` if the pointer is
195
/// inactive.
196
pub fn location(&self) -> Option<&Location> {
197
self.location.as_ref()
198
}
199
}
200
201
/// The location of a pointer, including the current [`NormalizedRenderTarget`], and the x/y
202
/// position of the pointer on this render target.
203
///
204
/// Note that:
205
/// - a pointer can move freely between render targets
206
/// - a pointer is not associated with a [`Camera`] because multiple cameras can target the same
207
/// render target. It is up to picking backends to associate a Pointer's `Location` with a
208
/// specific `Camera`, if any.
209
#[derive(Debug, Clone, Reflect, PartialEq)]
210
#[reflect(Debug, PartialEq, Clone)]
211
pub struct Location {
212
/// The [`NormalizedRenderTarget`] associated with the pointer, usually a window.
213
pub target: NormalizedRenderTarget,
214
/// The position of the pointer in the `target`.
215
pub position: Vec2,
216
}
217
218
impl Location {
219
/// Returns `true` if this pointer's [`Location`] is within the [`Camera`]'s viewport.
220
///
221
/// Note this returns `false` if the location and camera have different render targets.
222
#[inline]
223
pub fn is_in_viewport(
224
&self,
225
camera: &Camera,
226
primary_window: &Query<Entity, With<PrimaryWindow>>,
227
) -> bool {
228
if camera
229
.target
230
.normalize(Some(match primary_window.single() {
231
Ok(w) => w,
232
Err(_) => return false,
233
}))
234
.as_ref()
235
!= Some(&self.target)
236
{
237
return false;
238
}
239
240
camera
241
.logical_viewport_rect()
242
.is_some_and(|rect| rect.contains(self.position))
243
}
244
}
245
246
/// Event sent to drive a pointer.
247
#[derive(Debug, Clone, Copy, Reflect)]
248
#[reflect(Clone)]
249
pub enum PointerAction {
250
/// Causes the pointer to press a button.
251
Press(PointerButton),
252
/// Causes the pointer to release a button.
253
Release(PointerButton),
254
/// Move the pointer.
255
Move {
256
/// How much the pointer moved from the previous position.
257
delta: Vec2,
258
},
259
/// Scroll the pointer
260
Scroll {
261
/// The mouse scroll unit.
262
unit: MouseScrollUnit,
263
/// The horizontal scroll value.
264
x: f32,
265
/// The vertical scroll value.
266
y: f32,
267
},
268
/// Cancel the pointer. Often used for touch events.
269
Cancel,
270
}
271
272
/// An input event effecting a pointer.
273
#[derive(BufferedEvent, Debug, Clone, Reflect)]
274
#[reflect(Clone)]
275
pub struct PointerInput {
276
/// The id of the pointer.
277
pub pointer_id: PointerId,
278
/// The location of the pointer. For [`PointerAction::Move`], this is the location after the movement.
279
pub location: Location,
280
/// The action that the event describes.
281
pub action: PointerAction,
282
}
283
284
impl PointerInput {
285
/// Creates a new pointer input event.
286
///
287
/// Note that `location` refers to the position of the pointer *after* the event occurred.
288
pub fn new(pointer_id: PointerId, location: Location, action: PointerAction) -> PointerInput {
289
PointerInput {
290
pointer_id,
291
location,
292
action,
293
}
294
}
295
296
/// Returns true if the `target_button` of this pointer was just pressed.
297
#[inline]
298
pub fn button_just_pressed(&self, target_button: PointerButton) -> bool {
299
if let PointerAction::Press(button) = self.action {
300
button == target_button
301
} else {
302
false
303
}
304
}
305
306
/// Returns true if the `target_button` of this pointer was just released.
307
#[inline]
308
pub fn button_just_released(&self, target_button: PointerButton) -> bool {
309
if let PointerAction::Release(button) = self.action {
310
button == target_button
311
} else {
312
false
313
}
314
}
315
316
/// Updates pointer entities according to the input events.
317
pub fn receive(
318
mut events: EventReader<PointerInput>,
319
mut pointers: Query<(&PointerId, &mut PointerLocation, &mut PointerPress)>,
320
) {
321
for event in events.read() {
322
match event.action {
323
PointerAction::Press(button) => {
324
pointers
325
.iter_mut()
326
.for_each(|(pointer_id, _, mut pointer)| {
327
if *pointer_id == event.pointer_id {
328
match button {
329
PointerButton::Primary => pointer.primary = true,
330
PointerButton::Secondary => pointer.secondary = true,
331
PointerButton::Middle => pointer.middle = true,
332
}
333
}
334
});
335
}
336
PointerAction::Release(button) => {
337
pointers
338
.iter_mut()
339
.for_each(|(pointer_id, _, mut pointer)| {
340
if *pointer_id == event.pointer_id {
341
match button {
342
PointerButton::Primary => pointer.primary = false,
343
PointerButton::Secondary => pointer.secondary = false,
344
PointerButton::Middle => pointer.middle = false,
345
}
346
}
347
});
348
}
349
PointerAction::Move { .. } => {
350
pointers.iter_mut().for_each(|(id, mut pointer, _)| {
351
if *id == event.pointer_id {
352
pointer.location = Some(event.location.to_owned());
353
}
354
});
355
}
356
_ => {}
357
}
358
}
359
}
360
}
361
362