From eff96e20a0eb84e1173f6f937b8b78690c3aed43 Mon Sep 17 00:00:00 2001 From: Giacomo Stevanato Date: Fri, 19 Jan 2024 17:08:57 +0100 Subject: [PATCH] Add `ReflectFromWorld` and replace the `FromWorld` requirement on `ReflectComponent` and `ReflectBundle` with `FromReflect` (#9623) # Objective - `FromType` for `ReflectComponent` and `ReflectBundle` currently require `T: FromWorld` for two reasons: - they include a `from_world` method; - they create dummy `T`s using `FromWorld` and then `apply` a `&dyn Reflect` to it to simulate `FromReflect`. - However `FromWorld`/`Default` may be difficult/weird/impractical to implement, while `FromReflect` is easier and also more natural for the job. - See also https://discord.com/channels/691052431525675048/1146022009554337792 ## Solution - Split `from_world` from `ReflectComponent` and `ReflectBundle` into its own `ReflectFromWorld` struct. - Replace the requirement on `FromWorld` in `ReflectComponent` and `ReflectBundle` with `FromReflect` --- ## Changelog - `ReflectComponent` and `ReflectBundle` no longer offer a `from_world` method. - `ReflectComponent` and `ReflectBundle`'s `FromType` implementation no longer requires `T: FromWorld`, but now requires `FromReflect`. - `ReflectComponent::insert`, `ReflectComponent::apply_or_insert` and `ReflectComponent::copy` now take an extra `&TypeRegistry` parameter. - There is now a new `ReflectFromWorld` struct. ## Migration Guide - Existing uses of `ReflectComponent::from_world` and `ReflectBundle::from_world` will have to be changed to `ReflectFromWorld::from_world`. - Users of `#[reflect(Component)]` and `#[reflect(Bundle)]` will need to also implement/derive `FromReflect`. - Users of `#[reflect(Component)]` and `#[reflect(Bundle)]` may now want to also add `FromWorld` to the list of reflected traits in case their `FromReflect` implementation may fail. - Users of `ReflectComponent` will now need to pass a `&TypeRegistry` to its `insert`, `apply_or_insert` and `copy` methods. --- crates/bevy_ecs/src/lib.rs | 4 +- crates/bevy_ecs/src/reflect/bundle.rs | 157 +++++++++--------- crates/bevy_ecs/src/reflect/component.rs | 115 +++++++++---- .../bevy_ecs/src/reflect/entity_commands.rs | 2 +- crates/bevy_ecs/src/reflect/from_world.rs | 86 ++++++++++ crates/bevy_ecs/src/reflect/mod.rs | 2 + crates/bevy_scene/src/dynamic_scene.rs | 2 +- crates/bevy_scene/src/scene.rs | 8 +- examples/scene/scene.rs | 2 +- 9 files changed, 264 insertions(+), 114 deletions(-) create mode 100644 crates/bevy_ecs/src/reflect/from_world.rs diff --git a/crates/bevy_ecs/src/lib.rs b/crates/bevy_ecs/src/lib.rs index c262c8b3ce..0260860c75 100644 --- a/crates/bevy_ecs/src/lib.rs +++ b/crates/bevy_ecs/src/lib.rs @@ -28,7 +28,9 @@ pub use bevy_ptr as ptr; pub mod prelude { #[doc(hidden)] #[cfg(feature = "bevy_reflect")] - pub use crate::reflect::{AppTypeRegistry, ReflectComponent, ReflectResource}; + pub use crate::reflect::{ + AppTypeRegistry, ReflectComponent, ReflectFromWorld, ReflectResource, + }; #[doc(hidden)] pub use crate::{ bundle::Bundle, diff --git a/crates/bevy_ecs/src/reflect/bundle.rs b/crates/bevy_ecs/src/reflect/bundle.rs index e5ea2de2b1..22905f53b8 100644 --- a/crates/bevy_ecs/src/reflect/bundle.rs +++ b/crates/bevy_ecs/src/reflect/bundle.rs @@ -1,19 +1,17 @@ //! Definitions for [`Bundle`] reflection. +//! This allows inserting, updating and/or removing bundles whose type is only known at runtime. //! //! This module exports two types: [`ReflectBundleFns`] and [`ReflectBundle`]. //! //! Same as [`super::component`], but for bundles. use std::any::TypeId; -use crate::{ - prelude::Bundle, - world::{EntityWorldMut, FromWorld, World}, -}; -use bevy_reflect::{FromType, Reflect, ReflectRef, TypeRegistry}; +use crate::{prelude::Bundle, world::EntityWorldMut}; +use bevy_reflect::{FromReflect, FromType, Reflect, ReflectRef, TypeRegistry}; use super::ReflectComponent; -/// A struct used to operate on reflected [`Bundle`] of a type. +/// A struct used to operate on reflected [`Bundle`] trait of a type. /// /// A [`ReflectBundle`] for type `T` can be obtained via /// [`bevy_reflect::TypeRegistration::data`]. @@ -25,8 +23,6 @@ pub struct ReflectBundle(ReflectBundleFns); /// The also [`super::component::ReflectComponentFns`]. #[derive(Clone)] pub struct ReflectBundleFns { - /// Function pointer implementing [`ReflectBundle::from_world()`]. - pub from_world: fn(&mut World) -> Box, /// Function pointer implementing [`ReflectBundle::insert()`]. pub insert: fn(&mut EntityWorldMut, &dyn Reflect), /// Function pointer implementing [`ReflectBundle::apply()`]. @@ -43,17 +39,12 @@ impl ReflectBundleFns { /// /// This is useful if you want to start with the default implementation before overriding some /// of the functions to create a custom implementation. - pub fn new() -> Self { + pub fn new() -> Self { >::from_type().0 } } impl ReflectBundle { - /// Constructs default reflected [`Bundle`] from world using [`from_world()`](FromWorld::from_world). - pub fn from_world(&self, world: &mut World) -> Box { - (self.0.from_world)(world) - } - /// Insert a reflected [`Bundle`] into the entity like [`insert()`](EntityWorldMut::insert). pub fn insert(&self, entity: &mut EntityWorldMut, bundle: &dyn Reflect) { (self.0.insert)(entity, bundle); @@ -123,49 +114,53 @@ impl ReflectBundle { } } -impl FromType for ReflectBundle { +impl FromType for ReflectBundle { fn from_type() -> Self { ReflectBundle(ReflectBundleFns { - from_world: |world| Box::new(B::from_world(world)), insert: |entity, reflected_bundle| { - let mut bundle = entity.world_scope(|world| B::from_world(world)); - bundle.apply(reflected_bundle); + let bundle = B::from_reflect(reflected_bundle).unwrap(); entity.insert(bundle); }, apply: |entity, reflected_bundle, registry| { - let mut bundle = entity.world_scope(|world| B::from_world(world)); - bundle.apply(reflected_bundle); - - match bundle.reflect_ref() { - ReflectRef::Struct(bundle) => bundle - .iter_fields() - .for_each(|field| insert_field::(entity, field, registry)), - ReflectRef::Tuple(bundle) => bundle - .iter_fields() - .for_each(|field| insert_field::(entity, field, registry)), - _ => panic!( - "expected bundle `{}` to be named struct or tuple", - // FIXME: once we have unique reflect, use `TypePath`. - std::any::type_name::(), - ), + if let Some(reflect_component) = + registry.get_type_data::(TypeId::of::()) + { + reflect_component.apply(entity, reflected_bundle); + } else { + match reflected_bundle.reflect_ref() { + ReflectRef::Struct(bundle) => bundle + .iter_fields() + .for_each(|field| insert_field(entity, field, registry)), + ReflectRef::Tuple(bundle) => bundle + .iter_fields() + .for_each(|field| insert_field(entity, field, registry)), + _ => panic!( + "expected bundle `{}` to be named struct or tuple", + // FIXME: once we have unique reflect, use `TypePath`. + std::any::type_name::(), + ), + } } }, apply_or_insert: |entity, reflected_bundle, registry| { - let mut bundle = entity.world_scope(|world| B::from_world(world)); - bundle.apply(reflected_bundle); - - match bundle.reflect_ref() { - ReflectRef::Struct(bundle) => bundle - .iter_fields() - .for_each(|field| apply_or_insert_field::(entity, field, registry)), - ReflectRef::Tuple(bundle) => bundle - .iter_fields() - .for_each(|field| apply_or_insert_field::(entity, field, registry)), - _ => panic!( - "expected bundle `{}` to be named struct or tuple", - // FIXME: once we have unique reflect, use `TypePath`. - std::any::type_name::(), - ), + if let Some(reflect_component) = + registry.get_type_data::(TypeId::of::()) + { + reflect_component.apply_or_insert(entity, reflected_bundle, registry); + } else { + match reflected_bundle.reflect_ref() { + ReflectRef::Struct(bundle) => bundle + .iter_fields() + .for_each(|field| apply_or_insert_field(entity, field, registry)), + ReflectRef::Tuple(bundle) => bundle + .iter_fields() + .for_each(|field| apply_or_insert_field(entity, field, registry)), + _ => panic!( + "expected bundle `{}` to be named struct or tuple", + // FIXME: once we have unique reflect, use `TypePath`. + std::any::type_name::(), + ), + } } }, remove: |entity| { @@ -175,54 +170,58 @@ impl FromType for ReflectBundle { } } -fn insert_field( - entity: &mut EntityWorldMut, - field: &dyn Reflect, - registry: &TypeRegistry, -) { +fn insert_field(entity: &mut EntityWorldMut, field: &dyn Reflect, registry: &TypeRegistry) { if let Some(reflect_component) = registry.get_type_data::(field.type_id()) { reflect_component.apply(entity, field); } else if let Some(reflect_bundle) = registry.get_type_data::(field.type_id()) { reflect_bundle.apply(entity, field, registry); } else { - entity.world_scope(|world| { - if world.components().get_id(TypeId::of::()).is_some() { - panic!( - "no `ReflectComponent` registration found for `{}`", - field.reflect_type_path(), - ); - }; - }); + let is_component = entity + .world() + .components() + .get_id(field.type_id()) + .is_some(); - panic!( - "no `ReflectBundle` registration found for `{}`", - field.reflect_type_path(), - ) + if is_component { + panic!( + "no `ReflectComponent` registration found for `{}`", + field.reflect_type_path(), + ); + } else { + panic!( + "no `ReflectBundle` registration found for `{}`", + field.reflect_type_path(), + ) + } } } -fn apply_or_insert_field( +fn apply_or_insert_field( entity: &mut EntityWorldMut, field: &dyn Reflect, registry: &TypeRegistry, ) { if let Some(reflect_component) = registry.get_type_data::(field.type_id()) { - reflect_component.apply_or_insert(entity, field); + reflect_component.apply_or_insert(entity, field, registry); } else if let Some(reflect_bundle) = registry.get_type_data::(field.type_id()) { reflect_bundle.apply_or_insert(entity, field, registry); } else { - entity.world_scope(|world| { - if world.components().get_id(TypeId::of::()).is_some() { - panic!( - "no `ReflectComponent` registration found for `{}`", - field.reflect_type_path(), - ); - }; - }); + let is_component = entity + .world() + .components() + .get_id(field.type_id()) + .is_some(); - panic!( - "no `ReflectBundle` registration found for `{}`", - field.reflect_type_path(), - ) + if is_component { + panic!( + "no `ReflectComponent` registration found for `{}`", + field.reflect_type_path(), + ); + } else { + panic!( + "no `ReflectBundle` registration found for `{}`", + field.reflect_type_path(), + ) + } } } diff --git a/crates/bevy_ecs/src/reflect/component.rs b/crates/bevy_ecs/src/reflect/component.rs index b4755b6d6a..2a17c8aff0 100644 --- a/crates/bevy_ecs/src/reflect/component.rs +++ b/crates/bevy_ecs/src/reflect/component.rs @@ -1,4 +1,6 @@ //! Definitions for [`Component`] reflection. +//! This allows inserting, updating, removing and generally interacting with components +//! whose types are only known at runtime. //! //! This module exports two types: [`ReflectComponentFns`] and [`ReflectComponent`]. //! @@ -55,15 +57,18 @@ //! //! [`get_type_registration`]: bevy_reflect::GetTypeRegistration::get_type_registration +use std::any::TypeId; + +use super::ReflectFromWorld; use crate::{ change_detection::Mut, component::Component, entity::Entity, - world::{unsafe_world_cell::UnsafeEntityCell, EntityRef, EntityWorldMut, FromWorld, World}, + world::{unsafe_world_cell::UnsafeEntityCell, EntityRef, EntityWorldMut, World}, }; -use bevy_reflect::{FromType, Reflect}; +use bevy_reflect::{FromReflect, FromType, Reflect, TypeRegistry}; -/// A struct used to operate on reflected [`Component`] of a type. +/// A struct used to operate on reflected [`Component`] trait of a type. /// /// A [`ReflectComponent`] for type `T` can be obtained via /// [`bevy_reflect::TypeRegistration::data`]. @@ -80,7 +85,7 @@ pub struct ReflectComponent(ReflectComponentFns); /// > will not need. /// > Usually a [`ReflectComponent`] is created for a type by deriving [`Reflect`] /// > and adding the `#[reflect(Component)]` attribute. -/// > After adding the component to the [`TypeRegistry`][bevy_reflect::TypeRegistry], +/// > After adding the component to the [`TypeRegistry`], /// > its [`ReflectComponent`] can then be retrieved when needed. /// /// Creating a custom [`ReflectComponent`] may be useful if you need to create new component types @@ -92,14 +97,12 @@ pub struct ReflectComponent(ReflectComponentFns); /// world. #[derive(Clone)] pub struct ReflectComponentFns { - /// Function pointer implementing [`ReflectComponent::from_world()`]. - pub from_world: fn(&mut World) -> Box, /// Function pointer implementing [`ReflectComponent::insert()`]. - pub insert: fn(&mut EntityWorldMut, &dyn Reflect), + pub insert: fn(&mut EntityWorldMut, &dyn Reflect, &TypeRegistry), /// Function pointer implementing [`ReflectComponent::apply()`]. pub apply: fn(&mut EntityWorldMut, &dyn Reflect), /// Function pointer implementing [`ReflectComponent::apply_or_insert()`]. - pub apply_or_insert: fn(&mut EntityWorldMut, &dyn Reflect), + pub apply_or_insert: fn(&mut EntityWorldMut, &dyn Reflect, &TypeRegistry), /// Function pointer implementing [`ReflectComponent::remove()`]. pub remove: fn(&mut EntityWorldMut), /// Function pointer implementing [`ReflectComponent::contains()`]. @@ -114,7 +117,7 @@ pub struct ReflectComponentFns { /// The function may only be called with an [`UnsafeEntityCell`] that can be used to mutably access the relevant component on the given entity. pub reflect_unchecked_mut: unsafe fn(UnsafeEntityCell<'_>) -> Option>, /// Function pointer implementing [`ReflectComponent::copy()`]. - pub copy: fn(&World, &mut World, Entity, Entity), + pub copy: fn(&World, &mut World, Entity, Entity, &TypeRegistry), } impl ReflectComponentFns { @@ -123,20 +126,20 @@ impl ReflectComponentFns { /// /// This is useful if you want to start with the default implementation before overriding some /// of the functions to create a custom implementation. - pub fn new() -> Self { + pub fn new() -> Self { >::from_type().0 } } impl ReflectComponent { - /// Constructs default reflected [`Component`] from world using [`from_world()`](FromWorld::from_world). - pub fn from_world(&self, world: &mut World) -> Box { - (self.0.from_world)(world) - } - /// Insert a reflected [`Component`] into the entity like [`insert()`](EntityWorldMut::insert). - pub fn insert(&self, entity: &mut EntityWorldMut, component: &dyn Reflect) { - (self.0.insert)(entity, component); + pub fn insert( + &self, + entity: &mut EntityWorldMut, + component: &dyn Reflect, + registry: &TypeRegistry, + ) { + (self.0.insert)(entity, component, registry); } /// Uses reflection to set the value of this [`Component`] type in the entity to the given value. @@ -149,8 +152,13 @@ impl ReflectComponent { } /// 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. - pub fn apply_or_insert(&self, entity: &mut EntityWorldMut, component: &dyn Reflect) { - (self.0.apply_or_insert)(entity, component); + pub fn apply_or_insert( + &self, + entity: &mut EntityWorldMut, + component: &dyn Reflect, + registry: &TypeRegistry, + ) { + (self.0.apply_or_insert)(entity, component, registry); } /// Removes this [`Component`] type from the entity. Does nothing if it doesn't exist. @@ -200,12 +208,14 @@ impl ReflectComponent { destination_world: &mut World, source_entity: Entity, destination_entity: Entity, + registry: &TypeRegistry, ) { (self.0.copy)( source_world, destination_world, source_entity, destination_entity, + registry, ); } @@ -245,25 +255,26 @@ impl ReflectComponent { } } -impl FromType for ReflectComponent { +impl FromType for ReflectComponent { fn from_type() -> Self { ReflectComponent(ReflectComponentFns { - from_world: |world| Box::new(C::from_world(world)), - insert: |entity, reflected_component| { - let mut component = entity.world_scope(|world| C::from_world(world)); - component.apply(reflected_component); + insert: |entity, reflected_component, registry| { + let component = entity.world_scope(|world| { + from_reflect_or_world::(reflected_component, world, registry) + }); entity.insert(component); }, apply: |entity, reflected_component| { let mut component = entity.get_mut::().unwrap(); component.apply(reflected_component); }, - apply_or_insert: |entity, reflected_component| { + apply_or_insert: |entity, reflected_component, registry| { if let Some(mut component) = entity.get_mut::() { component.apply(reflected_component); } else { - let mut component = entity.world_scope(|world| C::from_world(world)); - component.apply(reflected_component); + let component = entity.world_scope(|world| { + from_reflect_or_world::(reflected_component, world, registry) + }); entity.insert(component); } }, @@ -271,10 +282,10 @@ impl FromType for ReflectComponent { entity.remove::(); }, contains: |entity| entity.contains::(), - copy: |source_world, destination_world, source_entity, destination_entity| { + copy: |source_world, destination_world, source_entity, destination_entity, registry| { let source_component = source_world.get::(source_entity).unwrap(); - let mut destination_component = C::from_world(destination_world); - destination_component.apply(source_component); + let destination_component = + from_reflect_or_world::(source_component, destination_world, registry); destination_world .entity_mut(destination_entity) .insert(destination_component); @@ -299,3 +310,47 @@ impl FromType for ReflectComponent { }) } } + +/// Creates a `T` from a `&dyn Reflect`. +/// +/// The first approach uses `T`'s implementation of `FromReflect`. +/// If this fails, it falls back to default-initializing a new instance of `T` using its +/// `ReflectFromWorld` data from the `world`'s `AppTypeRegistry` and `apply`ing the +/// `&dyn Reflect` on it. +/// +/// Panics if both approaches fail. +fn from_reflect_or_world( + reflected: &dyn Reflect, + world: &mut World, + registry: &TypeRegistry, +) -> T { + if let Some(value) = T::from_reflect(reflected) { + return value; + } + + // Clone the `ReflectFromWorld` because it's cheap and "frees" + // the borrow of `world` so that it can be passed to `from_world`. + let Some(reflect_from_world) = registry.get_type_data::(TypeId::of::()) + else { + panic!( + "`FromReflect` failed and no `ReflectFromWorld` registration found for `{}`", + // FIXME: once we have unique reflect, use `TypePath`. + std::any::type_name::(), + ); + }; + + let Ok(mut value) = reflect_from_world + .from_world(world) + .into_any() + .downcast::() + else { + panic!( + "the `ReflectFromWorld` registration for `{}` produced a value of a different type", + // FIXME: once we have unique reflect, use `TypePath`. + std::any::type_name::(), + ); + }; + + value.apply(reflected); + *value +} diff --git a/crates/bevy_ecs/src/reflect/entity_commands.rs b/crates/bevy_ecs/src/reflect/entity_commands.rs index 6954d27e3d..2e0f649558 100644 --- a/crates/bevy_ecs/src/reflect/entity_commands.rs +++ b/crates/bevy_ecs/src/reflect/entity_commands.rs @@ -202,7 +202,7 @@ fn insert_reflect( let Some(reflect_component) = type_registration.data::() else { panic!("Could not get ReflectComponent data (for component type {type_path}) because it doesn't exist in this TypeRegistration."); }; - reflect_component.insert(&mut entity, &*component); + reflect_component.insert(&mut entity, &*component, type_registry); } /// A [`Command`] that adds the boxed reflect component to an entity using the data in diff --git a/crates/bevy_ecs/src/reflect/from_world.rs b/crates/bevy_ecs/src/reflect/from_world.rs new file mode 100644 index 0000000000..8f9891406c --- /dev/null +++ b/crates/bevy_ecs/src/reflect/from_world.rs @@ -0,0 +1,86 @@ +//! Definitions for [`FromWorld`] reflection. +//! This allows creating instaces of types that are known only at runtime and +//! require an `&mut World` to be initialized. +//! +//! This module exports two types: [`ReflectFromWorldFns`] and [`ReflectFromWorld`]. +//! +//! Same as [`super::component`], but for [`FromWorld`]. + +use bevy_reflect::{FromType, Reflect}; + +use crate::world::{FromWorld, World}; + +/// A struct used to operate on the reflected [`FromWorld`] trait of a type. +/// +/// A [`ReflectFromWorld`] for type `T` can be obtained via +/// [`bevy_reflect::TypeRegistration::data`]. +#[derive(Clone)] +pub struct ReflectFromWorld(ReflectFromWorldFns); + +/// The raw function pointers needed to make up a [`ReflectFromWorld`]. +#[derive(Clone)] +pub struct ReflectFromWorldFns { + /// Function pointer implementing [`ReflectFromWorld::from_world()`]. + pub from_world: fn(&mut World) -> Box, +} + +impl ReflectFromWorldFns { + /// Get the default set of [`ReflectFromWorldFns`] for a specific type using its + /// [`FromType`] implementation. + /// + /// This is useful if you want to start with the default implementation before overriding some + /// of the functions to create a custom implementation. + pub fn new() -> Self { + >::from_type().0 + } +} + +impl ReflectFromWorld { + /// Constructs default reflected [`FromWorld`] from world using [`from_world()`](FromWorld::from_world). + pub fn from_world(&self, world: &mut World) -> Box { + (self.0.from_world)(world) + } + + /// Create a custom implementation of [`ReflectFromWorld`]. + /// + /// This is an advanced feature, + /// useful for scripting implementations, + /// that should not be used by most users + /// unless you know what you are doing. + /// + /// Usually you should derive [`Reflect`] and add the `#[reflect(FromWorld)]` bundle + /// to generate a [`ReflectFromWorld`] implementation automatically. + /// + /// See [`ReflectFromWorldFns`] for more information. + pub fn new(fns: ReflectFromWorldFns) -> Self { + Self(fns) + } + + /// The underlying function pointers implementing methods on `ReflectFromWorld`. + /// + /// This is useful when you want to keep track locally of an individual + /// function pointer. + /// + /// Calling [`TypeRegistry::get`] followed by + /// [`TypeRegistration::data::`] can be costly if done several + /// times per frame. Consider cloning [`ReflectFromWorld`] and keeping it + /// between frames, cloning a `ReflectFromWorld` is very cheap. + /// + /// If you only need a subset of the methods on `ReflectFromWorld`, + /// use `fn_pointers` to get the underlying [`ReflectFromWorldFns`] + /// and copy the subset of function pointers you care about. + /// + /// [`TypeRegistration::data::`]: bevy_reflect::TypeRegistration::data + /// [`TypeRegistry::get`]: bevy_reflect::TypeRegistry::get + pub fn fn_pointers(&self) -> &ReflectFromWorldFns { + &self.0 + } +} + +impl FromType for ReflectFromWorld { + fn from_type() -> Self { + ReflectFromWorld(ReflectFromWorldFns { + from_world: |world| Box::new(B::from_world(world)), + }) + } +} diff --git a/crates/bevy_ecs/src/reflect/mod.rs b/crates/bevy_ecs/src/reflect/mod.rs index 7dd481840a..544ad02932 100644 --- a/crates/bevy_ecs/src/reflect/mod.rs +++ b/crates/bevy_ecs/src/reflect/mod.rs @@ -9,12 +9,14 @@ use bevy_reflect::{impl_reflect_value, ReflectDeserialize, ReflectSerialize, Typ mod bundle; mod component; mod entity_commands; +mod from_world; mod map_entities; mod resource; pub use bundle::{ReflectBundle, ReflectBundleFns}; pub use component::{ReflectComponent, ReflectComponentFns}; pub use entity_commands::ReflectCommandExt; +pub use from_world::{ReflectFromWorld, ReflectFromWorldFns}; pub use map_entities::ReflectMapEntities; pub use resource::{ReflectResource, ReflectResourceFns}; diff --git a/crates/bevy_scene/src/dynamic_scene.rs b/crates/bevy_scene/src/dynamic_scene.rs index 0484d0a339..676fb9fdcd 100644 --- a/crates/bevy_scene/src/dynamic_scene.rs +++ b/crates/bevy_scene/src/dynamic_scene.rs @@ -139,7 +139,7 @@ impl DynamicScene { // If the entity already has the given component attached, // just apply the (possibly) new value, otherwise add the // component to the entity. - reflect_component.apply_or_insert(entity_mut, &**component); + reflect_component.apply_or_insert(entity_mut, &**component, &type_registry); } } diff --git a/crates/bevy_scene/src/scene.rs b/crates/bevy_scene/src/scene.rs index 93e15f55f0..28cf824aa0 100644 --- a/crates/bevy_scene/src/scene.rs +++ b/crates/bevy_scene/src/scene.rs @@ -117,7 +117,13 @@ impl Scene { } }) })?; - reflect_component.copy(&self.world, world, scene_entity.id(), entity); + reflect_component.copy( + &self.world, + world, + scene_entity.id(), + entity, + &type_registry, + ); } } } diff --git a/examples/scene/scene.rs b/examples/scene/scene.rs index dac36e96d7..ef2aef66d4 100644 --- a/examples/scene/scene.rs +++ b/examples/scene/scene.rs @@ -34,7 +34,7 @@ struct ComponentA { // trait comes into play. `FromWorld` gives you access to your App's current ECS `Resources` // when you construct your component. #[derive(Component, Reflect)] -#[reflect(Component)] +#[reflect(Component, FromWorld)] struct ComponentB { pub value: String, #[reflect(skip_serializing)]