mirror of
https://github.com/bevyengine/bevy
synced 2024-11-22 12:43:34 +00:00
Add ReflectFromWorld
and replace the FromWorld
requirement on ReflectComponent
and ReflectBundle
with FromReflect
(#9623)
# Objective - `FromType<T>` 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<T>` 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.
This commit is contained in:
parent
83d6600267
commit
eff96e20a0
9 changed files with 264 additions and 114 deletions
|
@ -28,7 +28,9 @@ pub use bevy_ptr as ptr;
|
||||||
pub mod prelude {
|
pub mod prelude {
|
||||||
#[doc(hidden)]
|
#[doc(hidden)]
|
||||||
#[cfg(feature = "bevy_reflect")]
|
#[cfg(feature = "bevy_reflect")]
|
||||||
pub use crate::reflect::{AppTypeRegistry, ReflectComponent, ReflectResource};
|
pub use crate::reflect::{
|
||||||
|
AppTypeRegistry, ReflectComponent, ReflectFromWorld, ReflectResource,
|
||||||
|
};
|
||||||
#[doc(hidden)]
|
#[doc(hidden)]
|
||||||
pub use crate::{
|
pub use crate::{
|
||||||
bundle::Bundle,
|
bundle::Bundle,
|
||||||
|
|
|
@ -1,19 +1,17 @@
|
||||||
//! Definitions for [`Bundle`] reflection.
|
//! 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`].
|
//! This module exports two types: [`ReflectBundleFns`] and [`ReflectBundle`].
|
||||||
//!
|
//!
|
||||||
//! Same as [`super::component`], but for bundles.
|
//! Same as [`super::component`], but for bundles.
|
||||||
use std::any::TypeId;
|
use std::any::TypeId;
|
||||||
|
|
||||||
use crate::{
|
use crate::{prelude::Bundle, world::EntityWorldMut};
|
||||||
prelude::Bundle,
|
use bevy_reflect::{FromReflect, FromType, Reflect, ReflectRef, TypeRegistry};
|
||||||
world::{EntityWorldMut, FromWorld, World},
|
|
||||||
};
|
|
||||||
use bevy_reflect::{FromType, Reflect, ReflectRef, TypeRegistry};
|
|
||||||
|
|
||||||
use super::ReflectComponent;
|
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
|
/// A [`ReflectBundle`] for type `T` can be obtained via
|
||||||
/// [`bevy_reflect::TypeRegistration::data`].
|
/// [`bevy_reflect::TypeRegistration::data`].
|
||||||
|
@ -25,8 +23,6 @@ pub struct ReflectBundle(ReflectBundleFns);
|
||||||
/// The also [`super::component::ReflectComponentFns`].
|
/// The also [`super::component::ReflectComponentFns`].
|
||||||
#[derive(Clone)]
|
#[derive(Clone)]
|
||||||
pub struct ReflectBundleFns {
|
pub struct ReflectBundleFns {
|
||||||
/// Function pointer implementing [`ReflectBundle::from_world()`].
|
|
||||||
pub from_world: fn(&mut World) -> Box<dyn Reflect>,
|
|
||||||
/// Function pointer implementing [`ReflectBundle::insert()`].
|
/// Function pointer implementing [`ReflectBundle::insert()`].
|
||||||
pub insert: fn(&mut EntityWorldMut, &dyn Reflect),
|
pub insert: fn(&mut EntityWorldMut, &dyn Reflect),
|
||||||
/// Function pointer implementing [`ReflectBundle::apply()`].
|
/// 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
|
/// This is useful if you want to start with the default implementation before overriding some
|
||||||
/// of the functions to create a custom implementation.
|
/// of the functions to create a custom implementation.
|
||||||
pub fn new<T: Bundle + Reflect + FromWorld>() -> Self {
|
pub fn new<T: Bundle + Reflect + FromReflect>() -> Self {
|
||||||
<ReflectBundle as FromType<T>>::from_type().0
|
<ReflectBundle as FromType<T>>::from_type().0
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
impl ReflectBundle {
|
impl ReflectBundle {
|
||||||
/// Constructs default reflected [`Bundle`] from world using [`from_world()`](FromWorld::from_world).
|
|
||||||
pub fn from_world(&self, world: &mut World) -> Box<dyn Reflect> {
|
|
||||||
(self.0.from_world)(world)
|
|
||||||
}
|
|
||||||
|
|
||||||
/// Insert a reflected [`Bundle`] into the entity like [`insert()`](EntityWorldMut::insert).
|
/// Insert a reflected [`Bundle`] into the entity like [`insert()`](EntityWorldMut::insert).
|
||||||
pub fn insert(&self, entity: &mut EntityWorldMut, bundle: &dyn Reflect) {
|
pub fn insert(&self, entity: &mut EntityWorldMut, bundle: &dyn Reflect) {
|
||||||
(self.0.insert)(entity, bundle);
|
(self.0.insert)(entity, bundle);
|
||||||
|
@ -123,50 +114,54 @@ impl ReflectBundle {
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
impl<B: Bundle + Reflect + FromWorld> FromType<B> for ReflectBundle {
|
impl<B: Bundle + Reflect + FromReflect> FromType<B> for ReflectBundle {
|
||||||
fn from_type() -> Self {
|
fn from_type() -> Self {
|
||||||
ReflectBundle(ReflectBundleFns {
|
ReflectBundle(ReflectBundleFns {
|
||||||
from_world: |world| Box::new(B::from_world(world)),
|
|
||||||
insert: |entity, reflected_bundle| {
|
insert: |entity, reflected_bundle| {
|
||||||
let mut bundle = entity.world_scope(|world| B::from_world(world));
|
let bundle = B::from_reflect(reflected_bundle).unwrap();
|
||||||
bundle.apply(reflected_bundle);
|
|
||||||
entity.insert(bundle);
|
entity.insert(bundle);
|
||||||
},
|
},
|
||||||
apply: |entity, reflected_bundle, registry| {
|
apply: |entity, reflected_bundle, registry| {
|
||||||
let mut bundle = entity.world_scope(|world| B::from_world(world));
|
if let Some(reflect_component) =
|
||||||
bundle.apply(reflected_bundle);
|
registry.get_type_data::<ReflectComponent>(TypeId::of::<B>())
|
||||||
|
{
|
||||||
match bundle.reflect_ref() {
|
reflect_component.apply(entity, reflected_bundle);
|
||||||
|
} else {
|
||||||
|
match reflected_bundle.reflect_ref() {
|
||||||
ReflectRef::Struct(bundle) => bundle
|
ReflectRef::Struct(bundle) => bundle
|
||||||
.iter_fields()
|
.iter_fields()
|
||||||
.for_each(|field| insert_field::<B>(entity, field, registry)),
|
.for_each(|field| insert_field(entity, field, registry)),
|
||||||
ReflectRef::Tuple(bundle) => bundle
|
ReflectRef::Tuple(bundle) => bundle
|
||||||
.iter_fields()
|
.iter_fields()
|
||||||
.for_each(|field| insert_field::<B>(entity, field, registry)),
|
.for_each(|field| insert_field(entity, field, registry)),
|
||||||
_ => panic!(
|
_ => panic!(
|
||||||
"expected bundle `{}` to be named struct or tuple",
|
"expected bundle `{}` to be named struct or tuple",
|
||||||
// FIXME: once we have unique reflect, use `TypePath`.
|
// FIXME: once we have unique reflect, use `TypePath`.
|
||||||
std::any::type_name::<B>(),
|
std::any::type_name::<B>(),
|
||||||
),
|
),
|
||||||
}
|
}
|
||||||
|
}
|
||||||
},
|
},
|
||||||
apply_or_insert: |entity, reflected_bundle, registry| {
|
apply_or_insert: |entity, reflected_bundle, registry| {
|
||||||
let mut bundle = entity.world_scope(|world| B::from_world(world));
|
if let Some(reflect_component) =
|
||||||
bundle.apply(reflected_bundle);
|
registry.get_type_data::<ReflectComponent>(TypeId::of::<B>())
|
||||||
|
{
|
||||||
match bundle.reflect_ref() {
|
reflect_component.apply_or_insert(entity, reflected_bundle, registry);
|
||||||
|
} else {
|
||||||
|
match reflected_bundle.reflect_ref() {
|
||||||
ReflectRef::Struct(bundle) => bundle
|
ReflectRef::Struct(bundle) => bundle
|
||||||
.iter_fields()
|
.iter_fields()
|
||||||
.for_each(|field| apply_or_insert_field::<B>(entity, field, registry)),
|
.for_each(|field| apply_or_insert_field(entity, field, registry)),
|
||||||
ReflectRef::Tuple(bundle) => bundle
|
ReflectRef::Tuple(bundle) => bundle
|
||||||
.iter_fields()
|
.iter_fields()
|
||||||
.for_each(|field| apply_or_insert_field::<B>(entity, field, registry)),
|
.for_each(|field| apply_or_insert_field(entity, field, registry)),
|
||||||
_ => panic!(
|
_ => panic!(
|
||||||
"expected bundle `{}` to be named struct or tuple",
|
"expected bundle `{}` to be named struct or tuple",
|
||||||
// FIXME: once we have unique reflect, use `TypePath`.
|
// FIXME: once we have unique reflect, use `TypePath`.
|
||||||
std::any::type_name::<B>(),
|
std::any::type_name::<B>(),
|
||||||
),
|
),
|
||||||
}
|
}
|
||||||
|
}
|
||||||
},
|
},
|
||||||
remove: |entity| {
|
remove: |entity| {
|
||||||
entity.remove::<B>();
|
entity.remove::<B>();
|
||||||
|
@ -175,54 +170,58 @@ impl<B: Bundle + Reflect + FromWorld> FromType<B> for ReflectBundle {
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
fn insert_field<B: 'static>(
|
fn insert_field(entity: &mut EntityWorldMut, field: &dyn Reflect, registry: &TypeRegistry) {
|
||||||
entity: &mut EntityWorldMut,
|
|
||||||
field: &dyn Reflect,
|
|
||||||
registry: &TypeRegistry,
|
|
||||||
) {
|
|
||||||
if let Some(reflect_component) = registry.get_type_data::<ReflectComponent>(field.type_id()) {
|
if let Some(reflect_component) = registry.get_type_data::<ReflectComponent>(field.type_id()) {
|
||||||
reflect_component.apply(entity, field);
|
reflect_component.apply(entity, field);
|
||||||
} else if let Some(reflect_bundle) = registry.get_type_data::<ReflectBundle>(field.type_id()) {
|
} else if let Some(reflect_bundle) = registry.get_type_data::<ReflectBundle>(field.type_id()) {
|
||||||
reflect_bundle.apply(entity, field, registry);
|
reflect_bundle.apply(entity, field, registry);
|
||||||
} else {
|
} else {
|
||||||
entity.world_scope(|world| {
|
let is_component = entity
|
||||||
if world.components().get_id(TypeId::of::<B>()).is_some() {
|
.world()
|
||||||
|
.components()
|
||||||
|
.get_id(field.type_id())
|
||||||
|
.is_some();
|
||||||
|
|
||||||
|
if is_component {
|
||||||
panic!(
|
panic!(
|
||||||
"no `ReflectComponent` registration found for `{}`",
|
"no `ReflectComponent` registration found for `{}`",
|
||||||
field.reflect_type_path(),
|
field.reflect_type_path(),
|
||||||
);
|
);
|
||||||
};
|
} else {
|
||||||
});
|
|
||||||
|
|
||||||
panic!(
|
panic!(
|
||||||
"no `ReflectBundle` registration found for `{}`",
|
"no `ReflectBundle` registration found for `{}`",
|
||||||
field.reflect_type_path(),
|
field.reflect_type_path(),
|
||||||
)
|
)
|
||||||
}
|
}
|
||||||
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
fn apply_or_insert_field<B: 'static>(
|
fn apply_or_insert_field(
|
||||||
entity: &mut EntityWorldMut,
|
entity: &mut EntityWorldMut,
|
||||||
field: &dyn Reflect,
|
field: &dyn Reflect,
|
||||||
registry: &TypeRegistry,
|
registry: &TypeRegistry,
|
||||||
) {
|
) {
|
||||||
if let Some(reflect_component) = registry.get_type_data::<ReflectComponent>(field.type_id()) {
|
if let Some(reflect_component) = registry.get_type_data::<ReflectComponent>(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::<ReflectBundle>(field.type_id()) {
|
} else if let Some(reflect_bundle) = registry.get_type_data::<ReflectBundle>(field.type_id()) {
|
||||||
reflect_bundle.apply_or_insert(entity, field, registry);
|
reflect_bundle.apply_or_insert(entity, field, registry);
|
||||||
} else {
|
} else {
|
||||||
entity.world_scope(|world| {
|
let is_component = entity
|
||||||
if world.components().get_id(TypeId::of::<B>()).is_some() {
|
.world()
|
||||||
|
.components()
|
||||||
|
.get_id(field.type_id())
|
||||||
|
.is_some();
|
||||||
|
|
||||||
|
if is_component {
|
||||||
panic!(
|
panic!(
|
||||||
"no `ReflectComponent` registration found for `{}`",
|
"no `ReflectComponent` registration found for `{}`",
|
||||||
field.reflect_type_path(),
|
field.reflect_type_path(),
|
||||||
);
|
);
|
||||||
};
|
} else {
|
||||||
});
|
|
||||||
|
|
||||||
panic!(
|
panic!(
|
||||||
"no `ReflectBundle` registration found for `{}`",
|
"no `ReflectBundle` registration found for `{}`",
|
||||||
field.reflect_type_path(),
|
field.reflect_type_path(),
|
||||||
)
|
)
|
||||||
}
|
}
|
||||||
|
}
|
||||||
}
|
}
|
||||||
|
|
|
@ -1,4 +1,6 @@
|
||||||
//! Definitions for [`Component`] reflection.
|
//! 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`].
|
//! This module exports two types: [`ReflectComponentFns`] and [`ReflectComponent`].
|
||||||
//!
|
//!
|
||||||
|
@ -55,15 +57,18 @@
|
||||||
//!
|
//!
|
||||||
//! [`get_type_registration`]: bevy_reflect::GetTypeRegistration::get_type_registration
|
//! [`get_type_registration`]: bevy_reflect::GetTypeRegistration::get_type_registration
|
||||||
|
|
||||||
|
use std::any::TypeId;
|
||||||
|
|
||||||
|
use super::ReflectFromWorld;
|
||||||
use crate::{
|
use crate::{
|
||||||
change_detection::Mut,
|
change_detection::Mut,
|
||||||
component::Component,
|
component::Component,
|
||||||
entity::Entity,
|
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
|
/// A [`ReflectComponent`] for type `T` can be obtained via
|
||||||
/// [`bevy_reflect::TypeRegistration::data`].
|
/// [`bevy_reflect::TypeRegistration::data`].
|
||||||
|
@ -80,7 +85,7 @@ pub struct ReflectComponent(ReflectComponentFns);
|
||||||
/// > will not need.
|
/// > will not need.
|
||||||
/// > Usually a [`ReflectComponent`] is created for a type by deriving [`Reflect`]
|
/// > Usually a [`ReflectComponent`] is created for a type by deriving [`Reflect`]
|
||||||
/// > and adding the `#[reflect(Component)]` attribute.
|
/// > 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.
|
/// > its [`ReflectComponent`] can then be retrieved when needed.
|
||||||
///
|
///
|
||||||
/// Creating a custom [`ReflectComponent`] may be useful if you need to create new component types
|
/// Creating a custom [`ReflectComponent`] may be useful if you need to create new component types
|
||||||
|
@ -92,14 +97,12 @@ pub struct ReflectComponent(ReflectComponentFns);
|
||||||
/// world.
|
/// world.
|
||||||
#[derive(Clone)]
|
#[derive(Clone)]
|
||||||
pub struct ReflectComponentFns {
|
pub struct ReflectComponentFns {
|
||||||
/// Function pointer implementing [`ReflectComponent::from_world()`].
|
|
||||||
pub from_world: fn(&mut World) -> Box<dyn Reflect>,
|
|
||||||
/// Function pointer implementing [`ReflectComponent::insert()`].
|
/// 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()`].
|
/// Function pointer implementing [`ReflectComponent::apply()`].
|
||||||
pub apply: fn(&mut EntityWorldMut, &dyn Reflect),
|
pub apply: fn(&mut EntityWorldMut, &dyn Reflect),
|
||||||
/// Function pointer implementing [`ReflectComponent::apply_or_insert()`].
|
/// 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()`].
|
/// Function pointer implementing [`ReflectComponent::remove()`].
|
||||||
pub remove: fn(&mut EntityWorldMut),
|
pub remove: fn(&mut EntityWorldMut),
|
||||||
/// Function pointer implementing [`ReflectComponent::contains()`].
|
/// 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.
|
/// 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<Mut<'_, dyn Reflect>>,
|
pub reflect_unchecked_mut: unsafe fn(UnsafeEntityCell<'_>) -> Option<Mut<'_, dyn Reflect>>,
|
||||||
/// Function pointer implementing [`ReflectComponent::copy()`].
|
/// Function pointer implementing [`ReflectComponent::copy()`].
|
||||||
pub copy: fn(&World, &mut World, Entity, Entity),
|
pub copy: fn(&World, &mut World, Entity, Entity, &TypeRegistry),
|
||||||
}
|
}
|
||||||
|
|
||||||
impl ReflectComponentFns {
|
impl ReflectComponentFns {
|
||||||
|
@ -123,20 +126,20 @@ impl ReflectComponentFns {
|
||||||
///
|
///
|
||||||
/// This is useful if you want to start with the default implementation before overriding some
|
/// This is useful if you want to start with the default implementation before overriding some
|
||||||
/// of the functions to create a custom implementation.
|
/// of the functions to create a custom implementation.
|
||||||
pub fn new<T: Component + Reflect + FromWorld>() -> Self {
|
pub fn new<T: Component + Reflect + FromReflect>() -> Self {
|
||||||
<ReflectComponent as FromType<T>>::from_type().0
|
<ReflectComponent as FromType<T>>::from_type().0
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
impl ReflectComponent {
|
impl ReflectComponent {
|
||||||
/// Constructs default reflected [`Component`] from world using [`from_world()`](FromWorld::from_world).
|
|
||||||
pub fn from_world(&self, world: &mut World) -> Box<dyn Reflect> {
|
|
||||||
(self.0.from_world)(world)
|
|
||||||
}
|
|
||||||
|
|
||||||
/// Insert a reflected [`Component`] into the entity like [`insert()`](EntityWorldMut::insert).
|
/// Insert a reflected [`Component`] into the entity like [`insert()`](EntityWorldMut::insert).
|
||||||
pub fn insert(&self, entity: &mut EntityWorldMut, component: &dyn Reflect) {
|
pub fn insert(
|
||||||
(self.0.insert)(entity, component);
|
&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.
|
/// 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.
|
/// 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) {
|
pub fn apply_or_insert(
|
||||||
(self.0.apply_or_insert)(entity, component);
|
&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.
|
/// Removes this [`Component`] type from the entity. Does nothing if it doesn't exist.
|
||||||
|
@ -200,12 +208,14 @@ impl ReflectComponent {
|
||||||
destination_world: &mut World,
|
destination_world: &mut World,
|
||||||
source_entity: Entity,
|
source_entity: Entity,
|
||||||
destination_entity: Entity,
|
destination_entity: Entity,
|
||||||
|
registry: &TypeRegistry,
|
||||||
) {
|
) {
|
||||||
(self.0.copy)(
|
(self.0.copy)(
|
||||||
source_world,
|
source_world,
|
||||||
destination_world,
|
destination_world,
|
||||||
source_entity,
|
source_entity,
|
||||||
destination_entity,
|
destination_entity,
|
||||||
|
registry,
|
||||||
);
|
);
|
||||||
}
|
}
|
||||||
|
|
||||||
|
@ -245,25 +255,26 @@ impl ReflectComponent {
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
impl<C: Component + Reflect + FromWorld> FromType<C> for ReflectComponent {
|
impl<C: Component + Reflect + FromReflect> FromType<C> for ReflectComponent {
|
||||||
fn from_type() -> Self {
|
fn from_type() -> Self {
|
||||||
ReflectComponent(ReflectComponentFns {
|
ReflectComponent(ReflectComponentFns {
|
||||||
from_world: |world| Box::new(C::from_world(world)),
|
insert: |entity, reflected_component, registry| {
|
||||||
insert: |entity, reflected_component| {
|
let component = entity.world_scope(|world| {
|
||||||
let mut component = entity.world_scope(|world| C::from_world(world));
|
from_reflect_or_world::<C>(reflected_component, world, registry)
|
||||||
component.apply(reflected_component);
|
});
|
||||||
entity.insert(component);
|
entity.insert(component);
|
||||||
},
|
},
|
||||||
apply: |entity, reflected_component| {
|
apply: |entity, reflected_component| {
|
||||||
let mut component = entity.get_mut::<C>().unwrap();
|
let mut component = entity.get_mut::<C>().unwrap();
|
||||||
component.apply(reflected_component);
|
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::<C>() {
|
if let Some(mut component) = entity.get_mut::<C>() {
|
||||||
component.apply(reflected_component);
|
component.apply(reflected_component);
|
||||||
} else {
|
} else {
|
||||||
let mut component = entity.world_scope(|world| C::from_world(world));
|
let component = entity.world_scope(|world| {
|
||||||
component.apply(reflected_component);
|
from_reflect_or_world::<C>(reflected_component, world, registry)
|
||||||
|
});
|
||||||
entity.insert(component);
|
entity.insert(component);
|
||||||
}
|
}
|
||||||
},
|
},
|
||||||
|
@ -271,10 +282,10 @@ impl<C: Component + Reflect + FromWorld> FromType<C> for ReflectComponent {
|
||||||
entity.remove::<C>();
|
entity.remove::<C>();
|
||||||
},
|
},
|
||||||
contains: |entity| entity.contains::<C>(),
|
contains: |entity| entity.contains::<C>(),
|
||||||
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::<C>(source_entity).unwrap();
|
let source_component = source_world.get::<C>(source_entity).unwrap();
|
||||||
let mut destination_component = C::from_world(destination_world);
|
let destination_component =
|
||||||
destination_component.apply(source_component);
|
from_reflect_or_world::<C>(source_component, destination_world, registry);
|
||||||
destination_world
|
destination_world
|
||||||
.entity_mut(destination_entity)
|
.entity_mut(destination_entity)
|
||||||
.insert(destination_component);
|
.insert(destination_component);
|
||||||
|
@ -299,3 +310,47 @@ impl<C: Component + Reflect + FromWorld> FromType<C> 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<T: FromReflect>(
|
||||||
|
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::<ReflectFromWorld>(TypeId::of::<T>())
|
||||||
|
else {
|
||||||
|
panic!(
|
||||||
|
"`FromReflect` failed and no `ReflectFromWorld` registration found for `{}`",
|
||||||
|
// FIXME: once we have unique reflect, use `TypePath`.
|
||||||
|
std::any::type_name::<T>(),
|
||||||
|
);
|
||||||
|
};
|
||||||
|
|
||||||
|
let Ok(mut value) = reflect_from_world
|
||||||
|
.from_world(world)
|
||||||
|
.into_any()
|
||||||
|
.downcast::<T>()
|
||||||
|
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::<T>(),
|
||||||
|
);
|
||||||
|
};
|
||||||
|
|
||||||
|
value.apply(reflected);
|
||||||
|
*value
|
||||||
|
}
|
||||||
|
|
|
@ -202,7 +202,7 @@ fn insert_reflect(
|
||||||
let Some(reflect_component) = type_registration.data::<ReflectComponent>() else {
|
let Some(reflect_component) = type_registration.data::<ReflectComponent>() else {
|
||||||
panic!("Could not get ReflectComponent data (for component type {type_path}) because it doesn't exist in this TypeRegistration.");
|
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
|
/// A [`Command`] that adds the boxed reflect component to an entity using the data in
|
||||||
|
|
86
crates/bevy_ecs/src/reflect/from_world.rs
Normal file
86
crates/bevy_ecs/src/reflect/from_world.rs
Normal file
|
@ -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<dyn Reflect>,
|
||||||
|
}
|
||||||
|
|
||||||
|
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<T: Reflect + FromWorld>() -> Self {
|
||||||
|
<ReflectFromWorld as FromType<T>>::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<dyn Reflect> {
|
||||||
|
(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::<ReflectFromWorld>`] 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::<ReflectFromWorld>`]: bevy_reflect::TypeRegistration::data
|
||||||
|
/// [`TypeRegistry::get`]: bevy_reflect::TypeRegistry::get
|
||||||
|
pub fn fn_pointers(&self) -> &ReflectFromWorldFns {
|
||||||
|
&self.0
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
impl<B: Reflect + FromWorld> FromType<B> for ReflectFromWorld {
|
||||||
|
fn from_type() -> Self {
|
||||||
|
ReflectFromWorld(ReflectFromWorldFns {
|
||||||
|
from_world: |world| Box::new(B::from_world(world)),
|
||||||
|
})
|
||||||
|
}
|
||||||
|
}
|
|
@ -9,12 +9,14 @@ use bevy_reflect::{impl_reflect_value, ReflectDeserialize, ReflectSerialize, Typ
|
||||||
mod bundle;
|
mod bundle;
|
||||||
mod component;
|
mod component;
|
||||||
mod entity_commands;
|
mod entity_commands;
|
||||||
|
mod from_world;
|
||||||
mod map_entities;
|
mod map_entities;
|
||||||
mod resource;
|
mod resource;
|
||||||
|
|
||||||
pub use bundle::{ReflectBundle, ReflectBundleFns};
|
pub use bundle::{ReflectBundle, ReflectBundleFns};
|
||||||
pub use component::{ReflectComponent, ReflectComponentFns};
|
pub use component::{ReflectComponent, ReflectComponentFns};
|
||||||
pub use entity_commands::ReflectCommandExt;
|
pub use entity_commands::ReflectCommandExt;
|
||||||
|
pub use from_world::{ReflectFromWorld, ReflectFromWorldFns};
|
||||||
pub use map_entities::ReflectMapEntities;
|
pub use map_entities::ReflectMapEntities;
|
||||||
pub use resource::{ReflectResource, ReflectResourceFns};
|
pub use resource::{ReflectResource, ReflectResourceFns};
|
||||||
|
|
||||||
|
|
|
@ -139,7 +139,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.
|
||||||
reflect_component.apply_or_insert(entity_mut, &**component);
|
reflect_component.apply_or_insert(entity_mut, &**component, &type_registry);
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
|
|
@ -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,
|
||||||
|
);
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
|
@ -34,7 +34,7 @@ struct ComponentA {
|
||||||
// trait comes into play. `FromWorld` gives you access to your App's current ECS `Resources`
|
// trait comes into play. `FromWorld` gives you access to your App's current ECS `Resources`
|
||||||
// when you construct your component.
|
// when you construct your component.
|
||||||
#[derive(Component, Reflect)]
|
#[derive(Component, Reflect)]
|
||||||
#[reflect(Component)]
|
#[reflect(Component, FromWorld)]
|
||||||
struct ComponentB {
|
struct ComponentB {
|
||||||
pub value: String,
|
pub value: String,
|
||||||
#[reflect(skip_serializing)]
|
#[reflect(skip_serializing)]
|
||||||
|
|
Loading…
Reference in a new issue