Skip to content

Commit

Permalink
Derive Reflect + FromReflect for input types (bevyengine#6232)
Browse files Browse the repository at this point in the history
# Objective

Adds support for reflecting many more of the input types. This allows those types to be used via scripting, `bevy-inspector-egui`, etc. These types are registered by the `InputPlugin` so that they're automatically available to anyone who wants to use them 

Closes bevyengine#6223 

## Solution

Many types now have `#[derive(Reflect, FromReflect)]` added to them in `bevy_input`. Additionally, `#[reflect(traits...)]` has been added for applicable traits to the types.

This PR does not add reflection support for types which have private fields. Notably, `Touch` and `Touches` don't implement `Reflect`/`FromReflect`.

This adds the "glam" feature to the `bevy_reflect` dependency for package `bevy_input`. Since `bevy_input` transitively depends on `glam` already, all this brings in are the reflection `impl`s.

## Migration Guide

- `Input<T>` now implements `Reflect` via `#[reflect]` instead of `#[reflect_value]`. This means it now exposes its private fields via the `Reflect` trait rather than being treated as a value type. For code that relies on the `Input<T>` struct being treated as a value type by reflection, it is still possible to wrap the `Input<T>` type with a wrapper struct and apply `#[reflect_value]` to it.
  - As a reminder, private fields exposed via reflection are not subject to any stability guarantees.
---

## Changelog

Added
- Implemented `Reflect` + `FromReflect` for many input-related types. These types are automatically registered when adding the `InputPlugin`.
  • Loading branch information
TehPers authored and james7132 committed Oct 28, 2022
1 parent 36da915 commit 4cefe57
Show file tree
Hide file tree
Showing 7 changed files with 220 additions and 57 deletions.
2 changes: 1 addition & 1 deletion crates/bevy_input/Cargo.toml
Expand Up @@ -18,7 +18,7 @@ bevy_app = { path = "../bevy_app", version = "0.9.0-dev" }
bevy_ecs = { path = "../bevy_ecs", version = "0.9.0-dev" }
bevy_math = { path = "../bevy_math", version = "0.9.0-dev" }
bevy_utils = { path = "../bevy_utils", version = "0.9.0-dev" }
bevy_reflect = { path = "../bevy_reflect", version = "0.9.0-dev" }
bevy_reflect = { path = "../bevy_reflect", version = "0.9.0-dev", features = ["glam"] }

# other
serde = { version = "1", features = ["derive"], optional = true }
Expand Down
97 changes: 75 additions & 22 deletions crates/bevy_input/src/gamepad.rs
@@ -1,6 +1,7 @@
use crate::{Axis, Input};
use bevy_ecs::event::{EventReader, EventWriter};
use bevy_ecs::system::{Res, ResMut, Resource};
use bevy_reflect::{std_traits::ReflectDefault, FromReflect, Reflect};
use bevy_utils::{tracing::info, HashMap};
use thiserror::Error;

Expand Down Expand Up @@ -53,6 +54,9 @@ pub enum ButtonSettingsError {
},
}

#[cfg(feature = "serialize")]
use bevy_reflect::{ReflectDeserialize, ReflectSerialize};

/// A gamepad with an associated `ID`.
///
/// ## Usage
Expand All @@ -64,8 +68,13 @@ pub enum ButtonSettingsError {
/// ## Note
///
/// The `ID` of a gamepad is fixed until the gamepad disconnects or the app is restarted.
#[derive(Debug, Copy, Clone, PartialEq, Eq, Hash)]
#[cfg_attr(feature = "serialize", derive(serde::Serialize, serde::Deserialize))]
#[derive(Debug, Copy, Clone, PartialEq, Eq, Hash, Reflect, FromReflect)]
#[reflect(Debug, Hash, PartialEq)]
#[cfg_attr(
feature = "serialize",
derive(serde::Serialize, serde::Deserialize),
reflect(Serialize, Deserialize)
)]
pub struct Gamepad {
/// The `ID` of the gamepad.
pub id: usize,
Expand All @@ -79,8 +88,13 @@ impl Gamepad {
}

/// Metadata associated with a `Gamepad`.
#[derive(Debug, Clone, PartialEq, Eq)]
#[cfg_attr(feature = "serialize", derive(serde::Serialize, serde::Deserialize))]
#[derive(Debug, Clone, PartialEq, Eq, Reflect, FromReflect)]
#[reflect(Debug, PartialEq)]
#[cfg_attr(
feature = "serialize",
derive(serde::Serialize, serde::Deserialize),
reflect(Serialize, Deserialize)
)]
pub struct GamepadInfo {
pub name: String,
}
Expand Down Expand Up @@ -129,8 +143,13 @@ impl Gamepads {
}

