Add apply_or_insert functions to reflected component and resources (#5201)

# Objective

`ReflectResource` and `ReflectComponent` will panic on `apply` method if there is no such component. It's not very ergonomic. And not very good for performance since I need to check if such component exists first.

## Solution

* Add `ReflectComponent::apply_or_insert` and `ReflectResource::apply_or_insert` functions.
* Rename `ReflectComponent::add` into `ReflectComponent::insert` for consistency.

---

## Changelog

### Added

* `ReflectResource::apply_or_insert` and `ReflectComponent::apply_on_insert`.

### Changed

* Rename `ReflectComponent::add` into `ReflectComponent::insert` for consistency.
* Use `ReflectComponent::apply_on_insert` in `DynamicScene` instead of manual checking.

## Migration Guide

* Rename `ReflectComponent::add` into `ReflectComponent::insert`.
This commit is contained in:
Hennadii Chernyshchyk 2022-07-11 14:11:24 +00:00
parent 8de03b0839
commit 1da6720132
2 changed files with 39 additions and 12 deletions

View File

@ -18,8 +18,9 @@ use bevy_reflect::{
/// [`bevy_reflect::TypeRegistration::data`]. /// [`bevy_reflect::TypeRegistration::data`].
#[derive(Clone)] #[derive(Clone)]
pub struct ReflectComponent { pub struct ReflectComponent {
add: fn(&mut World, Entity, &dyn Reflect), insert: fn(&mut World, Entity, &dyn Reflect),
apply: fn(&mut World, Entity, &dyn Reflect), apply: fn(&mut World, Entity, &dyn Reflect),
apply_or_insert: fn(&mut World, Entity, &dyn Reflect),
remove: fn(&mut World, Entity), remove: fn(&mut World, Entity),
reflect: fn(&World, Entity) -> Option<&dyn Reflect>, reflect: fn(&World, Entity) -> Option<&dyn Reflect>,
reflect_mut: unsafe fn(&World, Entity) -> Option<ReflectMut>, reflect_mut: unsafe fn(&World, Entity) -> Option<ReflectMut>,
@ -32,8 +33,8 @@ impl ReflectComponent {
/// # Panics /// # Panics
/// ///
/// Panics if there is no such entity. /// Panics if there is no such entity.
pub fn add(&self, world: &mut World, entity: Entity, component: &dyn Reflect) { pub fn insert(&self, world: &mut World, entity: Entity, component: &dyn Reflect) {
(self.add)(world, entity, component); (self.insert)(world, entity, component);
} }
/// Uses reflection to set the value of this [`Component`] type in the entity to the given value. /// Uses reflection to set the value of this [`Component`] type in the entity to the given value.
@ -45,6 +46,15 @@ impl ReflectComponent {
(self.apply)(world, entity, component); (self.apply)(world, entity, component);
} }
/// Uses reflection to set the value of this [`Component`] type in the entity to the given value or insert a new one if it does not exist.
///
/// # Panics
///
/// Panics if the `entity` does not exist.
pub fn apply_or_insert(&self, world: &mut World, entity: Entity, component: &dyn Reflect) {
(self.apply_or_insert)(world, entity, component);
}
/// Removes this [`Component`] type from the entity. Does nothing if it doesn't exist. /// Removes this [`Component`] type from the entity. Does nothing if it doesn't exist.
/// ///
/// # Panics /// # Panics
@ -103,7 +113,7 @@ impl ReflectComponent {
impl<C: Component + Reflect + FromWorld> FromType<C> for ReflectComponent { impl<C: Component + Reflect + FromWorld> FromType<C> for ReflectComponent {
fn from_type() -> Self { fn from_type() -> Self {
ReflectComponent { ReflectComponent {
add: |world, entity, reflected_component| { insert: |world, entity, reflected_component| {
let mut component = C::from_world(world); let mut component = C::from_world(world);
component.apply(reflected_component); component.apply(reflected_component);
world.entity_mut(entity).insert(component); world.entity_mut(entity).insert(component);
@ -112,6 +122,15 @@ impl<C: Component + Reflect + FromWorld> FromType<C> for ReflectComponent {
let mut component = world.get_mut::<C>(entity).unwrap(); let mut component = world.get_mut::<C>(entity).unwrap();
component.apply(reflected_component); component.apply(reflected_component);
}, },
apply_or_insert: |world, entity, reflected_component| {
if let Some(mut component) = world.get_mut::<C>(entity) {
component.apply(reflected_component);
} else {
let mut component = C::from_world(world);
component.apply(reflected_component);
world.entity_mut(entity).insert(component);
}
},
remove: |world, entity| { remove: |world, entity| {
world.entity_mut(entity).remove::<C>(); world.entity_mut(entity).remove::<C>();
}, },
@ -154,6 +173,7 @@ impl<C: Component + Reflect + FromWorld> FromType<C> for ReflectComponent {
pub struct ReflectResource { pub struct ReflectResource {
insert: fn(&mut World, &dyn Reflect), insert: fn(&mut World, &dyn Reflect),
apply: fn(&mut World, &dyn Reflect), apply: fn(&mut World, &dyn Reflect),
apply_or_insert: fn(&mut World, &dyn Reflect),
remove: fn(&mut World), remove: fn(&mut World),
reflect: fn(&World) -> Option<&dyn Reflect>, reflect: fn(&World) -> Option<&dyn Reflect>,
reflect_unchecked_mut: unsafe fn(&World) -> Option<ReflectMut>, reflect_unchecked_mut: unsafe fn(&World) -> Option<ReflectMut>,
@ -175,6 +195,11 @@ impl ReflectResource {
(self.apply)(world, resource); (self.apply)(world, resource);
} }
/// Uses reflection to set the value of this [`Resource`] type in the world to the given value or insert a new one if it does not exist.
pub fn apply_or_insert(&self, world: &mut World, resource: &dyn Reflect) {
(self.apply_or_insert)(world, resource);
}
/// Removes this [`Resource`] type from the world. Does nothing if it doesn't exist. /// Removes this [`Resource`] type from the world. Does nothing if it doesn't exist.
pub fn remove(&self, world: &mut World) { pub fn remove(&self, world: &mut World) {
(self.remove)(world); (self.remove)(world);
@ -224,6 +249,15 @@ impl<C: Resource + Reflect + FromWorld> FromType<C> for ReflectResource {
let mut resource = world.resource_mut::<C>(); let mut resource = world.resource_mut::<C>();
resource.apply(reflected_resource); resource.apply(reflected_resource);
}, },
apply_or_insert: |world, reflected_resource| {
if let Some(mut resource) = world.get_resource_mut::<C>() {
resource.apply(reflected_resource);
} else {
let mut resource = C::from_world(world);
resource.apply(reflected_resource);
world.insert_resource(resource);
}
},
remove: |world| { remove: |world| {
world.remove_resource::<C>(); world.remove_resource::<C>();
}, },

View File

@ -112,14 +112,7 @@ impl DynamicScene {
// If the entity already has the given component attached, // If the entity already has the given component attached,
// just apply the (possibly) new value, otherwise add the // just apply the (possibly) new value, otherwise add the
// component to the entity. // component to the entity.
if world reflect_component.apply_or_insert(world, entity, &**component);
.entity(entity)
.contains_type_id(registration.type_id())
{
reflect_component.apply(world, entity, &**component);
} else {
reflect_component.add(world, entity, &**component);
}
} }
} }