implement Reflect for Input<T>, some misc improvements to reflect value derive (#5676)

# Objective

- I'm currently working on being able to call methods on reflect types (https://github.com/jakobhellermann/bevy_reflect_fns)
- for that, I'd like to add methods to the `Input<KeyCode>` resource (which I'm doing by registering type data)
- implementing `Reflect` is currently a requirement for having type data in the `TypeRegistry`

## Solution

- derive `Reflect` for `KeyCode` and `Input`
- uses `#[reflect_value]` for `Input`, since it's fields aren't supposed to be observable
- using reflect_value would need `Clone` bounds on `T`, but since all the methods (`.pressed` etc) already require `T: Copy`, I unified everything to requiring `Copy`
- add `Send + Sync + 'static` bounds, also required by reflect derive

## Unrelated improvements 
I can extract into a separate PR if needed.

- the `Reflect` derive would previously ignore `#[reflect_value]` and only accept `#[reflect_value()]` which was a bit confusing
- the generated code used `val.clone()` on a reference, which is fine if `val` impls `Clone`, but otherwise also compiles with a worse error message. Change to `std::clone::Clone::clone(val)` instead which gives a neat `T does not implement Clone` error
This commit is contained in:
Jakob Hellermann 2022-09-07 15:59:50 +00:00
parent 7a92555233
commit 7d9e864d9c
4 changed files with 21 additions and 17 deletions

View file

@ -1,4 +1,5 @@
use bevy_ecs::system::Resource;
use bevy_reflect::Reflect;
use bevy_utils::HashSet;
use std::hash::Hash;
@ -33,8 +34,9 @@ use bevy_ecs::schedule::State;
/// * Call the [`Input::press`] method for each press event.
/// * 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)]
pub struct Input<T: Eq + Hash> {
#[derive(Debug, Clone, Resource, Reflect)]
#[reflect_value]
pub struct Input<T: Copy + Eq + Hash + Send + Sync + 'static> {
/// A collection of every button that is currently being pressed.
pressed: HashSet<T>,
/// A collection of every button that has just been pressed.
@ -43,7 +45,7 @@ pub struct Input<T: Eq + Hash> {
just_released: HashSet<T>,
}
impl<T: Eq + Hash> Default for Input<T> {
impl<T: Copy + Eq + Hash + Send + Sync + 'static> Default for Input<T> {
fn default() -> Self {
Self {
pressed: Default::default(),
@ -55,7 +57,7 @@ impl<T: Eq + Hash> Default for Input<T> {
impl<T> Input<T>
where
T: Copy + Eq + Hash,
T: Copy + Eq + Hash + Send + Sync + 'static,
{
/// Registers a press for the given `input`.
pub fn press(&mut self, input: T) {

View file

@ -1,5 +1,6 @@
use crate::{ButtonState, Input};
use bevy_ecs::{event::EventReader, system::ResMut};
use bevy_reflect::{FromReflect, Reflect};
/// A keyboard input event.
///
@ -60,8 +61,9 @@ pub fn keyboard_input_system(
/// ## Updating
///
/// The resource is updated inside of the [`keyboard_input_system`](crate::keyboard::keyboard_input_system).
#[derive(Debug, Hash, Ord, PartialOrd, PartialEq, Eq, Clone, Copy)]
#[derive(Reflect, FromReflect, Debug, Hash, Ord, PartialOrd, PartialEq, Eq, Clone, Copy)]
#[cfg_attr(feature = "serialize", derive(serde::Serialize, serde::Deserialize))]
#[reflect(Hash, PartialEq)]
#[repr(u32)]
pub enum KeyCode {
/// The `1` key over the letters.
@ -422,6 +424,7 @@ pub enum KeyCode {
/// ## Updating
///
/// The resource is updated inside of the [`keyboard_input_system`](crate::keyboard::keyboard_input_system).
#[derive(Debug, Hash, Ord, PartialOrd, PartialEq, Eq, Clone, Copy)]
#[derive(Reflect, FromReflect, Debug, Hash, Ord, PartialOrd, PartialEq, Eq, Clone, Copy)]
#[reflect(Hash, PartialEq)]
#[cfg_attr(feature = "serialize", derive(serde::Serialize, serde::Deserialize))]
pub struct ScanCode(pub u32);

View file

@ -111,19 +111,18 @@ impl<'a> ReflectDerive<'a> {
let mut force_reflect_value = false;
for attribute in input.attrs.iter().filter_map(|attr| attr.parse_meta().ok()) {
let meta_list = if let Meta::List(meta_list) = attribute {
meta_list
} else {
continue;
};
if let Some(ident) = meta_list.path.get_ident() {
if ident == REFLECT_ATTRIBUTE_NAME {
match attribute {
Meta::List(meta_list) if meta_list.path.is_ident(REFLECT_ATTRIBUTE_NAME) => {
traits = ReflectTraits::from_nested_metas(&meta_list.nested);
} else if ident == REFLECT_VALUE_ATTRIBUTE_NAME {
}
Meta::List(meta_list) if meta_list.path.is_ident(REFLECT_VALUE_ATTRIBUTE_NAME) => {
force_reflect_value = true;
traits = ReflectTraits::from_nested_metas(&meta_list.nested);
}
Meta::Path(path) if path.is_ident(REFLECT_VALUE_ATTRIBUTE_NAME) => {
force_reflect_value = true;
}
_ => continue,
}
}

View file

@ -68,14 +68,14 @@ pub(crate) fn impl_value(meta: &ReflectMeta) -> TokenStream {
#[inline]
fn clone_value(&self) -> Box<dyn #bevy_reflect_path::Reflect> {
Box::new(self.clone())
Box::new(std::clone::Clone::clone(self))
}
#[inline]
fn apply(&mut self, value: &dyn #bevy_reflect_path::Reflect) {
let value = value.as_any();
if let Some(value) = value.downcast_ref::<Self>() {
*self = value.clone();
*self = std::clone::Clone::clone(value);
} else {
panic!("Value is not {}.", std::any::type_name::<Self>());
}