2022-03-01 19:33:56 +00:00
|
|
|
|
use bevy_ecs::event::EventReader;
|
Make `Resource` trait opt-in, requiring `#[derive(Resource)]` V2 (#5577)
*This PR description is an edited copy of #5007, written by @alice-i-cecile.*
# Objective
Follow-up to https://github.com/bevyengine/bevy/pull/2254. The `Resource` trait currently has a blanket implementation for all types that meet its bounds.
While ergonomic, this results in several drawbacks:
* it is possible to make confusing, silent mistakes such as inserting a function pointer (Foo) rather than a value (Foo::Bar) as a resource
* it is challenging to discover if a type is intended to be used as a resource
* we cannot later add customization options (see the [RFC](https://github.com/bevyengine/rfcs/blob/main/rfcs/27-derive-component.md) for the equivalent choice for Component).
* dependencies can use the same Rust type as a resource in invisibly conflicting ways
* raw Rust types used as resources cannot preserve privacy appropriately, as anyone able to access that type can read and write to internal values
* we cannot capture a definitive list of possible resources to display to users in an editor
## Notes to reviewers
* Review this commit-by-commit; there's effectively no back-tracking and there's a lot of churn in some of these commits.
*ira: My commits are not as well organized :')*
* I've relaxed the bound on Local to Send + Sync + 'static: I don't think these concerns apply there, so this can keep things simple. Storing e.g. a u32 in a Local is fine, because there's a variable name attached explaining what it does.
* I think this is a bad place for the Resource trait to live, but I've left it in place to make reviewing easier. IMO that's best tackled with https://github.com/bevyengine/bevy/issues/4981.
## Changelog
`Resource` is no longer automatically implemented for all matching types. Instead, use the new `#[derive(Resource)]` macro.
## Migration Guide
Add `#[derive(Resource)]` to all types you are using as a resource.
If you are using a third party type as a resource, wrap it in a tuple struct to bypass orphan rules. Consider deriving `Deref` and `DerefMut` to improve ergonomics.
`ClearColor` no longer implements `Component`. Using `ClearColor` as a component in 0.8 did nothing.
Use the `ClearColorConfig` in the `Camera3d` and `Camera2d` components instead.
Co-authored-by: Alice <alice.i.cecile@gmail.com>
Co-authored-by: Alice Cecile <alice.i.cecile@gmail.com>
Co-authored-by: devil-ira <justthecooldude@gmail.com>
Co-authored-by: Carter Anderson <mcanders1@gmail.com>
2022-08-08 21:36:35 +00:00
|
|
|
|
use bevy_ecs::system::{ResMut, Resource};
|
2020-10-18 19:24:01 +00:00
|
|
|
|
use bevy_math::Vec2;
|
2022-10-26 19:52:20 +00:00
|
|
|
|
use bevy_reflect::{FromReflect, Reflect};
|
2020-11-03 19:32:48 +00:00
|
|
|
|
use bevy_utils::HashMap;
|
|
|
|
|
|
2022-10-26 19:52:20 +00:00
|
|
|
|
#[cfg(feature = "serialize")]
|
|
|
|
|
use bevy_reflect::{ReflectDeserialize, ReflectSerialize};
|
|
|
|
|
|
2022-04-18 18:25:44 +00:00
|
|
|
|
/// A touch input event.
|
2020-11-03 19:32:48 +00:00
|
|
|
|
///
|
2022-04-18 18:25:44 +00:00
|
|
|
|
/// ## Logic
|
|
|
|
|
///
|
|
|
|
|
/// Every time the user touches the screen, a new [`TouchPhase::Started`] event with an unique
|
|
|
|
|
/// identifier for the finger is generated. When the finger is lifted, the [`TouchPhase::Ended`]
|
2020-11-03 19:32:48 +00:00
|
|
|
|
/// event is generated with the same finger id.
|
|
|
|
|
///
|
2022-04-18 18:25:44 +00:00
|
|
|
|
/// After a [`TouchPhase::Started`] event has been emitted, there may be zero or more [`TouchPhase::Moved`]
|
2020-11-03 19:32:48 +00:00
|
|
|
|
/// events when the finger is moved or the touch pressure changes.
|
|
|
|
|
///
|
2022-04-18 18:25:44 +00:00
|
|
|
|
/// The finger id may be reused by the system after an [`TouchPhase::Ended`] event. The user
|
|
|
|
|
/// should assume that a new [`TouchPhase::Started`] event received with the same id has nothing
|
2020-11-03 19:32:48 +00:00
|
|
|
|
/// to do with the old finger and is a new finger.
|
|
|
|
|
///
|
2022-04-18 18:25:44 +00:00
|
|
|
|
/// A [`TouchPhase::Cancelled`] event is emitted when the system has canceled tracking this
|
2020-11-03 19:32:48 +00:00
|
|
|
|
/// touch, such as when the window loses focus, or on iOS if the user moves the
|
|
|
|
|
/// device against their face.
|
2022-04-18 18:25:44 +00:00
|
|
|
|
///
|
|
|
|
|
/// ## Note
|
|
|
|
|
///
|
|
|
|
|
/// This event is the translated version of the `WindowEvent::Touch` from the `winit` crate.
|
|
|
|
|
/// It is available to the end user and can be used for game logic.
|
2022-10-26 19:52:20 +00:00
|
|
|
|
#[derive(Debug, Clone, Copy, PartialEq, Reflect, FromReflect)]
|
|
|
|
|
#[reflect(Debug, PartialEq)]
|
|
|
|
|
#[cfg_attr(
|
|
|
|
|
feature = "serialize",
|
|
|
|
|
derive(serde::Serialize, serde::Deserialize),
|
|
|
|
|
reflect(Serialize, Deserialize)
|
|
|
|
|
)]
|
2020-10-18 19:24:01 +00:00
|
|
|
|
pub struct TouchInput {
|
2022-04-18 18:25:44 +00:00
|
|
|
|
/// The phase of the touch input.
|
2020-10-18 19:24:01 +00:00
|
|
|
|
pub phase: TouchPhase,
|
2022-04-18 18:25:44 +00:00
|
|
|
|
/// The position of the finger on the touchscreen.
|
2020-10-18 19:24:01 +00:00
|
|
|
|
pub position: Vec2,
|
2022-04-18 18:25:44 +00:00
|
|
|
|
/// Describes how hard the screen was pressed.
|
2020-10-18 19:24:01 +00:00
|
|
|
|
///
|
2022-04-18 18:25:44 +00:00
|
|
|
|
/// May be [`None`] if the platform does not support pressure sensitivity.
|
|
|
|
|
/// This feature is only available on **iOS** 9.0+ and **Windows** 8+.
|
2020-11-03 19:32:48 +00:00
|
|
|
|
pub force: Option<ForceTouch>,
|
2022-04-18 18:25:44 +00:00
|
|
|
|
/// The unique identifier of the finger.
|
2020-10-18 19:24:01 +00:00
|
|
|
|
pub id: u64,
|
|
|
|
|
}
|
|
|
|
|
|
2022-04-18 18:25:44 +00:00
|
|
|
|
/// A force description of a [`Touch`](crate::touch::Touch) input.
|
2022-10-26 19:52:20 +00:00
|
|
|
|
#[derive(Debug, Clone, Copy, PartialEq, Reflect, FromReflect)]
|
|
|
|
|
#[reflect(Debug, PartialEq)]
|
|
|
|
|
#[cfg_attr(
|
|
|
|
|
feature = "serialize",
|
|
|
|
|
derive(serde::Serialize, serde::Deserialize),
|
|
|
|
|
reflect(Serialize, Deserialize)
|
|
|
|
|
)]
|
2020-11-03 19:32:48 +00:00
|
|
|
|
pub enum ForceTouch {
|
|
|
|
|
/// On iOS, the force is calibrated so that the same number corresponds to
|
|
|
|
|
/// roughly the same amount of pressure on the screen regardless of the
|
|
|
|
|
/// device.
|
|
|
|
|
Calibrated {
|
|
|
|
|
/// The force of the touch, where a value of 1.0 represents the force of
|
|
|
|
|
/// an average touch (predetermined by the system, not user-specific).
|
|
|
|
|
///
|
|
|
|
|
/// The force reported by Apple Pencil is measured along the axis of the
|
|
|
|
|
/// pencil. If you want a force perpendicular to the device, you need to
|
|
|
|
|
/// calculate this value using the `altitude_angle` value.
|
|
|
|
|
force: f64,
|
|
|
|
|
/// The maximum possible force for a touch.
|
|
|
|
|
///
|
|
|
|
|
/// The value of this field is sufficiently high to provide a wide
|
|
|
|
|
/// dynamic range for values of the `force` field.
|
|
|
|
|
max_possible_force: f64,
|
|
|
|
|
/// The altitude (in radians) of the stylus.
|
|
|
|
|
///
|
|
|
|
|
/// A value of 0 radians indicates that the stylus is parallel to the
|
|
|
|
|
/// surface. The value of this property is Pi/2 when the stylus is
|
|
|
|
|
/// perpendicular to the surface.
|
|
|
|
|
altitude_angle: Option<f64>,
|
|
|
|
|
},
|
|
|
|
|
/// If the platform reports the force as normalized, we have no way of
|
|
|
|
|
/// knowing how much pressure 1.0 corresponds to – we know it's the maximum
|
|
|
|
|
/// amount of force, but as to how much force, you might either have to
|
2022-04-18 18:25:44 +00:00
|
|
|
|
/// press really hard, or not hard at all, depending on the device.
|
2020-11-03 19:32:48 +00:00
|
|
|
|
Normalized(f64),
|
|
|
|
|
}
|
|
|
|
|
|
2022-04-18 18:25:44 +00:00
|
|
|
|
/// A phase of a [`TouchInput`](crate::touch::TouchInput).
|
|
|
|
|
///
|
|
|
|
|
/// ## Usage
|
|
|
|
|
///
|
|
|
|
|
/// It is used to describe the phase of the touch input that is currently active.
|
|
|
|
|
/// This includes a phase that indicates that a touch input has started or ended,
|
|
|
|
|
/// or that a finger has moved. There is also a cancelled phase that indicates that
|
|
|
|
|
/// the system cancelled the tracking of the finger.
|
2022-10-26 19:52:20 +00:00
|
|
|
|
#[derive(Debug, Hash, PartialEq, Eq, Clone, Copy, Reflect, FromReflect)]
|
|
|
|
|
#[reflect(Debug, Hash, PartialEq)]
|
|
|
|
|
#[cfg_attr(
|
|
|
|
|
feature = "serialize",
|
|
|
|
|
derive(serde::Serialize, serde::Deserialize),
|
|
|
|
|
reflect(Serialize, Deserialize)
|
|
|
|
|
)]
|
2020-10-18 19:24:01 +00:00
|
|
|
|
pub enum TouchPhase {
|
2022-04-18 18:25:44 +00:00
|
|
|
|
/// A finger started to touch the touchscreen.
|
2020-10-18 19:24:01 +00:00
|
|
|
|
Started,
|
2022-04-18 18:25:44 +00:00
|
|
|
|
/// A finger moved over the touchscreen.
|
2020-10-18 19:24:01 +00:00
|
|
|
|
Moved,
|
2022-04-18 18:25:44 +00:00
|
|
|
|
/// A finger stopped touching the touchscreen.
|
2020-10-18 19:24:01 +00:00
|
|
|
|
Ended,
|
2022-04-18 18:25:44 +00:00
|
|
|
|
/// The system cancelled the tracking of the finger.
|
|
|
|
|
///
|
|
|
|
|
/// This occurs when the window loses focus, or on iOS if the user moves the
|
|
|
|
|
/// device against their face.
|
2020-10-18 19:24:01 +00:00
|
|
|
|
Cancelled,
|
|
|
|
|
}
|
|
|
|
|
|
2022-04-18 18:25:44 +00:00
|
|
|
|
/// A touch input.
|
|
|
|
|
///
|
|
|
|
|
/// ## Usage
|
|
|
|
|
///
|
|
|
|
|
/// It is used to store the position and force of a touch input and also the `id` of the finger.
|
|
|
|
|
/// The data of the touch input comes from the [`TouchInput`] event and is being stored
|
|
|
|
|
/// inside of the [`Touches`] `bevy` resource.
|
2020-11-03 19:32:48 +00:00
|
|
|
|
#[derive(Debug, Clone, Copy)]
|
2020-10-18 19:24:01 +00:00
|
|
|
|
pub struct Touch {
|
2022-04-18 18:25:44 +00:00
|
|
|
|
/// The id of the touch input.
|
2020-12-01 01:14:08 +00:00
|
|
|
|
id: u64,
|
2022-04-18 18:25:44 +00:00
|
|
|
|
/// The starting position of the touch input.
|
2020-12-01 01:14:08 +00:00
|
|
|
|
start_position: Vec2,
|
2022-04-18 18:25:44 +00:00
|
|
|
|
/// The starting force of the touch input.
|
2020-12-01 01:14:08 +00:00
|
|
|
|
start_force: Option<ForceTouch>,
|
2022-04-18 18:25:44 +00:00
|
|
|
|
/// The previous position of the touch input.
|
2020-12-01 01:14:08 +00:00
|
|
|
|
previous_position: Vec2,
|
2022-04-18 18:25:44 +00:00
|
|
|
|
/// The previous force of the touch input.
|
2020-12-01 01:14:08 +00:00
|
|
|
|
previous_force: Option<ForceTouch>,
|
2022-04-18 18:25:44 +00:00
|
|
|
|
/// The current position of the touch input.
|
2020-12-01 01:14:08 +00:00
|
|
|
|
position: Vec2,
|
2022-04-18 18:25:44 +00:00
|
|
|
|
/// The current force of the touch input.
|
2020-12-01 01:14:08 +00:00
|
|
|
|
force: Option<ForceTouch>,
|
2020-10-18 19:24:01 +00:00
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
impl Touch {
|
2022-04-18 18:25:44 +00:00
|
|
|
|
/// The delta of the current `position` and the `previous_position`.
|
2020-10-18 19:24:01 +00:00
|
|
|
|
pub fn delta(&self) -> Vec2 {
|
|
|
|
|
self.position - self.previous_position
|
|
|
|
|
}
|
|
|
|
|
|
2022-04-18 18:25:44 +00:00
|
|
|
|
/// The distance of the `start_position` and the current `position`.
|
2020-10-18 19:24:01 +00:00
|
|
|
|
pub fn distance(&self) -> Vec2 {
|
|
|
|
|
self.position - self.start_position
|
|
|
|
|
}
|
2020-12-01 01:14:08 +00:00
|
|
|
|
|
2022-04-18 18:25:44 +00:00
|
|
|
|
/// Returns the `id` of the touch.
|
2020-12-01 01:14:08 +00:00
|
|
|
|
#[inline]
|
|
|
|
|
pub fn id(&self) -> u64 {
|
|
|
|
|
self.id
|
|
|
|
|
}
|
|
|
|
|
|
2022-04-18 18:25:44 +00:00
|
|
|
|
/// Returns the `start_position` of the touch.
|
2020-12-01 01:14:08 +00:00
|
|
|
|
#[inline]
|
|
|
|
|
pub fn start_position(&self) -> Vec2 {
|
|
|
|
|
self.start_position
|
|
|
|
|
}
|
|
|
|
|
|
2022-04-18 18:25:44 +00:00
|
|
|
|
/// Returns the `start_force` of the touch.
|
2020-12-01 01:14:08 +00:00
|
|
|
|
#[inline]
|
|
|
|
|
pub fn start_force(&self) -> Option<ForceTouch> {
|
|
|
|
|
self.start_force
|
|
|
|
|
}
|
|
|
|
|
|
2022-04-18 18:25:44 +00:00
|
|
|
|
/// Returns the `previous_position` of the touch.
|
2020-12-01 01:14:08 +00:00
|
|
|
|
#[inline]
|
|
|
|
|
pub fn previous_position(&self) -> Vec2 {
|
|
|
|
|
self.previous_position
|
|
|
|
|
}
|
|
|
|
|
|
2022-04-18 18:25:44 +00:00
|
|
|
|
/// Returns the `previous_force` of the touch.
|
|
|
|
|
#[inline]
|
|
|
|
|
pub fn previous_force(&self) -> Option<ForceTouch> {
|
|
|
|
|
self.previous_force
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
/// Returns the current `position` of the touch.
|
2020-12-01 01:14:08 +00:00
|
|
|
|
#[inline]
|
|
|
|
|
pub fn position(&self) -> Vec2 {
|
|
|
|
|
self.position
|
|
|
|
|
}
|
|
|
|
|
|
2022-04-18 18:25:44 +00:00
|
|
|
|
/// Returns the current `force` of the touch.
|
2020-12-01 01:14:08 +00:00
|
|
|
|
#[inline]
|
|
|
|
|
pub fn force(&self) -> Option<ForceTouch> {
|
|
|
|
|
self.force
|
|
|
|
|
}
|
2020-10-18 19:24:01 +00:00
|
|
|
|
}
|
|
|
|
|
|
2020-11-03 19:32:48 +00:00
|
|
|
|
impl From<&TouchInput> for Touch {
|
|
|
|
|
fn from(input: &TouchInput) -> Touch {
|
|
|
|
|
Touch {
|
|
|
|
|
id: input.id,
|
|
|
|
|
start_position: input.position,
|
|
|
|
|
start_force: input.force,
|
|
|
|
|
previous_position: input.position,
|
|
|
|
|
previous_force: input.force,
|
|
|
|
|
position: input.position,
|
|
|
|
|
force: input.force,
|
|
|
|
|
}
|
|
|
|
|
}
|
|
|
|
|
}
|
|
|
|
|
|
2022-04-18 18:25:44 +00:00
|
|
|
|
/// A collection of [`Touch`]es.
|
|
|
|
|
///
|
|
|
|
|
/// ## Usage
|
|
|
|
|
///
|
|
|
|
|
/// It is used to create a `bevy` resource that stores the data of the touches on a touchscreen
|
|
|
|
|
/// and can be accessed inside of a system.
|
|
|
|
|
///
|
|
|
|
|
/// ## Updating
|
|
|
|
|
///
|
|
|
|
|
/// The resource is updated inside of the [`touch_screen_input_system`](crate::touch::touch_screen_input_system).
|
Make `Resource` trait opt-in, requiring `#[derive(Resource)]` V2 (#5577)
*This PR description is an edited copy of #5007, written by @alice-i-cecile.*
# Objective
Follow-up to https://github.com/bevyengine/bevy/pull/2254. The `Resource` trait currently has a blanket implementation for all types that meet its bounds.
While ergonomic, this results in several drawbacks:
* it is possible to make confusing, silent mistakes such as inserting a function pointer (Foo) rather than a value (Foo::Bar) as a resource
* it is challenging to discover if a type is intended to be used as a resource
* we cannot later add customization options (see the [RFC](https://github.com/bevyengine/rfcs/blob/main/rfcs/27-derive-component.md) for the equivalent choice for Component).
* dependencies can use the same Rust type as a resource in invisibly conflicting ways
* raw Rust types used as resources cannot preserve privacy appropriately, as anyone able to access that type can read and write to internal values
* we cannot capture a definitive list of possible resources to display to users in an editor
## Notes to reviewers
* Review this commit-by-commit; there's effectively no back-tracking and there's a lot of churn in some of these commits.
*ira: My commits are not as well organized :')*
* I've relaxed the bound on Local to Send + Sync + 'static: I don't think these concerns apply there, so this can keep things simple. Storing e.g. a u32 in a Local is fine, because there's a variable name attached explaining what it does.
* I think this is a bad place for the Resource trait to live, but I've left it in place to make reviewing easier. IMO that's best tackled with https://github.com/bevyengine/bevy/issues/4981.
## Changelog
`Resource` is no longer automatically implemented for all matching types. Instead, use the new `#[derive(Resource)]` macro.
## Migration Guide
Add `#[derive(Resource)]` to all types you are using as a resource.
If you are using a third party type as a resource, wrap it in a tuple struct to bypass orphan rules. Consider deriving `Deref` and `DerefMut` to improve ergonomics.
`ClearColor` no longer implements `Component`. Using `ClearColor` as a component in 0.8 did nothing.
Use the `ClearColorConfig` in the `Camera3d` and `Camera2d` components instead.
Co-authored-by: Alice <alice.i.cecile@gmail.com>
Co-authored-by: Alice Cecile <alice.i.cecile@gmail.com>
Co-authored-by: devil-ira <justthecooldude@gmail.com>
Co-authored-by: Carter Anderson <mcanders1@gmail.com>
2022-08-08 21:36:35 +00:00
|
|
|
|
#[derive(Debug, Clone, Default, Resource)]
|
2020-10-18 19:24:01 +00:00
|
|
|
|
pub struct Touches {
|
2022-04-18 18:25:44 +00:00
|
|
|
|
/// A collection of every [`Touch`] that is currently being pressed.
|
2020-11-03 19:32:48 +00:00
|
|
|
|
pressed: HashMap<u64, Touch>,
|
2022-04-18 18:25:44 +00:00
|
|
|
|
/// A collection of every [`Touch`] that just got pressed.
|
2020-11-03 19:32:48 +00:00
|
|
|
|
just_pressed: HashMap<u64, Touch>,
|
2022-04-18 18:25:44 +00:00
|
|
|
|
/// A collection of every [`Touch`] that just got released.
|
2020-11-03 19:32:48 +00:00
|
|
|
|
just_released: HashMap<u64, Touch>,
|
2022-04-18 18:25:44 +00:00
|
|
|
|
/// A collection of every [`Touch`] that just got cancelled.
|
2020-11-03 19:32:48 +00:00
|
|
|
|
just_cancelled: HashMap<u64, Touch>,
|
2020-10-18 19:24:01 +00:00
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
impl Touches {
|
2022-04-18 18:25:44 +00:00
|
|
|
|
/// An iterator visiting every pressed [`Touch`] input in arbitrary order.
|
2020-10-18 19:24:01 +00:00
|
|
|
|
pub fn iter(&self) -> impl Iterator<Item = &Touch> + '_ {
|
2020-11-03 19:32:48 +00:00
|
|
|
|
self.pressed.values()
|
|
|
|
|
}
|
|
|
|
|
|
2022-04-18 18:25:44 +00:00
|
|
|
|
/// Returns the [`Touch`] input corresponding to the `id` if it is being pressed.
|
2020-11-03 19:32:48 +00:00
|
|
|
|
pub fn get_pressed(&self, id: u64) -> Option<&Touch> {
|
|
|
|
|
self.pressed.get(&id)
|
2020-10-18 19:24:01 +00:00
|
|
|
|
}
|
|
|
|
|
|
2022-06-20 20:32:19 +00:00
|
|
|
|
/// Checks if any touch input was just pressed.
|
|
|
|
|
pub fn any_just_pressed(&self) -> bool {
|
|
|
|
|
!self.just_pressed.is_empty()
|
|
|
|
|
}
|
|
|
|
|
|
2022-04-18 18:25:44 +00:00
|
|
|
|
/// Returns `true` if the input corresponding to the `id` has just been pressed.
|
2020-10-18 19:24:01 +00:00
|
|
|
|
pub fn just_pressed(&self, id: u64) -> bool {
|
2020-11-03 19:32:48 +00:00
|
|
|
|
self.just_pressed.contains_key(&id)
|
2020-10-18 19:24:01 +00:00
|
|
|
|
}
|
|
|
|
|
|
2022-04-18 18:25:44 +00:00
|
|
|
|
/// An iterator visiting every just pressed [`Touch`] input in arbitrary order.
|
2020-12-01 06:58:49 +00:00
|
|
|
|
pub fn iter_just_pressed(&self) -> impl Iterator<Item = &Touch> {
|
|
|
|
|
self.just_pressed.values()
|
2020-11-03 19:32:48 +00:00
|
|
|
|
}
|
|
|
|
|
|
2022-04-18 18:25:44 +00:00
|
|
|
|
/// Returns the [`Touch`] input corresponding to the `id` if it has just been released.
|
2020-11-03 19:32:48 +00:00
|
|
|
|
pub fn get_released(&self, id: u64) -> Option<&Touch> {
|
|
|
|
|
self.just_released.get(&id)
|
2020-10-18 19:24:01 +00:00
|
|
|
|
}
|
|
|
|
|
|
2022-06-20 20:32:19 +00:00
|
|
|
|
/// Checks if any touch input was just released.
|
|
|
|
|
pub fn any_just_released(&self) -> bool {
|
|
|
|
|
!self.just_released.is_empty()
|
|
|
|
|
}
|
|
|
|
|
|
2022-04-18 18:25:44 +00:00
|
|
|
|
/// Returns `true` if the input corresponding to the `id` has just been released.
|
2020-10-18 19:24:01 +00:00
|
|
|
|
pub fn just_released(&self, id: u64) -> bool {
|
2020-11-03 19:32:48 +00:00
|
|
|
|
self.just_released.contains_key(&id)
|
2020-10-18 19:24:01 +00:00
|
|
|
|
}
|
|
|
|
|
|
2022-04-18 18:25:44 +00:00
|
|
|
|
/// An iterator visiting every just released [`Touch`] input in arbitrary order.
|
2020-12-01 06:58:49 +00:00
|
|
|
|
pub fn iter_just_released(&self) -> impl Iterator<Item = &Touch> {
|
|
|
|
|
self.just_released.values()
|
2020-10-18 19:24:01 +00:00
|
|
|
|
}
|
|
|
|
|
|
2022-06-20 20:32:19 +00:00
|
|
|
|
/// Checks if any touch input was just cancelled.
|
|
|
|
|
pub fn any_just_cancelled(&self) -> bool {
|
|
|
|
|
!self.just_cancelled.is_empty()
|
|
|
|
|
}
|
|
|
|
|
|
2022-04-18 18:25:44 +00:00
|
|
|
|
/// Returns `true` if the input corresponding to the `id` has just been cancelled.
|
2020-10-18 19:24:01 +00:00
|
|
|
|
pub fn just_cancelled(&self, id: u64) -> bool {
|
2020-11-03 19:32:48 +00:00
|
|
|
|
self.just_cancelled.contains_key(&id)
|
2020-10-18 19:24:01 +00:00
|
|
|
|
}
|
|
|
|
|
|
2022-04-18 18:25:44 +00:00
|
|
|
|
/// An iterator visiting every just cancelled [`Touch`] input in arbitrary order.
|
2020-12-01 06:58:49 +00:00
|
|
|
|
pub fn iter_just_cancelled(&self) -> impl Iterator<Item = &Touch> {
|
|
|
|
|
self.just_cancelled.values()
|
2020-10-18 19:24:01 +00:00
|
|
|
|
}
|
|
|
|
|
|
2022-06-20 20:32:19 +00:00
|
|
|
|
/// Retrieves the position of the first currently pressed touch, if any
|
|
|
|
|
pub fn first_pressed_position(&self) -> Option<Vec2> {
|
|
|
|
|
self.pressed.values().next().map(|t| t.position)
|
|
|
|
|
}
|
|
|
|
|
|
2022-04-18 18:25:44 +00:00
|
|
|
|
/// Processes a [`TouchInput`] event by updating the `pressed`, `just_pressed`,
|
|
|
|
|
/// `just_released`, and `just_cancelled` collections.
|
2020-12-01 06:58:49 +00:00
|
|
|
|
fn process_touch_event(&mut self, event: &TouchInput) {
|
2020-10-18 19:24:01 +00:00
|
|
|
|
match event.phase {
|
|
|
|
|
TouchPhase::Started => {
|
2020-12-01 06:58:49 +00:00
|
|
|
|
self.pressed.insert(event.id, event.into());
|
|
|
|
|
self.just_pressed.insert(event.id, event.into());
|
2020-10-18 19:24:01 +00:00
|
|
|
|
}
|
|
|
|
|
TouchPhase::Moved => {
|
2021-03-10 00:44:45 +00:00
|
|
|
|
if let Some(mut new_touch) = self.pressed.get(&event.id).cloned() {
|
|
|
|
|
new_touch.previous_position = new_touch.position;
|
|
|
|
|
new_touch.previous_force = new_touch.force;
|
|
|
|
|
new_touch.position = event.position;
|
|
|
|
|
new_touch.force = event.force;
|
|
|
|
|
self.pressed.insert(event.id, new_touch);
|
|
|
|
|
}
|
2020-10-18 19:24:01 +00:00
|
|
|
|
}
|
|
|
|
|
TouchPhase::Ended => {
|
2022-10-10 17:43:10 +00:00
|
|
|
|
// if touch `just_released`, add related event to it
|
|
|
|
|
// the event position info is inside `pressed`, so use it unless not found
|
|
|
|
|
if let Some((_, v)) = self.pressed.remove_entry(&event.id) {
|
|
|
|
|
self.just_released.insert(event.id, v);
|
|
|
|
|
} else {
|
|
|
|
|
self.just_released.insert(event.id, event.into());
|
|
|
|
|
}
|
2020-10-18 19:24:01 +00:00
|
|
|
|
}
|
|
|
|
|
TouchPhase::Cancelled => {
|
2022-10-10 17:43:10 +00:00
|
|
|
|
// if touch `just_cancelled`, add related event to it
|
|
|
|
|
// the event position info is inside `pressed`, so use it unless not found
|
|
|
|
|
if let Some((_, v)) = self.pressed.remove_entry(&event.id) {
|
|
|
|
|
self.just_cancelled.insert(event.id, v);
|
|
|
|
|
} else {
|
|
|
|
|
self.just_cancelled.insert(event.id, event.into());
|
|
|
|
|
}
|
2020-10-18 19:24:01 +00:00
|
|
|
|
}
|
|
|
|
|
};
|
|
|
|
|
}
|
2020-12-01 06:58:49 +00:00
|
|
|
|
|
2022-04-18 18:25:44 +00:00
|
|
|
|
/// Clears the `just_pressed`, `just_released`, and `just_cancelled` collections.
|
|
|
|
|
///
|
|
|
|
|
/// This is not clearing the `pressed` collection, because it could incorrectly mark
|
2022-07-21 20:46:54 +00:00
|
|
|
|
/// a touch input as not pressed even though it is pressed. This could happen if the
|
2022-04-18 18:25:44 +00:00
|
|
|
|
/// touch input is not moving for a single frame and would therefore be marked as
|
|
|
|
|
/// not pressed, because this function is called on every single frame no matter
|
|
|
|
|
/// if there was an event or not.
|
2020-12-01 06:58:49 +00:00
|
|
|
|
fn update(&mut self) {
|
|
|
|
|
self.just_pressed.clear();
|
|
|
|
|
self.just_released.clear();
|
|
|
|
|
self.just_cancelled.clear();
|
|
|
|
|
}
|
|
|
|
|
}
|
|
|
|
|
|
2022-04-18 18:25:44 +00:00
|
|
|
|
/// Updates the [`Touches`] resource with the latest [`TouchInput`] events.
|
|
|
|
|
///
|
|
|
|
|
/// ## Differences
|
|
|
|
|
///
|
|
|
|
|
/// The main difference between the [`TouchInput`] event and the [`Touches`] resource is that
|
|
|
|
|
/// the latter has convenient functions like [`Touches::just_pressed`] and [`Touches::just_released`].
|
2020-12-01 06:58:49 +00:00
|
|
|
|
pub fn touch_screen_input_system(
|
|
|
|
|
mut touch_state: ResMut<Touches>,
|
2021-01-19 06:23:30 +00:00
|
|
|
|
mut touch_input_events: EventReader<TouchInput>,
|
2020-12-01 06:58:49 +00:00
|
|
|
|
) {
|
|
|
|
|
touch_state.update();
|
|
|
|
|
|
2021-01-19 06:23:30 +00:00
|
|
|
|
for event in touch_input_events.iter() {
|
2020-12-01 06:58:49 +00:00
|
|
|
|
touch_state.process_touch_event(event);
|
|
|
|
|
}
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
#[cfg(test)]
|
|
|
|
|
mod test {
|
|
|
|
|
|
|
|
|
|
#[test]
|
|
|
|
|
fn touch_update() {
|
|
|
|
|
use crate::{touch::Touch, Touches};
|
|
|
|
|
use bevy_math::Vec2;
|
|
|
|
|
|
|
|
|
|
let mut touches = Touches::default();
|
|
|
|
|
|
|
|
|
|
let touch_event = Touch {
|
|
|
|
|
id: 4,
|
2022-07-03 19:55:33 +00:00
|
|
|
|
start_position: Vec2::ZERO,
|
2020-12-01 06:58:49 +00:00
|
|
|
|
start_force: None,
|
2022-07-03 19:55:33 +00:00
|
|
|
|
previous_position: Vec2::ZERO,
|
2020-12-01 06:58:49 +00:00
|
|
|
|
previous_force: None,
|
2022-07-03 19:55:33 +00:00
|
|
|
|
position: Vec2::ZERO,
|
2020-12-01 06:58:49 +00:00
|
|
|
|
force: None,
|
|
|
|
|
};
|
|
|
|
|
|
|
|
|
|
// Add a touch to `just_pressed`, 'just_released', and 'just cancelled'
|
|
|
|
|
|
|
|
|
|
touches.just_pressed.insert(4, touch_event);
|
|
|
|
|
touches.just_released.insert(4, touch_event);
|
|
|
|
|
touches.just_cancelled.insert(4, touch_event);
|
|
|
|
|
|
|
|
|
|
touches.update();
|
|
|
|
|
|
|
|
|
|
// Verify that all the `just_x` maps are cleared
|
|
|
|
|
assert!(touches.just_pressed.is_empty());
|
|
|
|
|
assert!(touches.just_released.is_empty());
|
|
|
|
|
assert!(touches.just_cancelled.is_empty());
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
#[test]
|
|
|
|
|
fn touch_process() {
|
|
|
|
|
use crate::{touch::TouchPhase, TouchInput, Touches};
|
|
|
|
|
use bevy_math::Vec2;
|
|
|
|
|
|
|
|
|
|
let mut touches = Touches::default();
|
|
|
|
|
|
|
|
|
|
// Test adding a `TouchPhase::Started`
|
|
|
|
|
|
|
|
|
|
let touch_event = TouchInput {
|
|
|
|
|
phase: TouchPhase::Started,
|
2022-07-03 19:55:33 +00:00
|
|
|
|
position: Vec2::splat(4.0),
|
2020-12-01 06:58:49 +00:00
|
|
|
|
force: None,
|
|
|
|
|
id: 4,
|
|
|
|
|
};
|
|
|
|
|
|
|
|
|
|
touches.update();
|
|
|
|
|
touches.process_touch_event(&touch_event);
|
|
|
|
|
|
|
|
|
|
assert!(touches.pressed.get(&touch_event.id).is_some());
|
|
|
|
|
assert!(touches.just_pressed.get(&touch_event.id).is_some());
|
|
|
|
|
|
|
|
|
|
// Test adding a `TouchPhase::Moved`
|
|
|
|
|
|
|
|
|
|
let moved_touch_event = TouchInput {
|
|
|
|
|
phase: TouchPhase::Moved,
|
2022-07-03 19:55:33 +00:00
|
|
|
|
position: Vec2::splat(5.0),
|
2020-12-01 06:58:49 +00:00
|
|
|
|
force: None,
|
|
|
|
|
id: touch_event.id,
|
|
|
|
|
};
|
|
|
|
|
|
|
|
|
|
touches.update();
|
|
|
|
|
touches.process_touch_event(&moved_touch_event);
|
|
|
|
|
|
|
|
|
|
assert_eq!(
|
|
|
|
|
touches
|
|
|
|
|
.pressed
|
|
|
|
|
.get(&moved_touch_event.id)
|
|
|
|
|
.expect("Missing from pressed after move.")
|
|
|
|
|
.previous_position,
|
|
|
|
|
touch_event.position
|
|
|
|
|
);
|
|
|
|
|
|
|
|
|
|
// Test cancelling an event
|
|
|
|
|
|
|
|
|
|
let cancel_touch_event = TouchInput {
|
|
|
|
|
phase: TouchPhase::Cancelled,
|
2022-07-03 19:55:33 +00:00
|
|
|
|
position: Vec2::ONE,
|
2020-12-01 06:58:49 +00:00
|
|
|
|
force: None,
|
|
|
|
|
id: touch_event.id,
|
|
|
|
|
};
|
|
|
|
|
|
|
|
|
|
touches.update();
|
|
|
|
|
touches.process_touch_event(&cancel_touch_event);
|
|
|
|
|
|
2022-10-10 17:43:10 +00:00
|
|
|
|
assert!(touches.just_cancelled.get(&touch_event.id).is_some());
|
|
|
|
|
assert!(touches.pressed.get(&touch_event.id).is_none());
|
2020-12-01 06:58:49 +00:00
|
|
|
|
|
|
|
|
|
// Test ending an event
|
|
|
|
|
|
|
|
|
|
let end_touch_event = TouchInput {
|
|
|
|
|
phase: TouchPhase::Ended,
|
2022-07-03 19:55:33 +00:00
|
|
|
|
position: Vec2::splat(4.0),
|
2020-12-01 06:58:49 +00:00
|
|
|
|
force: None,
|
2022-10-10 17:43:10 +00:00
|
|
|
|
id: touch_event.id,
|
2020-12-01 06:58:49 +00:00
|
|
|
|
};
|
|
|
|
|
|
|
|
|
|
touches.update();
|
|
|
|
|
touches.process_touch_event(&touch_event);
|
2022-10-10 17:43:10 +00:00
|
|
|
|
touches.process_touch_event(&moved_touch_event);
|
2020-12-01 06:58:49 +00:00
|
|
|
|
touches.process_touch_event(&end_touch_event);
|
|
|
|
|
|
|
|
|
|
assert!(touches.just_released.get(&touch_event.id).is_some());
|
|
|
|
|
assert!(touches.pressed.get(&touch_event.id).is_none());
|
2022-10-10 17:43:10 +00:00
|
|
|
|
let touch = touches.just_released.get(&touch_event.id).unwrap();
|
|
|
|
|
// Make sure the position is updated from TouchPhase::Moved and TouchPhase::Ended
|
|
|
|
|
assert!(touch.previous_position != touch.position);
|
2020-12-01 06:58:49 +00:00
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
#[test]
|
|
|
|
|
fn touch_pressed() {
|
|
|
|
|
use crate::{touch::TouchPhase, TouchInput, Touches};
|
|
|
|
|
use bevy_math::Vec2;
|
|
|
|
|
|
|
|
|
|
let mut touches = Touches::default();
|
|
|
|
|
|
|
|
|
|
let touch_event = TouchInput {
|
|
|
|
|
phase: TouchPhase::Started,
|
2022-07-03 19:55:33 +00:00
|
|
|
|
position: Vec2::splat(4.0),
|
2020-12-01 06:58:49 +00:00
|
|
|
|
force: None,
|
|
|
|
|
id: 4,
|
|
|
|
|
};
|
|
|
|
|
|
|
|
|
|
// Register the touch and test that it was registered correctly
|
|
|
|
|
touches.process_touch_event(&touch_event);
|
|
|
|
|
|
|
|
|
|
assert!(touches.get_pressed(touch_event.id).is_some());
|
|
|
|
|
assert!(touches.just_pressed(touch_event.id));
|
|
|
|
|
assert_eq!(touches.iter().count(), 1);
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
#[test]
|
|
|
|
|
fn touch_released() {
|
|
|
|
|
use crate::{touch::TouchPhase, TouchInput, Touches};
|
|
|
|
|
use bevy_math::Vec2;
|
|
|
|
|
|
|
|
|
|
let mut touches = Touches::default();
|
|
|
|
|
|
|
|
|
|
let touch_event = TouchInput {
|
|
|
|
|
phase: TouchPhase::Ended,
|
2022-07-03 19:55:33 +00:00
|
|
|
|
position: Vec2::splat(4.0),
|
2020-12-01 06:58:49 +00:00
|
|
|
|
force: None,
|
|
|
|
|
id: 4,
|
|
|
|
|
};
|
|
|
|
|
|
|
|
|
|
// Register the touch and test that it was registered correctly
|
|
|
|
|
touches.process_touch_event(&touch_event);
|
|
|
|
|
|
|
|
|
|
assert!(touches.get_released(touch_event.id).is_some());
|
|
|
|
|
assert!(touches.just_released(touch_event.id));
|
|
|
|
|
assert_eq!(touches.iter_just_released().count(), 1);
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
#[test]
|
|
|
|
|
fn touch_cancelled() {
|
|
|
|
|
use crate::{touch::TouchPhase, TouchInput, Touches};
|
|
|
|
|
use bevy_math::Vec2;
|
|
|
|
|
|
|
|
|
|
let mut touches = Touches::default();
|
|
|
|
|
|
|
|
|
|
let touch_event = TouchInput {
|
|
|
|
|
phase: TouchPhase::Cancelled,
|
2022-07-03 19:55:33 +00:00
|
|
|
|
position: Vec2::splat(4.0),
|
2020-12-01 06:58:49 +00:00
|
|
|
|
force: None,
|
|
|
|
|
id: 4,
|
|
|
|
|
};
|
|
|
|
|
|
|
|
|
|
// Register the touch and test that it was registered correctly
|
|
|
|
|
touches.process_touch_event(&touch_event);
|
|
|
|
|
|
|
|
|
|
assert!(touches.just_cancelled(touch_event.id));
|
|
|
|
|
assert_eq!(touches.iter_just_cancelled().count(), 1);
|
|
|
|
|
}
|
2020-10-18 19:24:01 +00:00
|
|
|
|
}
|