/// The data contained in a [`GamepadEvent`] or [`GamepadEventRaw`].
#[derive(Debug, Clone, PartialEq)]
#[cfg_attr(feature = "serialize", derive(serde::Serialize, serde::Deserialize))]
#[derive(Debug, Clone, PartialEq, Reflect, FromReflect)]
#[reflect(Debug, PartialEq)]
#[cfg_attr(
feature = "serialize",
derive(serde::Serialize, serde::Deserialize),
reflect(Serialize, Deserialize)
)]
pub enum GamepadEventType {
/// A [`Gamepad`] has been connected.
Connected(GamepadInfo),
Expand Down Expand Up @@ -162,8 +181,13 @@ pub enum GamepadEventType {
/// [`Axis<GamepadAxis>`], and [`Axis<GamepadButton>`] resources won't be updated correctly.
///
/// An example for gamepad input mocking can be seen in the documentation of the [`GamepadEventRaw`].
#[derive(Debug, Clone, PartialEq)]
#[cfg_attr(feature = "serialize", derive(serde::Serialize, serde::Deserialize))]
#[derive(Debug, Clone, PartialEq, Reflect, FromReflect)]
#[reflect(Debug, PartialEq)]
#[cfg_attr(
feature = "serialize",
derive(serde::Serialize, serde::Deserialize),
reflect(Serialize, Deserialize)
)]
pub struct GamepadEvent {
/// The gamepad this event corresponds to.
pub gamepad: Gamepad,
Expand Down Expand Up @@ -266,8 +290,13 @@ impl GamepadEvent {
/// #
/// # bevy_ecs::system::assert_is_system(change_resource_on_gamepad_button_press);
/// ```
#[derive(Debug, Clone, PartialEq)]
#[cfg_attr(feature = "serialize", derive(serde::Serialize, serde::Deserialize))]
#[derive(Debug, Clone, PartialEq, Reflect, FromReflect)]
#[reflect(Debug, PartialEq)]
#[cfg_attr(
feature = "serialize",
derive(serde::Serialize, serde::Deserialize),
reflect(Serialize, Deserialize)
)]
pub struct GamepadEventRaw {
/// The gamepad this event corresponds to.
pub gamepad: Gamepad,
Expand All @@ -293,8 +322,13 @@ impl GamepadEventRaw {
/// [`GamepadEventType::ButtonChanged`]. It is also used in the [`GamepadButton`]
/// which in turn is used to create the [`Input<GamepadButton>`] or
/// [`Axis<GamepadButton>`] `bevy` resources.
#[derive(Debug, Copy, Clone, PartialEq, Eq, Hash)]
#[cfg_attr(feature = "serialize", derive(serde::Serialize, serde::Deserialize))]
#[derive(Debug, Copy, Clone, PartialEq, Eq, Hash, Reflect, FromReflect)]
#[reflect(Debug, Hash, PartialEq)]
#[cfg_attr(
feature = "serialize",
derive(serde::Serialize, serde::Deserialize),
reflect(Serialize, Deserialize)
)]
pub enum GamepadButtonType {
/// The bottom action button of the action pad (i.e. PS: Cross, Xbox: A).
South,
Expand Down Expand Up @@ -353,8 +387,13 @@ pub enum GamepadButtonType {
/// ## Updating
///
/// The resources are updated inside of the [`gamepad_event_system`].
#[derive(Debug, Copy, Clone, PartialEq, Eq, Hash)]
#[cfg_attr(feature = "serialize", derive(serde::Serialize, serde::Deserialize))]
#[derive(Debug, Copy, Clone, PartialEq, Eq, Hash, Reflect, FromReflect)]
#[reflect(Debug, Hash, PartialEq)]
#[cfg_attr(
feature = "serialize",
derive(serde::Serialize, serde::Deserialize),
reflect(Serialize, Deserialize)
)]
pub struct GamepadButton {
/// The gamepad on which the button is located on.
pub gamepad: Gamepad,
Expand Down Expand Up @@ -390,8 +429,13 @@ impl GamepadButton {
/// This is used to determine which axis has changed its value when receiving a
/// [`GamepadEventType::AxisChanged`]. It is also used in the [`GamepadAxis`]
/// which in turn is used to create the [`Axis<GamepadAxis>`] `bevy` resource.
#[derive(Debug, Copy, Clone, PartialEq, Eq, Hash)]
#[cfg_attr(feature = "serialize", derive(serde::Serialize, serde::Deserialize))]
#[derive(Debug, Copy, Clone, PartialEq, Eq, Hash, Reflect, FromReflect)]
#[reflect(Debug, Hash, PartialEq)]
#[cfg_attr(
feature = "serialize",
derive(serde::Serialize, serde::Deserialize),
reflect(Serialize, Deserialize)
)]
pub enum GamepadAxisType {
/// The horizontal value of the left stick.
LeftStickX,
Expand Down Expand Up @@ -421,8 +465,13 @@ pub enum GamepadAxisType {
/// ## Updating
///
/// The resource is updated inside of the [`gamepad_event_system`].
#[derive(Debug, Copy, Clone, PartialEq, Eq, Hash)]
#[cfg_attr(feature = "serialize", derive(serde::Serialize, serde::Deserialize))]
#[derive(Debug, Copy, Clone, PartialEq, Eq, Hash, Reflect, FromReflect)]
#[reflect(Debug, Hash, PartialEq)]
#[cfg_attr(
feature = "serialize",
derive(serde::Serialize, serde::Deserialize),
reflect(Serialize, Deserialize)
)]
pub struct GamepadAxis {
/// The gamepad on which the axis is located on.
pub gamepad: Gamepad,
Expand Down Expand Up @@ -460,7 +509,8 @@ impl GamepadAxis {
///
/// The [`GamepadSettings`] are used inside of the [`gamepad_event_system`], but are never written to
/// inside of `bevy`. To modify these settings, mutate the corresponding resource.
#[derive(Resource, Default, Debug)]
#[derive(Resource, Default, Debug, Reflect, FromReflect)]
#[reflect(Debug, Default)]
pub struct GamepadSettings {
/// The default button settings.
pub default_button_settings: ButtonSettings,
Expand Down Expand Up @@ -542,7 +592,8 @@ impl GamepadSettings {
/// value is surpassed and released if the `release_threshold` value is undercut.
///
/// Allowed values: `0.0 <= ``release_threshold`` <= ``press_threshold`` <= 1.0`
#[derive(Debug, Clone)]
#[derive(Debug, Clone, Reflect, FromReflect)]
#[reflect(Debug, Default)]
pub struct ButtonSettings {
press_threshold: f32,
release_threshold: f32,
Expand Down Expand Up @@ -701,7 +752,8 @@ impl ButtonSettings {
/// Otherwise, values will not be rounded.
///
/// The valid range is `[-1.0, 1.0]`.
#[derive(Debug, Clone)]
#[derive(Debug, Clone, Reflect, FromReflect)]
#[reflect(Debug, Default)]
pub struct AxisSettings {
/// Values that are higher than `livezone_upperbound` will be rounded up to -1.0.
livezone_upperbound: f32,
Expand Down Expand Up @@ -1015,7 +1067,8 @@ impl AxisSettings {
/// ## Updating
///
/// The current value of a button is received through the [`GamepadEvent`]s or [`GamepadEventRaw`]s.
#[derive(Debug, Clone)]
#[derive(Debug, Clone, Reflect, FromReflect)]
#[reflect(Debug, Default)]
pub struct ButtonAxisSettings {
/// The high value at which to apply rounding.
pub high: f32,
Expand Down
4 changes: 2 additions & 2 deletions crates/bevy_input/src/input.rs
@@ -1,5 +1,5 @@
use bevy_ecs::system::Resource;
use bevy_reflect::Reflect;
use bevy_reflect::{std_traits::ReflectDefault, Reflect};
use bevy_utils::HashSet;
use std::hash::Hash;

Expand Down Expand Up @@ -35,7 +35,7 @@ use bevy_ecs::schedule::State;
/// * Call the [`Input::release`] method for each release event.
/// * Call the [`Input::clear`] method at each frame start, before processing events.
#[derive(Debug, Clone, Resource, Reflect)]
#[reflect_value]
#[reflect(Default)]
pub struct Input<T: Copy + Eq + Hash + Send + Sync + 'static> {
/// A collection of every button that is currently being pressed.
pressed: HashSet<T>,
Expand Down
32 changes: 24 additions & 8 deletions crates/bevy_input/src/keyboard.rs
Expand Up @@ -2,6 +2,9 @@ use crate::{ButtonState, Input};
use bevy_ecs::{event::EventReader, system::ResMut};
use bevy_reflect::{FromReflect, Reflect};

#[cfg(feature = "serialize")]
use bevy_reflect::{ReflectDeserialize, ReflectSerialize};

/// A keyboard input event.
///
/// This event is the translated version of the `WindowEvent::KeyboardInput` from the `winit` crate.
Expand All @@ -11,8 +14,13 @@ use bevy_reflect::{FromReflect, Reflect};
///
/// The event is consumed inside of the [`keyboard_input_system`](crate::keyboard::keyboard_input_system)
/// to update the [`Input<KeyCode>`](crate::Input<KeyCode>) resource.
#[derive(Debug, Clone, Copy, PartialEq, Eq)]
#[cfg_attr(feature = "serialize", derive(serde::Serialize, serde::Deserialize))]
#[derive(Debug, Clone, Copy, PartialEq, Eq, Reflect, FromReflect)]
#[reflect(Debug, PartialEq)]
#[cfg_attr(
feature = "serialize",
derive(serde::Serialize, serde::Deserialize),
reflect(Serialize, Deserialize)
)]
pub struct KeyboardInput {
/// The scan code of the key.
pub scan_code: u32,
Expand Down Expand Up @@ -62,9 +70,13 @@ pub fn keyboard_input_system(
/// ## Updating
///
/// The resource is updated inside of the [`keyboard_input_system`](crate::keyboard::keyboard_input_system).
#[derive(Reflect, FromReflect, Debug, Hash, Ord, PartialOrd, PartialEq, Eq, Clone, Copy)]
#[cfg_attr(feature = "serialize", derive(serde::Serialize, serde::Deserialize))]
#[reflect(Hash, PartialEq)]
#[derive(Debug, Hash, Ord, PartialOrd, PartialEq, Eq, Clone, Copy, Reflect, FromReflect)]
#[reflect(Debug, Hash, PartialEq)]
#[cfg_attr(
feature = "serialize",
derive(serde::Serialize, serde::Deserialize),
reflect(Serialize, Deserialize)
)]
#[repr(u32)]
pub enum KeyCode {
/// The `1` key over the letters.
Expand Down Expand Up @@ -425,7 +437,11 @@ pub enum KeyCode {
/// ## Updating
///
/// The resource is updated inside of the [`keyboard_input_system`](crate::keyboard::keyboard_input_system).
#[derive(Reflect, FromReflect, Debug, Hash, Ord, PartialOrd, PartialEq, Eq, Clone, Copy)]
#[reflect(Hash, PartialEq)]
#[cfg_attr(feature = "serialize", derive(serde::Serialize, serde::Deserialize))]
#[derive(Debug, Hash, Ord, PartialOrd, PartialEq, Eq, Clone, Copy, Reflect, FromReflect)]
#[reflect(Debug, Hash, PartialEq)]
#[cfg_attr(
feature = "serialize",
derive(serde::Serialize, serde::Deserialize),
reflect(Serialize, Deserialize)
)]
pub struct ScanCode(pub u32);
62 changes: 54 additions & 8 deletions crates/bevy_input/src/lib.rs
Expand Up @@ -6,7 +6,6 @@ pub mod mouse;
pub mod touch;

pub use axis::*;
use bevy_ecs::schedule::{IntoSystemDescriptor, SystemLabel};
pub use input::*;

pub mod prelude {
Expand All @@ -24,16 +23,24 @@ pub mod prelude {
}

use bevy_app::prelude::*;
use bevy_ecs::schedule::{IntoSystemDescriptor, SystemLabel};
use bevy_reflect::{FromReflect, Reflect};
use keyboard::{keyboard_input_system, KeyCode, KeyboardInput, ScanCode};
use mouse::{mouse_button_input_system, MouseButton, MouseButtonInput, MouseMotion, MouseWheel};
use prelude::Gamepads;
use touch::{touch_screen_input_system, TouchInput, Touches};
use mouse::{
mouse_button_input_system, MouseButton, MouseButtonInput, MouseMotion, MouseScrollUnit,
MouseWheel,
};
use touch::{touch_screen_input_system, ForceTouch, TouchInput, TouchPhase, Touches};

use gamepad::{
gamepad_connection_system, gamepad_event_system, GamepadAxis, GamepadButton, GamepadEvent,
GamepadEventRaw, GamepadSettings,
gamepad_connection_system, gamepad_event_system, AxisSettings, ButtonAxisSettings,
ButtonSettings, Gamepad, GamepadAxis, GamepadAxisType, GamepadButton, GamepadButtonType,
GamepadEvent, GamepadEventRaw, GamepadEventType, GamepadSettings, Gamepads,
};

#[cfg(feature = "serialize")]
use bevy_reflect::{ReflectDeserialize, ReflectSerialize};

/// Adds keyboard and mouse input to an App
#[derive(Default)]
pub struct InputPlugin;
Expand Down Expand Up @@ -84,12 +91,51 @@ impl Plugin for InputPlugin {
CoreStage::PreUpdate,
touch_screen_input_system.label(InputSystem),
);

// Register common types
app.register_type::<ButtonState>();

// Register keyboard types
app.register_type::<KeyboardInput>()
.register_type::<KeyCode>()
.register_type::<ScanCode>();

// Register mouse types
app.register_type::<MouseButtonInput>()
.register_type::<MouseButton>()
.register_type::<MouseMotion>()
.register_type::<MouseScrollUnit>()
.register_type::<MouseWheel>();

// Register touch types
app.register_type::<TouchInput>()
.register_type::<ForceTouch>()
.register_type::<TouchPhase>();

// Register gamepad types
app.register_type::<Gamepad>()
.register_type::<GamepadEventType>()
.register_type::<GamepadEvent>()
.register_type::<GamepadEventRaw>()
.register_type::<GamepadButtonType>()
.register_type::<GamepadButton>()
.register_type::<GamepadAxisType>()
.register_type::<GamepadAxis>()
.register_type::<GamepadSettings>()
.register_type::<ButtonSettings>()
.register_type::<AxisSettings>()
.register_type::<ButtonAxisSettings>();
}
}

/// The current "press" state of an element
#[derive(Debug, Copy, Clone, Eq, PartialEq, Hash)]
#[cfg_attr(feature = "serialize", derive(serde::Serialize, serde::Deserialize))]
#[derive(Debug, Copy, Clone, Eq, PartialEq, Hash, Reflect, FromReflect)]
#[reflect(Debug, Hash, PartialEq)]
#[cfg_attr(
feature = "serialize",
derive(serde::Serialize, serde::Deserialize),
reflect(Serialize, Deserialize)
)]
pub enum ButtonState {
Pressed,
Released,
Expand Down

0 comments on commit 4cefe57

Please sign in to comment.