Path: blob/main/crates/bevy_ecs/src/reflect/entity_commands.rs
6600 views
use crate::{1prelude::Mut,2reflect::{AppTypeRegistry, ReflectBundle, ReflectComponent},3resource::Resource,4system::EntityCommands,5world::EntityWorldMut,6};7use alloc::{borrow::Cow, boxed::Box};8use bevy_reflect::{PartialReflect, TypeRegistry};910/// An extension trait for [`EntityCommands`] for reflection related functions11pub trait ReflectCommandExt {12/// Adds the given boxed reflect component or bundle to the entity using the reflection data in13/// [`AppTypeRegistry`].14///15/// This will overwrite any previous component(s) of the same type.16///17/// # Panics18///19/// - If the entity doesn't exist.20/// - If [`AppTypeRegistry`] does not have the reflection data for the given21/// [`Component`](crate::component::Component) or [`Bundle`](crate::bundle::Bundle).22/// - If the component or bundle data is invalid. See [`PartialReflect::apply`] for further details.23/// - If [`AppTypeRegistry`] is not present in the [`World`](crate::world::World).24///25/// # Note26///27/// Prefer to use the typed [`EntityCommands::insert`] if possible. Adding a reflected component28/// is much slower.29///30/// # Example31///32/// ```33/// // Note that you need to register the component type in the AppTypeRegistry prior to using34/// // reflection. You can use the helpers on the App with `app.register_type::<ComponentA>()`35/// // or write to the TypeRegistry directly to register all your components36///37/// # use bevy_ecs::prelude::*;38/// # use bevy_ecs::reflect::{ReflectCommandExt, ReflectBundle};39/// # use bevy_reflect::{FromReflect, FromType, Reflect, TypeRegistry};40/// // A resource that can hold any component that implements reflect as a boxed reflect component41/// #[derive(Resource)]42/// struct Prefab {43/// data: Box<dyn Reflect>,44/// }45/// #[derive(Component, Reflect, Default)]46/// #[reflect(Component)]47/// struct ComponentA(u32);48///49/// #[derive(Component, Reflect, Default)]50/// #[reflect(Component)]51/// struct ComponentB(String);52///53/// #[derive(Bundle, Reflect, Default)]54/// #[reflect(Bundle)]55/// struct BundleA {56/// a: ComponentA,57/// b: ComponentB,58/// }59///60/// fn insert_reflect_component(61/// mut commands: Commands,62/// mut prefab: ResMut<Prefab>63/// ) {64/// // Create a set of new boxed reflect components to use65/// let boxed_reflect_component_a: Box<dyn Reflect> = Box::new(ComponentA(916));66/// let boxed_reflect_component_b: Box<dyn Reflect> = Box::new(ComponentB("NineSixteen".to_string()));67/// let boxed_reflect_bundle_a: Box<dyn Reflect> = Box::new(BundleA {68/// a: ComponentA(24),69/// b: ComponentB("Twenty-Four".to_string()),70/// });71///72/// // You can overwrite the component in the resource with either ComponentA or ComponentB73/// prefab.data = boxed_reflect_component_a;74/// prefab.data = boxed_reflect_component_b;75///76/// // Or even with BundleA77/// prefab.data = boxed_reflect_bundle_a;78///79/// // No matter which component or bundle is in the resource and without knowing the exact type, you can80/// // use the insert_reflect entity command to insert that component/bundle into an entity.81/// commands82/// .spawn_empty()83/// .insert_reflect(prefab.data.reflect_clone().unwrap().into_partial_reflect());84/// }85/// ```86fn insert_reflect(&mut self, component: Box<dyn PartialReflect>) -> &mut Self;8788/// Same as [`insert_reflect`](ReflectCommandExt::insert_reflect), but using the `T` resource as type registry instead of89/// `AppTypeRegistry`.90///91/// # Panics92///93/// - If the given [`Resource`] is not present in the [`World`](crate::world::World).94///95/// # Note96///97/// - The given [`Resource`] is removed from the [`World`](crate::world::World) before the command is applied.98fn insert_reflect_with_registry<T: Resource + AsRef<TypeRegistry>>(99&mut self,100component: Box<dyn PartialReflect>,101) -> &mut Self;102103/// Removes from the entity the component or bundle with the given type path registered in [`AppTypeRegistry`].104///105/// If the type is a bundle, it will remove any components in that bundle regardless if the entity106/// contains all the components.107///108/// Does nothing if the type is a component and the entity does not have a component of the same type,109/// if the type is a bundle and the entity does not contain any of the components in the bundle,110/// if [`AppTypeRegistry`] does not contain the reflection data for the given component,111/// or if the entity does not exist.112///113/// # Note114///115/// Prefer to use the typed [`EntityCommands::remove`] if possible. Removing a reflected component116/// is much slower.117///118/// # Example119///120/// ```121/// // Note that you need to register the component/bundle type in the AppTypeRegistry prior to using122/// // reflection. You can use the helpers on the App with `app.register_type::<ComponentA>()`123/// // or write to the TypeRegistry directly to register all your components and bundles124///125/// # use bevy_ecs::prelude::*;126/// # use bevy_ecs::reflect::{ReflectCommandExt, ReflectBundle};127/// # use bevy_reflect::{FromReflect, FromType, Reflect, TypeRegistry};128///129/// // A resource that can hold any component or bundle that implements reflect as a boxed reflect130/// #[derive(Resource)]131/// struct Prefab{132/// entity: Entity,133/// data: Box<dyn Reflect>,134/// }135/// #[derive(Component, Reflect, Default)]136/// #[reflect(Component)]137/// struct ComponentA(u32);138/// #[derive(Component, Reflect, Default)]139/// #[reflect(Component)]140/// struct ComponentB(String);141/// #[derive(Bundle, Reflect, Default)]142/// #[reflect(Bundle)]143/// struct BundleA {144/// a: ComponentA,145/// b: ComponentB,146/// }147///148/// fn remove_reflect_component(149/// mut commands: Commands,150/// prefab: Res<Prefab>151/// ) {152/// // Prefab can hold any boxed reflect component or bundle. In this case either153/// // ComponentA, ComponentB, or BundleA. No matter which component or bundle is in the resource though,154/// // we can attempt to remove any component (or set of components in the case of a bundle)155/// // of that same type from an entity.156/// commands.entity(prefab.entity)157/// .remove_reflect(prefab.data.reflect_type_path().to_owned());158/// }159/// ```160fn remove_reflect(&mut self, component_type_path: impl Into<Cow<'static, str>>) -> &mut Self;161/// Same as [`remove_reflect`](ReflectCommandExt::remove_reflect), but using the `T` resource as type registry instead of162/// `AppTypeRegistry`.163fn remove_reflect_with_registry<T: Resource + AsRef<TypeRegistry>>(164&mut self,165component_type_path: impl Into<Cow<'static, str>>,166) -> &mut Self;167}168169impl ReflectCommandExt for EntityCommands<'_> {170fn insert_reflect(&mut self, component: Box<dyn PartialReflect>) -> &mut Self {171self.queue(move |mut entity: EntityWorldMut| {172entity.insert_reflect(component);173})174}175176fn insert_reflect_with_registry<T: Resource + AsRef<TypeRegistry>>(177&mut self,178component: Box<dyn PartialReflect>,179) -> &mut Self {180self.queue(move |mut entity: EntityWorldMut| {181entity.insert_reflect_with_registry::<T>(component);182})183}184185fn remove_reflect(&mut self, component_type_path: impl Into<Cow<'static, str>>) -> &mut Self {186let component_type_path: Cow<'static, str> = component_type_path.into();187self.queue(move |mut entity: EntityWorldMut| {188entity.remove_reflect(component_type_path);189})190}191192fn remove_reflect_with_registry<T: Resource + AsRef<TypeRegistry>>(193&mut self,194component_type_path: impl Into<Cow<'static, str>>,195) -> &mut Self {196let component_type_path: Cow<'static, str> = component_type_path.into();197self.queue(move |mut entity: EntityWorldMut| {198entity.remove_reflect_with_registry::<T>(component_type_path);199})200}201}202203impl<'w> EntityWorldMut<'w> {204/// Adds the given boxed reflect component or bundle to the entity using the reflection data in205/// [`AppTypeRegistry`].206///207/// This will overwrite any previous component(s) of the same type.208///209/// # Panics210///211/// - If the entity has been despawned while this `EntityWorldMut` is still alive.212/// - If [`AppTypeRegistry`] does not have the reflection data for the given213/// [`Component`](crate::component::Component) or [`Bundle`](crate::bundle::Bundle).214/// - If the component or bundle data is invalid. See [`PartialReflect::apply`] for further details.215/// - If [`AppTypeRegistry`] is not present in the [`World`](crate::world::World).216///217/// # Note218///219/// Prefer to use the typed [`EntityWorldMut::insert`] if possible. Adding a reflected component220/// is much slower.221pub fn insert_reflect(&mut self, component: Box<dyn PartialReflect>) -> &mut Self {222self.assert_not_despawned();223self.resource_scope(|entity, registry: Mut<AppTypeRegistry>| {224let type_registry = ®istry.as_ref().read();225insert_reflect_with_registry_ref(entity, type_registry, component);226});227self228}229230/// Same as [`insert_reflect`](EntityWorldMut::insert_reflect), but using231/// the `T` resource as type registry instead of [`AppTypeRegistry`].232///233/// This will overwrite any previous component(s) of the same type.234///235/// # Panics236///237/// - If the entity has been despawned while this `EntityWorldMut` is still alive.238/// - If the given [`Resource`] does not have the reflection data for the given239/// [`Component`](crate::component::Component) or [`Bundle`](crate::bundle::Bundle).240/// - If the component or bundle data is invalid. See [`PartialReflect::apply`] for further details.241/// - If the given [`Resource`] is not present in the [`World`](crate::world::World).242pub fn insert_reflect_with_registry<T: Resource + AsRef<TypeRegistry>>(243&mut self,244component: Box<dyn PartialReflect>,245) -> &mut Self {246self.assert_not_despawned();247self.resource_scope(|entity, registry: Mut<T>| {248let type_registry = registry.as_ref().as_ref();249insert_reflect_with_registry_ref(entity, type_registry, component);250});251self252}253254/// Removes from the entity the component or bundle with the given type path registered in [`AppTypeRegistry`].255///256/// If the type is a bundle, it will remove any components in that bundle regardless if the entity257/// contains all the components.258///259/// Does nothing if the type is a component and the entity does not have a component of the same type,260/// if the type is a bundle and the entity does not contain any of the components in the bundle,261/// or if [`AppTypeRegistry`] does not contain the reflection data for the given component.262///263/// # Panics264///265/// - If the entity has been despawned while this `EntityWorldMut` is still alive.266/// - If [`AppTypeRegistry`] is not present in the [`World`](crate::world::World).267///268/// # Note269///270/// Prefer to use the typed [`EntityCommands::remove`] if possible. Removing a reflected component271/// is much slower.272pub fn remove_reflect(&mut self, component_type_path: Cow<'static, str>) -> &mut Self {273self.assert_not_despawned();274self.resource_scope(|entity, registry: Mut<AppTypeRegistry>| {275let type_registry = ®istry.as_ref().read();276remove_reflect_with_registry_ref(entity, type_registry, component_type_path);277});278self279}280281/// Same as [`remove_reflect`](EntityWorldMut::remove_reflect), but using282/// the `T` resource as type registry instead of `AppTypeRegistry`.283///284/// If the given type is a bundle, it will remove any components in that bundle regardless if the entity285/// contains all the components.286///287/// Does nothing if the type is a component and the entity does not have a component of the same type,288/// if the type is a bundle and the entity does not contain any of the components in the bundle,289/// or if [`AppTypeRegistry`] does not contain the reflection data for the given component.290///291/// # Panics292///293/// - If the entity has been despawned while this `EntityWorldMut` is still alive.294/// - If [`AppTypeRegistry`] is not present in the [`World`](crate::world::World).295pub fn remove_reflect_with_registry<T: Resource + AsRef<TypeRegistry>>(296&mut self,297component_type_path: Cow<'static, str>,298) -> &mut Self {299self.assert_not_despawned();300self.resource_scope(|entity, registry: Mut<T>| {301let type_registry = registry.as_ref().as_ref();302remove_reflect_with_registry_ref(entity, type_registry, component_type_path);303});304self305}306}307308/// Helper function to add a reflect component or bundle to a given entity309fn insert_reflect_with_registry_ref(310entity: &mut EntityWorldMut,311type_registry: &TypeRegistry,312component: Box<dyn PartialReflect>,313) {314let type_info = component315.get_represented_type_info()316.expect("component should represent a type.");317let type_path = type_info.type_path();318let Some(type_registration) = type_registry.get(type_info.type_id()) else {319panic!("`{type_path}` should be registered in type registry via `App::register_type<{type_path}>`");320};321322if let Some(reflect_component) = type_registration.data::<ReflectComponent>() {323reflect_component.insert(entity, component.as_partial_reflect(), type_registry);324} else if let Some(reflect_bundle) = type_registration.data::<ReflectBundle>() {325reflect_bundle.insert(entity, component.as_partial_reflect(), type_registry);326} else {327panic!("`{type_path}` should have #[reflect(Component)] or #[reflect(Bundle)]");328}329}330331/// Helper function to remove a reflect component or bundle from a given entity332fn remove_reflect_with_registry_ref(333entity: &mut EntityWorldMut,334type_registry: &TypeRegistry,335component_type_path: Cow<'static, str>,336) {337let Some(type_registration) = type_registry.get_with_type_path(&component_type_path) else {338return;339};340if let Some(reflect_component) = type_registration.data::<ReflectComponent>() {341reflect_component.remove(entity);342} else if let Some(reflect_bundle) = type_registration.data::<ReflectBundle>() {343reflect_bundle.remove(entity);344}345}346347#[cfg(test)]348mod tests {349use crate::{350bundle::Bundle,351component::Component,352prelude::{AppTypeRegistry, ReflectComponent},353reflect::{ReflectBundle, ReflectCommandExt},354system::{Commands, SystemState},355world::World,356};357use alloc::{borrow::ToOwned, boxed::Box};358use bevy_ecs_macros::Resource;359use bevy_reflect::{PartialReflect, Reflect, TypeRegistry};360361#[derive(Resource)]362struct TypeRegistryResource {363type_registry: TypeRegistry,364}365366impl AsRef<TypeRegistry> for TypeRegistryResource {367fn as_ref(&self) -> &TypeRegistry {368&self.type_registry369}370}371372#[derive(Component, Reflect, Default, PartialEq, Eq, Debug)]373#[reflect(Component)]374struct ComponentA(u32);375376#[derive(Component, Reflect, Default, PartialEq, Eq, Debug)]377#[reflect(Component)]378struct ComponentB(u32);379380#[derive(Bundle, Reflect, Default, Debug, PartialEq)]381#[reflect(Bundle)]382struct BundleA {383a: ComponentA,384b: ComponentB,385}386387#[test]388fn insert_reflected() {389let mut world = World::new();390391let type_registry = AppTypeRegistry::default();392{393let mut registry = type_registry.write();394registry.register::<ComponentA>();395registry.register_type_data::<ComponentA, ReflectComponent>();396}397world.insert_resource(type_registry);398399let mut system_state: SystemState<Commands> = SystemState::new(&mut world);400let mut commands = system_state.get_mut(&mut world);401402let entity = commands.spawn_empty().id();403let entity2 = commands.spawn_empty().id();404let entity3 = commands.spawn_empty().id();405406let boxed_reflect_component_a = Box::new(ComponentA(916)) as Box<dyn PartialReflect>;407let boxed_reflect_component_a_clone = boxed_reflect_component_a.reflect_clone().unwrap();408let boxed_reflect_component_a_dynamic = boxed_reflect_component_a.to_dynamic();409410commands411.entity(entity)412.insert_reflect(boxed_reflect_component_a);413commands414.entity(entity2)415.insert_reflect(boxed_reflect_component_a_clone.into_partial_reflect());416commands417.entity(entity3)418.insert_reflect(boxed_reflect_component_a_dynamic);419system_state.apply(&mut world);420421assert_eq!(422world.entity(entity).get::<ComponentA>(),423world.entity(entity2).get::<ComponentA>(),424);425assert_eq!(426world.entity(entity).get::<ComponentA>(),427world.entity(entity3).get::<ComponentA>(),428);429}430431#[test]432fn insert_reflected_with_registry() {433let mut world = World::new();434435let mut type_registry = TypeRegistryResource {436type_registry: TypeRegistry::new(),437};438439type_registry.type_registry.register::<ComponentA>();440type_registry441.type_registry442.register_type_data::<ComponentA, ReflectComponent>();443world.insert_resource(type_registry);444445let mut system_state: SystemState<Commands> = SystemState::new(&mut world);446let mut commands = system_state.get_mut(&mut world);447448let entity = commands.spawn_empty().id();449450let boxed_reflect_component_a = Box::new(ComponentA(916)) as Box<dyn PartialReflect>;451452commands453.entity(entity)454.insert_reflect_with_registry::<TypeRegistryResource>(boxed_reflect_component_a);455system_state.apply(&mut world);456457assert_eq!(458world.entity(entity).get::<ComponentA>(),459Some(&ComponentA(916))460);461}462463#[test]464fn remove_reflected() {465let mut world = World::new();466467let type_registry = AppTypeRegistry::default();468{469let mut registry = type_registry.write();470registry.register::<ComponentA>();471registry.register_type_data::<ComponentA, ReflectComponent>();472}473world.insert_resource(type_registry);474475let mut system_state: SystemState<Commands> = SystemState::new(&mut world);476let mut commands = system_state.get_mut(&mut world);477478let entity = commands.spawn(ComponentA(0)).id();479480let boxed_reflect_component_a = Box::new(ComponentA(916)) as Box<dyn Reflect>;481482commands483.entity(entity)484.remove_reflect(boxed_reflect_component_a.reflect_type_path().to_owned());485system_state.apply(&mut world);486487assert_eq!(world.entity(entity).get::<ComponentA>(), None);488}489490#[test]491fn remove_reflected_with_registry() {492let mut world = World::new();493494let mut type_registry = TypeRegistryResource {495type_registry: TypeRegistry::new(),496};497498type_registry.type_registry.register::<ComponentA>();499type_registry500.type_registry501.register_type_data::<ComponentA, ReflectComponent>();502world.insert_resource(type_registry);503504let mut system_state: SystemState<Commands> = SystemState::new(&mut world);505let mut commands = system_state.get_mut(&mut world);506507let entity = commands.spawn(ComponentA(0)).id();508509let boxed_reflect_component_a = Box::new(ComponentA(916)) as Box<dyn Reflect>;510511commands512.entity(entity)513.remove_reflect_with_registry::<TypeRegistryResource>(514boxed_reflect_component_a.reflect_type_path().to_owned(),515);516system_state.apply(&mut world);517518assert_eq!(world.entity(entity).get::<ComponentA>(), None);519}520521#[test]522fn insert_reflect_bundle() {523let mut world = World::new();524525let type_registry = AppTypeRegistry::default();526{527let mut registry = type_registry.write();528registry.register::<BundleA>();529registry.register_type_data::<BundleA, ReflectBundle>();530}531world.insert_resource(type_registry);532533let mut system_state: SystemState<Commands> = SystemState::new(&mut world);534let mut commands = system_state.get_mut(&mut world);535536let entity = commands.spawn_empty().id();537let bundle = Box::new(BundleA {538a: ComponentA(31),539b: ComponentB(20),540}) as Box<dyn PartialReflect>;541commands.entity(entity).insert_reflect(bundle);542543system_state.apply(&mut world);544545assert_eq!(world.get::<ComponentA>(entity), Some(&ComponentA(31)));546assert_eq!(world.get::<ComponentB>(entity), Some(&ComponentB(20)));547}548549#[test]550fn insert_reflect_bundle_with_registry() {551let mut world = World::new();552553let mut type_registry = TypeRegistryResource {554type_registry: TypeRegistry::new(),555};556557type_registry.type_registry.register::<BundleA>();558type_registry559.type_registry560.register_type_data::<BundleA, ReflectBundle>();561world.insert_resource(type_registry);562563let mut system_state: SystemState<Commands> = SystemState::new(&mut world);564let mut commands = system_state.get_mut(&mut world);565566let entity = commands.spawn_empty().id();567let bundle = Box::new(BundleA {568a: ComponentA(31),569b: ComponentB(20),570}) as Box<dyn PartialReflect>;571572commands573.entity(entity)574.insert_reflect_with_registry::<TypeRegistryResource>(bundle);575system_state.apply(&mut world);576577assert_eq!(world.get::<ComponentA>(entity), Some(&ComponentA(31)));578assert_eq!(world.get::<ComponentB>(entity), Some(&ComponentB(20)));579}580581#[test]582fn remove_reflected_bundle() {583let mut world = World::new();584585let type_registry = AppTypeRegistry::default();586{587let mut registry = type_registry.write();588registry.register::<BundleA>();589registry.register_type_data::<BundleA, ReflectBundle>();590}591world.insert_resource(type_registry);592593let mut system_state: SystemState<Commands> = SystemState::new(&mut world);594let mut commands = system_state.get_mut(&mut world);595596let entity = commands597.spawn(BundleA {598a: ComponentA(31),599b: ComponentB(20),600})601.id();602603let boxed_reflect_bundle_a = Box::new(BundleA {604a: ComponentA(1),605b: ComponentB(23),606}) as Box<dyn Reflect>;607608commands609.entity(entity)610.remove_reflect(boxed_reflect_bundle_a.reflect_type_path().to_owned());611system_state.apply(&mut world);612613assert_eq!(world.entity(entity).get::<ComponentA>(), None);614assert_eq!(world.entity(entity).get::<ComponentB>(), None);615}616617#[test]618fn remove_reflected_bundle_with_registry() {619let mut world = World::new();620621let mut type_registry = TypeRegistryResource {622type_registry: TypeRegistry::new(),623};624625type_registry.type_registry.register::<BundleA>();626type_registry627.type_registry628.register_type_data::<BundleA, ReflectBundle>();629world.insert_resource(type_registry);630631let mut system_state: SystemState<Commands> = SystemState::new(&mut world);632let mut commands = system_state.get_mut(&mut world);633634let entity = commands635.spawn(BundleA {636a: ComponentA(31),637b: ComponentB(20),638})639.id();640641let boxed_reflect_bundle_a = Box::new(BundleA {642a: ComponentA(1),643b: ComponentB(23),644}) as Box<dyn Reflect>;645646commands647.entity(entity)648.remove_reflect_with_registry::<TypeRegistryResource>(649boxed_reflect_bundle_a.reflect_type_path().to_owned(),650);651system_state.apply(&mut world);652653assert_eq!(world.entity(entity).get::<ComponentA>(), None);654assert_eq!(world.entity(entity).get::<ComponentB>(), None);655}656}657658659