mirror of
https://github.com/bevyengine/bevy
synced 2024-12-19 09:33:06 +00:00
Entity cloning (#16132)
## Objective Fixes #1515 This PR implements a flexible entity cloning system. The primary use case for it is to clone dynamically-generated entities. Example: ```rs #[derive(Component, Clone)] pub struct Projectile; #[derive(Component, Clone)] pub struct Damage { value: f32, } fn player_input( mut commands: Commands, projectiles: Query<Entity, With<Projectile>>, input: Res<ButtonInput<KeyCode>>, ) { // Fire a projectile if input.just_pressed(KeyCode::KeyF) { commands.spawn((Projectile, Damage { value: 10.0 })); } // Triplicate all active projectiles if input.just_pressed(KeyCode::KeyT) { for projectile in projectiles.iter() { // To triplicate a projectile we need to create 2 more clones for _ in 0..2{ commands.clone_entity(projectile) } } } } ``` ## Solution ### Commands Add a `clone_entity` command to create a clone of an entity with all components that can be cloned. Components that can't be cloned will be ignored. ```rs commands.clone_entity(entity) ``` If there is a need to configure the cloning process (like set to clone recursively), there is a second command: ```rs commands.clone_entity_with(entity, |builder| { builder.recursive(true) }); ``` Both of these commands return `EntityCommands` of the cloned entity, so the copy can be modified afterwards. ### Builder All these commands use `EntityCloneBuilder` internally. If there is a need to clone an entity using `World` instead, it is also possible: ```rs let entity = world.spawn(Component).id(); let entity_clone = world.spawn_empty().id(); EntityCloneBuilder::new(&mut world).clone_entity(entity, entity_clone); ``` Builder has methods to `allow` or `deny` certain components during cloning if required and can be extended by implementing traits on it. This PR includes two `EntityCloneBuilder` extensions: `CloneEntityWithObserversExt` to configure adding cloned entity to observers of the original entity, and `CloneEntityRecursiveExt` to configure cloning an entity recursively. ### Clone implementations By default, all components that implement either `Clone` or `Reflect` will be cloned (with `Clone`-based implementation preferred in case component implements both). This can be overriden on a per-component basis: ```rs impl Component for SomeComponent { const STORAGE_TYPE: StorageType = StorageType::Table; fn get_component_clone_handler() -> ComponentCloneHandler { // Don't clone this component ComponentCloneHandler::Ignore } } ``` ### `ComponentCloneHandlers` Clone implementation specified in `get_component_clone_handler` will get registered in `ComponentCloneHandlers` (stored in `bevy_ecs::component::Components`) at component registration time. The clone handler implementation provided by a component can be overriden after registration like so: ```rs let component_id = world.components().component_id::<Component>().unwrap() world.get_component_clone_handlers_mut() .set_component_handler(component_id, ComponentCloneHandler::Custom(component_clone_custom)) ``` The default clone handler for all components that do not explicitly define one (or don't derive `Component`) is `component_clone_via_reflect` if `bevy_reflect` feature is enabled, and `component_clone_ignore` (noop) otherwise. Default handler can be overriden using `ComponentCloneHandlers::set_default_handler` ### Handlers Component clone handlers can be used to modify component cloning behavior. The general signature for a handler that can be used in `ComponentCloneHandler::Custom` is as follows: ```rs pub fn component_clone_custom( world: &mut DeferredWorld, entity_cloner: &EntityCloner, ) { // implementation } ``` The `EntityCloner` implementation (used internally by `EntityCloneBuilder`) assumes that after calling this custom handler, the `target` entity has the desired version of the component from the `source` entity. ### Builder handler overrides Besides component-defined and world-overriden handlers, `EntityCloneBuilder` also has a way to override handlers locally. It is mainly used to allow configuration methods like `recursive` and `add_observers`. ```rs // From observer clone handler implementation impl CloneEntityWithObserversExt for EntityCloneBuilder<'_> { fn add_observers(&mut self, add_observers: bool) -> &mut Self { if add_observers { self.override_component_clone_handler::<ObservedBy>(ComponentCloneHandler::Custom( component_clone_observed_by, )) } else { self.remove_component_clone_handler_override::<ObservedBy>() } } } ``` ## Testing Includes some basic functionality tests and doctests. Performance-wise this feature is the same as calling `clone` followed by `insert` for every entity component. There is also some inherent overhead due to every component clone handler having to access component data through `World`, but this can be reduced without breaking current public API in a later PR.
This commit is contained in:
parent
d221665386
commit
2e267bba5a
12 changed files with 1111 additions and 14 deletions
|
@ -163,6 +163,12 @@ pub fn derive_component(input: TokenStream) -> TokenStream {
|
|||
#on_replace
|
||||
#on_remove
|
||||
}
|
||||
|
||||
fn get_component_clone_handler() -> #bevy_ecs_path::component::ComponentCloneHandler {
|
||||
use #bevy_ecs_path::component::{ComponentCloneViaClone, ComponentCloneBase};
|
||||
(&&&#bevy_ecs_path::component::ComponentCloneSpecializationWrapper::<Self>::default())
|
||||
.get_component_clone_handler()
|
||||
}
|
||||
}
|
||||
})
|
||||
}
|
||||
|
|
|
@ -5,7 +5,7 @@ use crate::{
|
|||
archetype::ArchetypeFlags,
|
||||
bundle::BundleInfo,
|
||||
change_detection::MAX_CHANGE_AGE,
|
||||
entity::Entity,
|
||||
entity::{Entity, EntityCloner},
|
||||
query::DebugCheckedUnwrap,
|
||||
storage::{SparseSetIndex, SparseSets, Storages, Table, TableRow},
|
||||
system::{Local, Resource, SystemParam},
|
||||
|
@ -390,6 +390,13 @@ pub trait Component: Send + Sync + 'static {
|
|||
_inheritance_depth: u16,
|
||||
) {
|
||||
}
|
||||
|
||||
/// Called when registering this component, allowing to override clone function (or disable cloning altogether) for this component.
|
||||
///
|
||||
/// See [Handlers section of `EntityCloneBuilder`](crate::entity::EntityCloneBuilder#handlers) to understand how this affects handler priority.
|
||||
fn get_component_clone_handler() -> ComponentCloneHandler {
|
||||
ComponentCloneHandler::default()
|
||||
}
|
||||
}
|
||||
|
||||
/// The storage used for a specific component type.
|
||||
|
@ -875,12 +882,96 @@ impl ComponentDescriptor {
|
|||
}
|
||||
}
|
||||
|
||||
/// Function type that can be used to clone an entity.
|
||||
pub type ComponentCloneFn = fn(&mut DeferredWorld, &EntityCloner);
|
||||
|
||||
/// An enum instructing how to clone a component.
|
||||
#[derive(Debug, Default)]
|
||||
pub enum ComponentCloneHandler {
|
||||
#[default]
|
||||
/// Use the global default function to clone the component with this handler.
|
||||
Default,
|
||||
/// Do not clone the component. When a command to clone an entity is issued, component with this handler will be skipped.
|
||||
Ignore,
|
||||
/// Set a custom handler for the component.
|
||||
Custom(ComponentCloneFn),
|
||||
}
|
||||
|
||||
/// A registry of component clone handlers. Allows to set global default and per-component clone function for all components in the world.
|
||||
#[derive(Debug)]
|
||||
pub struct ComponentCloneHandlers {
|
||||
handlers: Vec<Option<ComponentCloneFn>>,
|
||||
default_handler: ComponentCloneFn,
|
||||
}
|
||||
|
||||
impl ComponentCloneHandlers {
|
||||
/// Sets the default handler for this registry. All components with [`Default`](ComponentCloneHandler::Default) handler, as well as any component that does not have an
|
||||
/// explicitly registered clone function will use this handler.
|
||||
///
|
||||
/// See [Handlers section of `EntityCloneBuilder`](crate::entity::EntityCloneBuilder#handlers) to understand how this affects handler priority.
|
||||
pub fn set_default_handler(&mut self, handler: ComponentCloneFn) {
|
||||
self.default_handler = handler;
|
||||
}
|
||||
|
||||
/// Returns the currently registered default handler.
|
||||
pub fn get_default_handler(&self) -> ComponentCloneFn {
|
||||
self.default_handler
|
||||
}
|
||||
|
||||
/// Sets a handler for a specific component.
|
||||
///
|
||||
/// See [Handlers section of `EntityCloneBuilder`](crate::entity::EntityCloneBuilder#handlers) to understand how this affects handler priority.
|
||||
pub fn set_component_handler(&mut self, id: ComponentId, handler: ComponentCloneHandler) {
|
||||
if id.0 >= self.handlers.len() {
|
||||
self.handlers.resize(id.0 + 1, None);
|
||||
}
|
||||
match handler {
|
||||
ComponentCloneHandler::Default => self.handlers[id.0] = None,
|
||||
ComponentCloneHandler::Ignore => self.handlers[id.0] = Some(component_clone_ignore),
|
||||
ComponentCloneHandler::Custom(handler) => self.handlers[id.0] = Some(handler),
|
||||
};
|
||||
}
|
||||
|
||||
/// Checks if the specified component is registered. If not, the component will use the default global handler.
|
||||
///
|
||||
/// This will return an incorrect result if `id` did not come from the same world as `self`.
|
||||
pub fn is_handler_registered(&self, id: ComponentId) -> bool {
|
||||
self.handlers.get(id.0).is_some_and(Option::is_some)
|
||||
}
|
||||
|
||||
/// Gets a handler to clone a component. This can be one of the following:
|
||||
/// - Custom clone function for this specific component.
|
||||
/// - Default global handler.
|
||||
/// - A [`component_clone_ignore`] (no cloning).
|
||||
///
|
||||
/// This will return an incorrect result if `id` did not come from the same world as `self`.
|
||||
pub fn get_handler(&self, id: ComponentId) -> ComponentCloneFn {
|
||||
match self.handlers.get(id.0) {
|
||||
Some(Some(handler)) => *handler,
|
||||
Some(None) | None => self.default_handler,
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
impl Default for ComponentCloneHandlers {
|
||||
fn default() -> Self {
|
||||
Self {
|
||||
handlers: Default::default(),
|
||||
#[cfg(feature = "bevy_reflect")]
|
||||
default_handler: component_clone_via_reflect,
|
||||
#[cfg(not(feature = "bevy_reflect"))]
|
||||
default_handler: component_clone_ignore,
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
/// Stores metadata associated with each kind of [`Component`] in a given [`World`].
|
||||
#[derive(Debug, Default)]
|
||||
pub struct Components {
|
||||
components: Vec<ComponentInfo>,
|
||||
indices: TypeIdMap<ComponentId>,
|
||||
resource_indices: TypeIdMap<ComponentId>,
|
||||
component_clone_handlers: ComponentCloneHandlers,
|
||||
}
|
||||
|
||||
impl Components {
|
||||
|
@ -918,6 +1009,9 @@ impl Components {
|
|||
let info = &mut self.components[id.index()];
|
||||
T::register_component_hooks(&mut info.hooks);
|
||||
info.required_components = required_components;
|
||||
let clone_handler = T::get_component_clone_handler();
|
||||
self.component_clone_handlers
|
||||
.set_component_handler(id, clone_handler);
|
||||
}
|
||||
id
|
||||
}
|
||||
|
@ -1276,6 +1370,16 @@ impl Components {
|
|||
.map(|info| &mut info.required_by)
|
||||
}
|
||||
|
||||
/// Retrieves the [`ComponentCloneHandlers`]. Can be used to get clone functions for components.
|
||||
pub fn get_component_clone_handlers(&self) -> &ComponentCloneHandlers {
|
||||
&self.component_clone_handlers
|
||||
}
|
||||
|
||||
/// Retrieves a mutable reference to the [`ComponentCloneHandlers`]. Can be used to set and update clone functions for components.
|
||||
pub fn get_component_clone_handlers_mut(&mut self) -> &mut ComponentCloneHandlers {
|
||||
&mut self.component_clone_handlers
|
||||
}
|
||||
|
||||
/// Type-erased equivalent of [`Components::component_id()`].
|
||||
#[inline]
|
||||
pub fn get_id(&self, type_id: TypeId) -> Option<ComponentId> {
|
||||
|
@ -1853,3 +1957,98 @@ impl RequiredComponents {
|
|||
}
|
||||
}
|
||||
}
|
||||
|
||||
/// Component [clone handler function](ComponentCloneFn) implemented using the [`Clone`] trait.
|
||||
/// Can be [set](ComponentCloneHandlers::set_component_handler) as clone handler for the specific component it is implemented for.
|
||||
/// It will panic if set as handler for any other component.
|
||||
///
|
||||
/// See [`ComponentCloneHandlers`] for more details.
|
||||
pub fn component_clone_via_clone<C: Clone + Component>(
|
||||
world: &mut DeferredWorld,
|
||||
entity_cloner: &EntityCloner,
|
||||
) {
|
||||
let component = world
|
||||
.entity(entity_cloner.source())
|
||||
.get::<C>()
|
||||
.expect("Component must exists on source entity")
|
||||
.clone();
|
||||
world
|
||||
.commands()
|
||||
.entity(entity_cloner.target())
|
||||
.insert(component);
|
||||
}
|
||||
|
||||
/// Component [clone handler function](ComponentCloneFn) implemented using reflect.
|
||||
/// Can be [set](ComponentCloneHandlers::set_component_handler) as clone handler for any registered component,
|
||||
/// but only reflected components will be cloned.
|
||||
///
|
||||
/// See [`ComponentCloneHandlers`] for more details.
|
||||
#[cfg(feature = "bevy_reflect")]
|
||||
pub fn component_clone_via_reflect(world: &mut DeferredWorld, entity_cloner: &EntityCloner) {
|
||||
let component_id = entity_cloner.component_id();
|
||||
let source = entity_cloner.source();
|
||||
let target = entity_cloner.target();
|
||||
world.commands().queue(move |world: &mut World| {
|
||||
world.resource_scope::<crate::reflect::AppTypeRegistry, ()>(|world, registry| {
|
||||
let registry = registry.read();
|
||||
|
||||
let component_info = world
|
||||
.components()
|
||||
.get_info(component_id)
|
||||
.expect("Component must be registered");
|
||||
let Some(type_id) = component_info.type_id() else {
|
||||
return;
|
||||
};
|
||||
let Some(reflect_component) =
|
||||
registry.get_type_data::<crate::reflect::ReflectComponent>(type_id)
|
||||
else {
|
||||
return;
|
||||
};
|
||||
let source_component = reflect_component
|
||||
.reflect(world.get_entity(source).expect("Source entity must exist"))
|
||||
.expect("Source entity must have reflected component")
|
||||
.clone_value();
|
||||
let mut target = world
|
||||
.get_entity_mut(target)
|
||||
.expect("Target entity must exist");
|
||||
reflect_component.apply_or_insert(&mut target, &*source_component, ®istry);
|
||||
});
|
||||
});
|
||||
}
|
||||
|
||||
/// Noop implementation of component clone handler function.
|
||||
///
|
||||
/// See [`ComponentCloneHandlers`] for more details.
|
||||
pub fn component_clone_ignore(_world: &mut DeferredWorld, _entity_cloner: &EntityCloner) {}
|
||||
|
||||
/// Wrapper for components clone specialization using autoderef.
|
||||
#[doc(hidden)]
|
||||
pub struct ComponentCloneSpecializationWrapper<T>(PhantomData<T>);
|
||||
|
||||
impl<T> Default for ComponentCloneSpecializationWrapper<T> {
|
||||
fn default() -> Self {
|
||||
Self(PhantomData)
|
||||
}
|
||||
}
|
||||
|
||||
/// Base trait for components clone specialization using autoderef.
|
||||
#[doc(hidden)]
|
||||
pub trait ComponentCloneBase {
|
||||
fn get_component_clone_handler(&self) -> ComponentCloneHandler;
|
||||
}
|
||||
impl<C: Component> ComponentCloneBase for ComponentCloneSpecializationWrapper<C> {
|
||||
fn get_component_clone_handler(&self) -> ComponentCloneHandler {
|
||||
ComponentCloneHandler::default()
|
||||
}
|
||||
}
|
||||
|
||||
/// Specialized trait for components clone specialization using autoderef.
|
||||
#[doc(hidden)]
|
||||
pub trait ComponentCloneViaClone {
|
||||
fn get_component_clone_handler(&self) -> ComponentCloneHandler;
|
||||
}
|
||||
impl<C: Clone + Component> ComponentCloneViaClone for &ComponentCloneSpecializationWrapper<C> {
|
||||
fn get_component_clone_handler(&self) -> ComponentCloneHandler {
|
||||
ComponentCloneHandler::Custom(component_clone_via_clone::<C>)
|
||||
}
|
||||
}
|
||||
|
|
522
crates/bevy_ecs/src/entity/clone_entities.rs
Normal file
522
crates/bevy_ecs/src/entity/clone_entities.rs
Normal file
|
@ -0,0 +1,522 @@
|
|||
use alloc::sync::Arc;
|
||||
use core::any::TypeId;
|
||||
|
||||
use bevy_utils::{HashMap, HashSet};
|
||||
|
||||
use crate::{
|
||||
bundle::Bundle,
|
||||
component::{component_clone_ignore, Component, ComponentCloneHandler, ComponentId},
|
||||
entity::Entity,
|
||||
world::World,
|
||||
};
|
||||
|
||||
/// A helper struct to clone an entity. Used internally by [`EntityCloneBuilder::clone_entity`] and custom clone handlers.
|
||||
pub struct EntityCloner {
|
||||
source: Entity,
|
||||
target: Entity,
|
||||
component_id: Option<ComponentId>,
|
||||
filter_allows_components: bool,
|
||||
filter: Arc<HashSet<ComponentId>>,
|
||||
clone_handlers_overrides: Arc<HashMap<ComponentId, ComponentCloneHandler>>,
|
||||
}
|
||||
|
||||
impl EntityCloner {
|
||||
/// Clones and inserts components from the `source` entity into `target` entity using the stored configuration.
|
||||
pub fn clone_entity(&mut self, world: &mut World) {
|
||||
let source_entity = world
|
||||
.get_entity(self.source)
|
||||
.expect("Source entity must exist");
|
||||
let archetype = source_entity.archetype();
|
||||
|
||||
let mut components = Vec::with_capacity(archetype.component_count());
|
||||
components.extend(
|
||||
archetype
|
||||
.components()
|
||||
.filter(|id| self.is_cloning_allowed(id)),
|
||||
);
|
||||
|
||||
for component in components {
|
||||
let global_handlers = world.components().get_component_clone_handlers();
|
||||
let handler = match self.clone_handlers_overrides.get(&component) {
|
||||
None => global_handlers.get_handler(component),
|
||||
Some(ComponentCloneHandler::Default) => global_handlers.get_default_handler(),
|
||||
Some(ComponentCloneHandler::Ignore) => component_clone_ignore,
|
||||
Some(ComponentCloneHandler::Custom(handler)) => *handler,
|
||||
};
|
||||
self.component_id = Some(component);
|
||||
(handler)(&mut world.into(), self);
|
||||
}
|
||||
}
|
||||
|
||||
fn is_cloning_allowed(&self, component: &ComponentId) -> bool {
|
||||
(self.filter_allows_components && self.filter.contains(component))
|
||||
|| (!self.filter_allows_components && !self.filter.contains(component))
|
||||
}
|
||||
|
||||
/// Returns the current source entity.
|
||||
pub fn source(&self) -> Entity {
|
||||
self.source
|
||||
}
|
||||
|
||||
/// Returns the current target entity.
|
||||
pub fn target(&self) -> Entity {
|
||||
self.target
|
||||
}
|
||||
|
||||
/// Returns the [`ComponentId`] of currently cloned component.
|
||||
pub fn component_id(&self) -> ComponentId {
|
||||
self.component_id
|
||||
.expect("ComponentId must be set in clone_entity")
|
||||
}
|
||||
|
||||
/// Reuse existing [`EntityCloner`] configuration with new source and target.
|
||||
pub fn with_source_and_target(&self, source: Entity, target: Entity) -> EntityCloner {
|
||||
EntityCloner {
|
||||
source,
|
||||
target,
|
||||
filter: self.filter.clone(),
|
||||
clone_handlers_overrides: self.clone_handlers_overrides.clone(),
|
||||
..*self
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
/// Builder struct to clone an entity. Allows configuring which components to clone, as well as how to clone them.
|
||||
/// After configuration is complete an entity can be cloned using [`Self::clone_entity`].
|
||||
///
|
||||
///```
|
||||
/// use bevy_ecs::prelude::*;
|
||||
/// use bevy_ecs::entity::EntityCloneBuilder;
|
||||
///
|
||||
/// #[derive(Component, Clone, PartialEq, Eq)]
|
||||
/// struct A {
|
||||
/// field: usize,
|
||||
/// }
|
||||
///
|
||||
/// let mut world = World::default();
|
||||
///
|
||||
/// let component = A { field: 5 };
|
||||
///
|
||||
/// let entity = world.spawn(component.clone()).id();
|
||||
/// let entity_clone = world.spawn_empty().id();
|
||||
///
|
||||
/// EntityCloneBuilder::new(&mut world).clone_entity(entity, entity_clone);
|
||||
///
|
||||
/// assert!(world.get::<A>(entity_clone).is_some_and(|c| *c == component));
|
||||
///```
|
||||
///
|
||||
/// # Default cloning strategy
|
||||
/// By default, all types that derive [`Component`] and implement either [`Clone`] or `Reflect` (with `ReflectComponent`) will be cloned
|
||||
/// (with `Clone`-based implementation preferred in case component implements both).
|
||||
///
|
||||
/// It should be noted that if `Component` is implemented manually or if `Clone` implementation is conditional
|
||||
/// (like when deriving `Clone` for a type with a generic parameter without `Clone` bound),
|
||||
/// the component will be cloned using the [default cloning strategy](crate::component::ComponentCloneHandlers::get_default_handler).
|
||||
/// To use `Clone`-based handler ([`component_clone_via_clone`](crate::component::component_clone_via_clone)) in this case it should be set manually using one
|
||||
/// of the methods mentioned in the [Handlers](#handlers) section
|
||||
///
|
||||
/// Here's an example of how to do it using [`get_component_clone_handler`](Component::get_component_clone_handler):
|
||||
/// ```
|
||||
/// # use bevy_ecs::prelude::*;
|
||||
/// # use bevy_ecs::component::{StorageType, component_clone_via_clone, ComponentCloneHandler};
|
||||
/// #[derive(Clone)]
|
||||
/// struct SomeComponent;
|
||||
///
|
||||
/// impl Component for SomeComponent {
|
||||
/// const STORAGE_TYPE: StorageType = StorageType::Table;
|
||||
/// fn get_component_clone_handler() -> ComponentCloneHandler {
|
||||
/// ComponentCloneHandler::Custom(component_clone_via_clone::<Self>)
|
||||
/// }
|
||||
/// }
|
||||
/// ```
|
||||
///
|
||||
/// # Handlers
|
||||
/// `EntityCloneBuilder` clones entities by cloning components using [`handlers`](ComponentCloneHandler), and there are multiple layers
|
||||
/// to decide which handler to use for which component. The overall hierarchy looks like this (priority from most to least):
|
||||
/// 1. local overrides using [`override_component_clone_handler`](Self::override_component_clone_handler)
|
||||
/// 2. global overrides using [`set_component_handler`](crate::component::ComponentCloneHandlers::set_component_handler)
|
||||
/// 3. component-defined handler using [`get_component_clone_handler`](Component::get_component_clone_handler)
|
||||
/// 4. default handler override using [`set_default_handler`](crate::component::ComponentCloneHandlers::set_default_handler)
|
||||
/// 5. reflect-based or noop default clone handler depending on if `bevy_reflect` feature is enabled or not.
|
||||
#[derive(Debug)]
|
||||
pub struct EntityCloneBuilder<'w> {
|
||||
world: &'w mut World,
|
||||
filter_allows_components: bool,
|
||||
filter: HashSet<ComponentId>,
|
||||
clone_handlers_overrides: HashMap<ComponentId, ComponentCloneHandler>,
|
||||
}
|
||||
|
||||
impl<'w> EntityCloneBuilder<'w> {
|
||||
/// Creates a new [`EntityCloneBuilder`] for world.
|
||||
pub fn new(world: &'w mut World) -> Self {
|
||||
Self {
|
||||
world,
|
||||
filter_allows_components: false,
|
||||
filter: Default::default(),
|
||||
clone_handlers_overrides: Default::default(),
|
||||
}
|
||||
}
|
||||
|
||||
/// Finishes configuring the builder and clones `source` entity to `target`.
|
||||
pub fn clone_entity(self, source: Entity, target: Entity) {
|
||||
let EntityCloneBuilder {
|
||||
world,
|
||||
filter_allows_components,
|
||||
filter,
|
||||
clone_handlers_overrides,
|
||||
..
|
||||
} = self;
|
||||
|
||||
EntityCloner {
|
||||
source,
|
||||
target,
|
||||
component_id: None,
|
||||
filter_allows_components,
|
||||
filter: Arc::new(filter),
|
||||
clone_handlers_overrides: Arc::new(clone_handlers_overrides),
|
||||
}
|
||||
.clone_entity(world);
|
||||
|
||||
world.flush_commands();
|
||||
}
|
||||
|
||||
/// Adds all components of the bundle to the list of components to clone.
|
||||
///
|
||||
/// Note that all components are allowed by default, to clone only explicitly allowed components make sure to call
|
||||
/// [`deny_all`](`Self::deny_all`) before calling any of the `allow` methods.
|
||||
pub fn allow<T: Bundle>(&mut self) -> &mut Self {
|
||||
if self.filter_allows_components {
|
||||
T::get_component_ids(self.world.components(), &mut |id| {
|
||||
if let Some(id) = id {
|
||||
self.filter.insert(id);
|
||||
}
|
||||
});
|
||||
} else {
|
||||
T::get_component_ids(self.world.components(), &mut |id| {
|
||||
if let Some(id) = id {
|
||||
self.filter.remove(&id);
|
||||
}
|
||||
});
|
||||
}
|
||||
self
|
||||
}
|
||||
|
||||
/// Extends the list of components to clone.
|
||||
///
|
||||
/// Note that all components are allowed by default, to clone only explicitly allowed components make sure to call
|
||||
/// [`deny_all`](`Self::deny_all`) before calling any of the `allow` methods.
|
||||
pub fn allow_by_ids(&mut self, ids: impl IntoIterator<Item = ComponentId>) -> &mut Self {
|
||||
if self.filter_allows_components {
|
||||
self.filter.extend(ids);
|
||||
} else {
|
||||
ids.into_iter().for_each(|id| {
|
||||
self.filter.remove(&id);
|
||||
});
|
||||
}
|
||||
self
|
||||
}
|
||||
|
||||
/// Extends the list of components to clone using [`TypeId`]s.
|
||||
///
|
||||
/// Note that all components are allowed by default, to clone only explicitly allowed components make sure to call
|
||||
/// [`deny_all`](`Self::deny_all`) before calling any of the `allow` methods.
|
||||
pub fn allow_by_type_ids(&mut self, ids: impl IntoIterator<Item = TypeId>) -> &mut Self {
|
||||
let ids = ids
|
||||
.into_iter()
|
||||
.filter_map(|id| self.world.components().get_id(id));
|
||||
if self.filter_allows_components {
|
||||
self.filter.extend(ids);
|
||||
} else {
|
||||
ids.into_iter().for_each(|id| {
|
||||
self.filter.remove(&id);
|
||||
});
|
||||
}
|
||||
self
|
||||
}
|
||||
|
||||
/// Resets the filter to allow all components to be cloned.
|
||||
pub fn allow_all(&mut self) -> &mut Self {
|
||||
self.filter_allows_components = false;
|
||||
self.filter.clear();
|
||||
self
|
||||
}
|
||||
|
||||
/// Disallows all components of the bundle from being cloned.
|
||||
pub fn deny<T: Bundle>(&mut self) -> &mut Self {
|
||||
if self.filter_allows_components {
|
||||
T::get_component_ids(self.world.components(), &mut |id| {
|
||||
if let Some(id) = id {
|
||||
self.filter.remove(&id);
|
||||
}
|
||||
});
|
||||
} else {
|
||||
T::get_component_ids(self.world.components(), &mut |id| {
|
||||
if let Some(id) = id {
|
||||
self.filter.insert(id);
|
||||
}
|
||||
});
|
||||
}
|
||||
self
|
||||
}
|
||||
|
||||
/// Extends the list of components that shouldn't be cloned.
|
||||
pub fn deny_by_ids(&mut self, ids: impl IntoIterator<Item = ComponentId>) -> &mut Self {
|
||||
if self.filter_allows_components {
|
||||
ids.into_iter().for_each(|id| {
|
||||
self.filter.remove(&id);
|
||||
});
|
||||
} else {
|
||||
self.filter.extend(ids);
|
||||
}
|
||||
self
|
||||
}
|
||||
|
||||
/// Extends the list of components that shouldn't be cloned by type ids.
|
||||
pub fn deny_by_type_ids(&mut self, ids: impl IntoIterator<Item = TypeId>) -> &mut Self {
|
||||
let ids = ids
|
||||
.into_iter()
|
||||
.filter_map(|id| self.world.components().get_id(id));
|
||||
if self.filter_allows_components {
|
||||
ids.into_iter().for_each(|id| {
|
||||
self.filter.remove(&id);
|
||||
});
|
||||
} else {
|
||||
self.filter.extend(ids);
|
||||
}
|
||||
self
|
||||
}
|
||||
|
||||
/// Sets the filter to deny all components.
|
||||
pub fn deny_all(&mut self) -> &mut Self {
|
||||
self.filter_allows_components = true;
|
||||
self.filter.clear();
|
||||
self
|
||||
}
|
||||
|
||||
/// Overrides the [`ComponentCloneHandler`] for a component in this builder.
|
||||
/// This handler will be used to clone the component instead of the global one defined by [`ComponentCloneHandlers`](crate::component::ComponentCloneHandlers)
|
||||
///
|
||||
/// See [Handlers section of `EntityCloneBuilder`](EntityCloneBuilder#handlers) to understand how this affects handler priority.
|
||||
pub fn override_component_clone_handler<T: Component>(
|
||||
&mut self,
|
||||
handler: ComponentCloneHandler,
|
||||
) -> &mut Self {
|
||||
if let Some(id) = self.world.components().component_id::<T>() {
|
||||
self.clone_handlers_overrides.insert(id, handler);
|
||||
}
|
||||
self
|
||||
}
|
||||
|
||||
/// Removes a previously set override of [`ComponentCloneHandler`] for a component in this builder.
|
||||
pub fn remove_component_clone_handler_override<T: Component>(&mut self) -> &mut Self {
|
||||
if let Some(id) = self.world.components().component_id::<T>() {
|
||||
self.clone_handlers_overrides.remove(&id);
|
||||
}
|
||||
self
|
||||
}
|
||||
}
|
||||
|
||||
#[cfg(test)]
|
||||
mod tests {
|
||||
use crate::{self as bevy_ecs, component::Component, entity::EntityCloneBuilder, world::World};
|
||||
|
||||
#[cfg(feature = "bevy_reflect")]
|
||||
#[test]
|
||||
fn clone_entity_using_reflect() {
|
||||
use crate::reflect::{AppTypeRegistry, ReflectComponent};
|
||||
use bevy_reflect::Reflect;
|
||||
|
||||
#[derive(Component, Reflect, Clone, PartialEq, Eq)]
|
||||
#[reflect(Component)]
|
||||
struct A {
|
||||
field: usize,
|
||||
}
|
||||
|
||||
let mut world = World::default();
|
||||
world.init_resource::<AppTypeRegistry>();
|
||||
let registry = world.get_resource::<AppTypeRegistry>().unwrap();
|
||||
registry.write().register::<A>();
|
||||
|
||||
let component = A { field: 5 };
|
||||
|
||||
let e = world.spawn(component.clone()).id();
|
||||
let e_clone = world.spawn_empty().id();
|
||||
|
||||
EntityCloneBuilder::new(&mut world).clone_entity(e, e_clone);
|
||||
|
||||
assert!(world.get::<A>(e_clone).is_some_and(|c| *c == component));
|
||||
}
|
||||
|
||||
#[test]
|
||||
fn clone_entity_using_clone() {
|
||||
#[derive(Component, Clone, PartialEq, Eq)]
|
||||
struct A {
|
||||
field: usize,
|
||||
}
|
||||
|
||||
let mut world = World::default();
|
||||
|
||||
let component = A { field: 5 };
|
||||
|
||||
let e = world.spawn(component.clone()).id();
|
||||
let e_clone = world.spawn_empty().id();
|
||||
|
||||
EntityCloneBuilder::new(&mut world).clone_entity(e, e_clone);
|
||||
|
||||
assert!(world.get::<A>(e_clone).is_some_and(|c| *c == component));
|
||||
}
|
||||
|
||||
#[cfg(feature = "bevy_reflect")]
|
||||
#[test]
|
||||
fn clone_entity_specialization() {
|
||||
use crate::reflect::{AppTypeRegistry, ReflectComponent};
|
||||
use bevy_reflect::Reflect;
|
||||
|
||||
#[derive(Component, Reflect, PartialEq, Eq)]
|
||||
#[reflect(Component)]
|
||||
struct A {
|
||||
field: usize,
|
||||
}
|
||||
|
||||
impl Clone for A {
|
||||
fn clone(&self) -> Self {
|
||||
Self { field: 10 }
|
||||
}
|
||||
}
|
||||
|
||||
let mut world = World::default();
|
||||
world.init_resource::<AppTypeRegistry>();
|
||||
let registry = world.get_resource::<AppTypeRegistry>().unwrap();
|
||||
registry.write().register::<A>();
|
||||
|
||||
let component = A { field: 5 };
|
||||
|
||||
let e = world.spawn(component.clone()).id();
|
||||
let e_clone = world.spawn_empty().id();
|
||||
|
||||
EntityCloneBuilder::new(&mut world).clone_entity(e, e_clone);
|
||||
|
||||
assert!(world
|
||||
.get::<A>(e_clone)
|
||||
.is_some_and(|comp| *comp == A { field: 10 }));
|
||||
}
|
||||
|
||||
#[test]
|
||||
fn clone_entity_with_allow_filter() {
|
||||
#[derive(Component, Clone, PartialEq, Eq)]
|
||||
struct A {
|
||||
field: usize,
|
||||
}
|
||||
|
||||
#[derive(Component, Clone)]
|
||||
struct B;
|
||||
|
||||
let mut world = World::default();
|
||||
|
||||
let component = A { field: 5 };
|
||||
|
||||
let e = world.spawn((component.clone(), B)).id();
|
||||
let e_clone = world.spawn_empty().id();
|
||||
|
||||
let mut builder = EntityCloneBuilder::new(&mut world);
|
||||
builder.deny_all();
|
||||
builder.allow::<A>();
|
||||
builder.clone_entity(e, e_clone);
|
||||
|
||||
assert!(world.get::<A>(e_clone).is_some_and(|c| *c == component));
|
||||
assert!(world.get::<B>(e_clone).is_none());
|
||||
}
|
||||
|
||||
#[test]
|
||||
fn clone_entity_with_deny_filter() {
|
||||
#[derive(Component, Clone, PartialEq, Eq)]
|
||||
struct A {
|
||||
field: usize,
|
||||
}
|
||||
|
||||
#[derive(Component, Clone)]
|
||||
struct B;
|
||||
|
||||
#[derive(Component, Clone)]
|
||||
struct C;
|
||||
|
||||
let mut world = World::default();
|
||||
|
||||
let component = A { field: 5 };
|
||||
|
||||
let e = world.spawn((component.clone(), B, C)).id();
|
||||
let e_clone = world.spawn_empty().id();
|
||||
|
||||
let mut builder = EntityCloneBuilder::new(&mut world);
|
||||
builder.deny::<B>();
|
||||
builder.clone_entity(e, e_clone);
|
||||
|
||||
assert!(world.get::<A>(e_clone).is_some_and(|c| *c == component));
|
||||
assert!(world.get::<B>(e_clone).is_none());
|
||||
assert!(world.get::<C>(e_clone).is_some());
|
||||
}
|
||||
|
||||
#[test]
|
||||
fn clone_entity_with_override_allow_filter() {
|
||||
#[derive(Component, Clone, PartialEq, Eq)]
|
||||
struct A {
|
||||
field: usize,
|
||||
}
|
||||
|
||||
#[derive(Component, Clone)]
|
||||
struct B;
|
||||
|
||||
#[derive(Component, Clone)]
|
||||
struct C;
|
||||
|
||||
let mut world = World::default();
|
||||
|
||||
let component = A { field: 5 };
|
||||
|
||||
let e = world.spawn((component.clone(), B, C)).id();
|
||||
let e_clone = world.spawn_empty().id();
|
||||
|
||||
let mut builder = EntityCloneBuilder::new(&mut world);
|
||||
builder.deny_all();
|
||||
builder.allow::<A>();
|
||||
builder.allow::<B>();
|
||||
builder.allow::<C>();
|
||||
builder.deny::<B>();
|
||||
builder.clone_entity(e, e_clone);
|
||||
|
||||
assert!(world.get::<A>(e_clone).is_some_and(|c| *c == component));
|
||||
assert!(world.get::<B>(e_clone).is_none());
|
||||
assert!(world.get::<C>(e_clone).is_some());
|
||||
}
|
||||
|
||||
#[test]
|
||||
fn clone_entity_with_override_bundle() {
|
||||
#[derive(Component, Clone, PartialEq, Eq)]
|
||||
struct A {
|
||||
field: usize,
|
||||
}
|
||||
|
||||
#[derive(Component, Clone)]
|
||||
struct B;
|
||||
|
||||
#[derive(Component, Clone)]
|
||||
struct C;
|
||||
|
||||
let mut world = World::default();
|
||||
|
||||
let component = A { field: 5 };
|
||||
|
||||
let e = world.spawn((component.clone(), B, C)).id();
|
||||
let e_clone = world.spawn_empty().id();
|
||||
|
||||
let mut builder = EntityCloneBuilder::new(&mut world);
|
||||
builder.deny_all();
|
||||
builder.allow::<(A, B, C)>();
|
||||
builder.deny::<(B, C)>();
|
||||
builder.clone_entity(e, e_clone);
|
||||
|
||||
assert!(world.get::<A>(e_clone).is_some_and(|c| *c == component));
|
||||
assert!(world.get::<B>(e_clone).is_none());
|
||||
assert!(world.get::<C>(e_clone).is_none());
|
||||
}
|
||||
}
|
|
@ -35,12 +35,14 @@
|
|||
//! [`World::despawn`]: crate::world::World::despawn
|
||||
//! [`EntityWorldMut::insert`]: crate::world::EntityWorldMut::insert
|
||||
//! [`EntityWorldMut::remove`]: crate::world::EntityWorldMut::remove
|
||||
mod clone_entities;
|
||||
mod map_entities;
|
||||
mod visit_entities;
|
||||
#[cfg(feature = "bevy_reflect")]
|
||||
use bevy_reflect::Reflect;
|
||||
#[cfg(all(feature = "bevy_reflect", feature = "serialize"))]
|
||||
use bevy_reflect::{ReflectDeserialize, ReflectSerialize};
|
||||
pub use clone_entities::*;
|
||||
pub use map_entities::*;
|
||||
pub use visit_entities::*;
|
||||
|
||||
|
|
|
@ -49,7 +49,7 @@ pub mod prelude {
|
|||
component::Component,
|
||||
entity::{Entity, EntityMapper},
|
||||
event::{Event, EventMutator, EventReader, EventWriter, Events},
|
||||
observer::{Observer, Trigger},
|
||||
observer::{CloneEntityWithObserversExt, Observer, Trigger},
|
||||
query::{Added, AnyOf, Changed, Has, Or, QueryBuilder, QueryState, With, Without},
|
||||
removal_detection::RemovedComponents,
|
||||
schedule::{
|
||||
|
|
|
@ -1,7 +1,8 @@
|
|||
use crate::{
|
||||
component::{Component, ComponentHooks, StorageType},
|
||||
entity::Entity,
|
||||
component::{Component, ComponentCloneHandler, ComponentHooks, StorageType},
|
||||
entity::{Entity, EntityCloneBuilder, EntityCloner},
|
||||
observer::ObserverState,
|
||||
world::{DeferredWorld, World},
|
||||
};
|
||||
|
||||
/// Tracks a list of entity observers for the [`Entity`] [`ObservedBy`] is added to.
|
||||
|
@ -39,4 +40,110 @@ impl Component for ObservedBy {
|
|||
}
|
||||
});
|
||||
}
|
||||
|
||||
fn get_component_clone_handler() -> ComponentCloneHandler {
|
||||
ComponentCloneHandler::Ignore
|
||||
}
|
||||
}
|
||||
|
||||
/// Trait that holds functions for configuring interaction with observers during entity cloning.
|
||||
pub trait CloneEntityWithObserversExt {
|
||||
/// Sets the option to automatically add cloned entities to the obsevers targeting source entity.
|
||||
fn add_observers(&mut self, add_observers: bool) -> &mut Self;
|
||||
}
|
||||
|
||||
impl CloneEntityWithObserversExt for EntityCloneBuilder<'_> {
|
||||
fn add_observers(&mut self, add_observers: bool) -> &mut Self {
|
||||
if add_observers {
|
||||
self.override_component_clone_handler::<ObservedBy>(ComponentCloneHandler::Custom(
|
||||
component_clone_observed_by,
|
||||
))
|
||||
} else {
|
||||
self.remove_component_clone_handler_override::<ObservedBy>()
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
fn component_clone_observed_by(world: &mut DeferredWorld, entity_cloner: &EntityCloner) {
|
||||
let target = entity_cloner.target();
|
||||
let source = entity_cloner.source();
|
||||
|
||||
world.commands().queue(move |world: &mut World| {
|
||||
let observed_by = world
|
||||
.get::<ObservedBy>(source)
|
||||
.map(|observed_by| observed_by.0.clone())
|
||||
.expect("Source entity must have ObservedBy");
|
||||
|
||||
world
|
||||
.entity_mut(target)
|
||||
.insert(ObservedBy(observed_by.clone()));
|
||||
|
||||
for observer in &observed_by {
|
||||
let mut observer_state = world
|
||||
.get_mut::<ObserverState>(*observer)
|
||||
.expect("Source observer entity must have ObserverState");
|
||||
observer_state.descriptor.entities.push(target);
|
||||
let event_types = observer_state.descriptor.events.clone();
|
||||
let components = observer_state.descriptor.components.clone();
|
||||
for event_type in event_types {
|
||||
let observers = world.observers.get_observers(event_type);
|
||||
if components.is_empty() {
|
||||
if let Some(map) = observers.entity_observers.get(&source).cloned() {
|
||||
observers.entity_observers.insert(target, map);
|
||||
}
|
||||
} else {
|
||||
for component in &components {
|
||||
let Some(observers) = observers.component_observers.get_mut(component)
|
||||
else {
|
||||
continue;
|
||||
};
|
||||
if let Some(map) = observers.entity_map.get(&source).cloned() {
|
||||
observers.entity_map.insert(target, map);
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
});
|
||||
}
|
||||
|
||||
#[cfg(test)]
|
||||
mod tests {
|
||||
use crate::{
|
||||
self as bevy_ecs,
|
||||
entity::EntityCloneBuilder,
|
||||
event::Event,
|
||||
observer::{CloneEntityWithObserversExt, Trigger},
|
||||
system::{ResMut, Resource},
|
||||
world::World,
|
||||
};
|
||||
|
||||
#[derive(Resource, Default)]
|
||||
struct Num(usize);
|
||||
|
||||
#[derive(Event)]
|
||||
struct E;
|
||||
|
||||
#[test]
|
||||
fn clone_entity_with_observer() {
|
||||
let mut world = World::default();
|
||||
world.init_resource::<Num>();
|
||||
|
||||
let e = world
|
||||
.spawn_empty()
|
||||
.observe(|_: Trigger<E>, mut res: ResMut<Num>| res.0 += 1)
|
||||
.id();
|
||||
world.flush();
|
||||
|
||||
world.trigger_targets(E, e);
|
||||
|
||||
let e_clone = world.spawn_empty().id();
|
||||
let mut builder = EntityCloneBuilder::new(&mut world);
|
||||
builder.add_observers(true);
|
||||
builder.clone_entity(e, e_clone);
|
||||
|
||||
world.trigger_targets(E, [e, e_clone]);
|
||||
|
||||
assert_eq!(world.resource::<Num>().0, 3);
|
||||
}
|
||||
}
|
||||
|
|
|
@ -4,6 +4,7 @@ mod entity_observer;
|
|||
mod runner;
|
||||
mod trigger_event;
|
||||
|
||||
pub use entity_observer::CloneEntityWithObserversExt;
|
||||
pub use runner::*;
|
||||
pub use trigger_event::*;
|
||||
|
||||
|
|
|
@ -11,7 +11,7 @@ use crate::{
|
|||
bundle::{Bundle, InsertMode},
|
||||
change_detection::Mut,
|
||||
component::{Component, ComponentId, ComponentInfo},
|
||||
entity::{Entities, Entity},
|
||||
entity::{Entities, Entity, EntityCloneBuilder},
|
||||
event::{Event, SendEvent},
|
||||
observer::{Observer, TriggerEvent, TriggerTargets},
|
||||
schedule::ScheduleLabel,
|
||||
|
@ -271,6 +271,69 @@ impl<'w, 's> Commands<'w, 's> {
|
|||
}
|
||||
}
|
||||
|
||||
/// Clones an entity and allows configuring cloning behavior using [`EntityCloneBuilder`], returning [`EntityCommands`] of the cloned entity.
|
||||
///
|
||||
/// # Example
|
||||
///
|
||||
/// ```
|
||||
/// # use bevy_ecs::prelude::*;
|
||||
///
|
||||
/// #[derive(Component, Clone)]
|
||||
/// struct ComponentA(u32);
|
||||
/// #[derive(Component, Clone)]
|
||||
/// struct ComponentB(u32);
|
||||
///
|
||||
/// fn example_system(mut commands: Commands) {
|
||||
/// // Create a new entity and retrieve its id.
|
||||
/// let entity = commands.spawn((ComponentA(10), ComponentB(20))).id();
|
||||
///
|
||||
/// // Create a clone of the first entity, but without ComponentB
|
||||
/// let entity_clone = commands.clone_entity_with(entity, |builder| {
|
||||
/// builder.deny::<ComponentB>();
|
||||
/// }).id();
|
||||
/// }
|
||||
/// # bevy_ecs::system::assert_is_system(example_system);
|
||||
pub fn clone_entity_with(
|
||||
&mut self,
|
||||
entity: Entity,
|
||||
f: impl FnOnce(&mut EntityCloneBuilder) + Send + Sync + 'static,
|
||||
) -> EntityCommands<'_> {
|
||||
let cloned_entity = self.spawn_empty().id();
|
||||
self.queue(move |world: &mut World| {
|
||||
let mut builder = EntityCloneBuilder::new(world);
|
||||
f(&mut builder);
|
||||
builder.clone_entity(entity, cloned_entity);
|
||||
});
|
||||
EntityCommands {
|
||||
commands: self.reborrow(),
|
||||
entity: cloned_entity,
|
||||
}
|
||||
}
|
||||
|
||||
/// Clones an entity and returns [`EntityCommands`] of the cloned entity.
|
||||
///
|
||||
/// # Example
|
||||
///
|
||||
/// ```
|
||||
/// # use bevy_ecs::prelude::*;
|
||||
///
|
||||
/// #[derive(Component, Clone)]
|
||||
/// struct ComponentA(u32);
|
||||
/// #[derive(Component, Clone)]
|
||||
/// struct ComponentB(u32);
|
||||
///
|
||||
/// fn example_system(mut commands: Commands) {
|
||||
/// // Create a new entity and retrieve its id.
|
||||
/// let entity = commands.spawn((ComponentA(10), ComponentB(20))).id();
|
||||
///
|
||||
/// // Create a clone of the first entity
|
||||
/// let entity_clone = commands.clone_entity(entity).id();
|
||||
/// }
|
||||
/// # bevy_ecs::system::assert_is_system(example_system);
|
||||
pub fn clone_entity(&mut self, entity: Entity) -> EntityCommands<'_> {
|
||||
self.clone_entity_with(entity, |_| {})
|
||||
}
|
||||
|
||||
/// Reserves a new empty [`Entity`] to be spawned, and returns its corresponding [`EntityCommands`].
|
||||
///
|
||||
/// See [`World::spawn_empty`] for more details.
|
||||
|
|
|
@ -34,8 +34,9 @@ use crate::{
|
|||
bundle::{Bundle, BundleInfo, BundleInserter, BundleSpawner, Bundles, InsertMode},
|
||||
change_detection::{MutUntyped, TicksMut},
|
||||
component::{
|
||||
Component, ComponentDescriptor, ComponentHooks, ComponentId, ComponentInfo, ComponentTicks,
|
||||
Components, RequiredComponents, RequiredComponentsError, Tick,
|
||||
Component, ComponentCloneHandlers, ComponentDescriptor, ComponentHooks, ComponentId,
|
||||
ComponentInfo, ComponentTicks, Components, RequiredComponents, RequiredComponentsError,
|
||||
Tick,
|
||||
},
|
||||
entity::{AllocAtWithoutReplacement, Entities, Entity, EntityHashSet, EntityLocation},
|
||||
event::{Event, EventId, Events, SendBatchIds},
|
||||
|
@ -3338,6 +3339,35 @@ impl World {
|
|||
// SAFETY: We just initialized the bundle so its id should definitely be valid.
|
||||
unsafe { self.bundles.get(id).debug_checked_unwrap() }
|
||||
}
|
||||
|
||||
/// Retrieves a mutable reference to the [`ComponentCloneHandlers`]. Can be used to set and update clone functions for components.
|
||||
///
|
||||
/// ```
|
||||
/// # use bevy_ecs::prelude::*;
|
||||
/// use bevy_ecs::component::{ComponentId, ComponentCloneHandler};
|
||||
/// use bevy_ecs::entity::EntityCloner;
|
||||
/// use bevy_ecs::world::DeferredWorld;
|
||||
///
|
||||
/// fn custom_clone_handler(
|
||||
/// _world: &mut DeferredWorld,
|
||||
/// _entity_cloner: &EntityCloner,
|
||||
/// ) {
|
||||
/// // Custom cloning logic for component
|
||||
/// }
|
||||
///
|
||||
/// #[derive(Component)]
|
||||
/// struct ComponentA;
|
||||
///
|
||||
/// let mut world = World::new();
|
||||
///
|
||||
/// let component_id = world.register_component::<ComponentA>();
|
||||
///
|
||||
/// world.get_component_clone_handlers_mut()
|
||||
/// .set_component_handler(component_id, ComponentCloneHandler::Custom(custom_clone_handler))
|
||||
/// ```
|
||||
pub fn get_component_clone_handlers_mut(&mut self) -> &mut ComponentCloneHandlers {
|
||||
self.components.get_component_clone_handlers_mut()
|
||||
}
|
||||
}
|
||||
|
||||
impl World {
|
||||
|
|
|
@ -4,7 +4,7 @@ use bevy_ecs::reflect::{
|
|||
ReflectVisitEntitiesMut,
|
||||
};
|
||||
use bevy_ecs::{
|
||||
component::Component,
|
||||
component::{Component, ComponentCloneHandler, StorageType},
|
||||
entity::{Entity, VisitEntitiesMut},
|
||||
prelude::FromWorld,
|
||||
world::World,
|
||||
|
@ -25,7 +25,7 @@ use smallvec::SmallVec;
|
|||
/// [`Query`]: bevy_ecs::system::Query
|
||||
/// [`Parent`]: crate::components::parent::Parent
|
||||
/// [`BuildChildren::with_children`]: crate::child_builder::BuildChildren::with_children
|
||||
#[derive(Component, Debug, VisitEntitiesMut)]
|
||||
#[derive(Debug, VisitEntitiesMut)]
|
||||
#[cfg_attr(feature = "reflect", derive(bevy_reflect::Reflect))]
|
||||
#[cfg_attr(
|
||||
feature = "reflect",
|
||||
|
@ -40,6 +40,14 @@ use smallvec::SmallVec;
|
|||
)]
|
||||
pub struct Children(pub(crate) SmallVec<[Entity; 8]>);
|
||||
|
||||
impl Component for Children {
|
||||
const STORAGE_TYPE: StorageType = StorageType::Table;
|
||||
|
||||
fn get_component_clone_handler() -> ComponentCloneHandler {
|
||||
ComponentCloneHandler::Ignore
|
||||
}
|
||||
}
|
||||
|
||||
// TODO: We need to impl either FromWorld or Default so Children can be registered as Reflect.
|
||||
// This is because Reflect deserialize by creating an instance and apply a patch on top.
|
||||
// However Children should only ever be set with a real user-defined entities. Its worth looking
|
||||
|
|
|
@ -4,7 +4,7 @@ use bevy_ecs::reflect::{
|
|||
ReflectVisitEntitiesMut,
|
||||
};
|
||||
use bevy_ecs::{
|
||||
component::Component,
|
||||
component::{Component, ComponentCloneHandler, StorageType},
|
||||
entity::{Entity, VisitEntities, VisitEntitiesMut},
|
||||
traversal::Traversal,
|
||||
world::{FromWorld, World},
|
||||
|
@ -24,7 +24,7 @@ use core::ops::Deref;
|
|||
/// [`Query`]: bevy_ecs::system::Query
|
||||
/// [`Children`]: super::children::Children
|
||||
/// [`BuildChildren::with_children`]: crate::child_builder::BuildChildren::with_children
|
||||
#[derive(Component, Debug, Eq, PartialEq, VisitEntities, VisitEntitiesMut)]
|
||||
#[derive(Debug, Eq, PartialEq, VisitEntities, VisitEntitiesMut)]
|
||||
#[cfg_attr(feature = "reflect", derive(bevy_reflect::Reflect))]
|
||||
#[cfg_attr(
|
||||
feature = "reflect",
|
||||
|
@ -40,6 +40,14 @@ use core::ops::Deref;
|
|||
)]
|
||||
pub struct Parent(pub(crate) Entity);
|
||||
|
||||
impl Component for Parent {
|
||||
const STORAGE_TYPE: StorageType = StorageType::Table;
|
||||
|
||||
fn get_component_clone_handler() -> ComponentCloneHandler {
|
||||
ComponentCloneHandler::Ignore
|
||||
}
|
||||
}
|
||||
|
||||
impl Parent {
|
||||
/// Gets the [`Entity`] ID of the parent.
|
||||
#[inline(always)]
|
||||
|
|
|
@ -1,8 +1,12 @@
|
|||
use crate::components::{Children, Parent};
|
||||
use crate::{
|
||||
components::{Children, Parent},
|
||||
BuildChildren,
|
||||
};
|
||||
use bevy_ecs::{
|
||||
entity::Entity,
|
||||
component::ComponentCloneHandler,
|
||||
entity::{Entity, EntityCloneBuilder, EntityCloner},
|
||||
system::EntityCommands,
|
||||
world::{Command, EntityWorldMut, World},
|
||||
world::{Command, DeferredWorld, EntityWorldMut, World},
|
||||
};
|
||||
use bevy_utils::tracing::debug;
|
||||
|
||||
|
@ -198,6 +202,67 @@ impl<'w> DespawnRecursiveExt for EntityWorldMut<'w> {
|
|||
}
|
||||
}
|
||||
|
||||
/// Trait that holds functions for cloning entities recursively down the hierarchy
|
||||
pub trait CloneEntityHierarchyExt {
|
||||
/// Sets the option to recursively clone entities.
|
||||
/// When set to true all children will be cloned with the same options as the parent.
|
||||
fn recursive(&mut self, recursive: bool) -> &mut Self;
|
||||
/// Sets the option to add cloned entity as a child to the parent entity.
|
||||
fn as_child(&mut self, as_child: bool) -> &mut Self;
|
||||
}
|
||||
|
||||
impl CloneEntityHierarchyExt for EntityCloneBuilder<'_> {
|
||||
fn recursive(&mut self, recursive: bool) -> &mut Self {
|
||||
if recursive {
|
||||
self.override_component_clone_handler::<Children>(ComponentCloneHandler::Custom(
|
||||
component_clone_children,
|
||||
))
|
||||
} else {
|
||||
self.remove_component_clone_handler_override::<Children>()
|
||||
}
|
||||
}
|
||||
fn as_child(&mut self, as_child: bool) -> &mut Self {
|
||||
if as_child {
|
||||
self.override_component_clone_handler::<Parent>(ComponentCloneHandler::Custom(
|
||||
component_clone_parent,
|
||||
))
|
||||
} else {
|
||||
self.remove_component_clone_handler_override::<Parent>()
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
/// Clone handler for the [`Children`] component. Allows to clone the entity recursively.
|
||||
fn component_clone_children(world: &mut DeferredWorld, entity_cloner: &EntityCloner) {
|
||||
let children = world
|
||||
.get::<Children>(entity_cloner.source())
|
||||
.expect("Source entity must have Children component")
|
||||
.iter()
|
||||
.cloned()
|
||||
.collect::<Vec<_>>();
|
||||
let parent = entity_cloner.target();
|
||||
for child in children {
|
||||
let child_clone = world.commands().spawn_empty().id();
|
||||
let mut entity_cloner = entity_cloner.with_source_and_target(child, child_clone);
|
||||
world.commands().queue(move |world: &mut World| {
|
||||
entity_cloner.clone_entity(world);
|
||||
world.entity_mut(child_clone).set_parent(parent);
|
||||
});
|
||||
}
|
||||
}
|
||||
|
||||
/// Clone handler for the [`Parent`] component. Allows to add clone as a child to the parent entity.
|
||||
fn component_clone_parent(world: &mut DeferredWorld, entity_cloner: &EntityCloner) {
|
||||
let parent = world
|
||||
.get::<Parent>(entity_cloner.source())
|
||||
.map(|p| p.0)
|
||||
.expect("Source entity must have Parent component");
|
||||
world
|
||||
.commands()
|
||||
.entity(entity_cloner.target())
|
||||
.set_parent(parent);
|
||||
}
|
||||
|
||||
#[cfg(test)]
|
||||
mod tests {
|
||||
use bevy_ecs::{
|
||||
|
@ -210,6 +275,7 @@ mod tests {
|
|||
use crate::{
|
||||
child_builder::{BuildChildren, ChildBuild},
|
||||
components::Children,
|
||||
CloneEntityHierarchyExt,
|
||||
};
|
||||
|
||||
#[derive(Component, Clone, Copy, PartialEq, Eq, Ord, PartialOrd, Debug)]
|
||||
|
@ -342,4 +408,89 @@ mod tests {
|
|||
// The original child should be despawned.
|
||||
assert!(world.get_entity(child).is_err());
|
||||
}
|
||||
|
||||
#[test]
|
||||
fn clone_entity_recursive() {
|
||||
#[derive(Component, PartialEq, Eq, Clone)]
|
||||
struct Component1 {
|
||||
field: usize,
|
||||
}
|
||||
|
||||
let parent_component = Component1 { field: 10 };
|
||||
let child1_component = Component1 { field: 20 };
|
||||
let child1_1_component = Component1 { field: 30 };
|
||||
let child2_component = Component1 { field: 21 };
|
||||
let child2_1_component = Component1 { field: 31 };
|
||||
|
||||
let mut world = World::default();
|
||||
|
||||
let mut queue = CommandQueue::default();
|
||||
let e_clone = {
|
||||
let mut commands = Commands::new(&mut queue, &world);
|
||||
let e = commands
|
||||
.spawn(parent_component.clone())
|
||||
.with_children(|children| {
|
||||
children
|
||||
.spawn(child1_component.clone())
|
||||
.with_children(|children| {
|
||||
children.spawn(child1_1_component.clone());
|
||||
});
|
||||
children
|
||||
.spawn(child2_component.clone())
|
||||
.with_children(|children| {
|
||||
children.spawn(child2_1_component.clone());
|
||||
});
|
||||
})
|
||||
.id();
|
||||
let e_clone = commands
|
||||
.clone_entity_with(e, |builder| {
|
||||
builder.recursive(true);
|
||||
})
|
||||
.id();
|
||||
e_clone
|
||||
};
|
||||
queue.apply(&mut world);
|
||||
|
||||
assert!(world
|
||||
.get::<Component1>(e_clone)
|
||||
.is_some_and(|c| *c == parent_component));
|
||||
|
||||
let children = world.get::<Children>(e_clone).unwrap();
|
||||
for (child, (component1, component2)) in children.iter().zip([
|
||||
(child1_component, child1_1_component),
|
||||
(child2_component, child2_1_component),
|
||||
]) {
|
||||
assert!(world
|
||||
.get::<Component1>(*child)
|
||||
.is_some_and(|c| *c == component1));
|
||||
for child2 in world.get::<Children>(*child).unwrap().iter() {
|
||||
assert!(world
|
||||
.get::<Component1>(*child2)
|
||||
.is_some_and(|c| *c == component2));
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
#[test]
|
||||
fn clone_entity_as_child() {
|
||||
let mut world = World::default();
|
||||
let mut queue = CommandQueue::default();
|
||||
let mut commands = Commands::new(&mut queue, &world);
|
||||
|
||||
let child = commands.spawn_empty().id();
|
||||
let parent = commands.spawn_empty().add_child(child).id();
|
||||
|
||||
let child_clone = commands
|
||||
.clone_entity_with(child, |builder| {
|
||||
builder.as_child(true);
|
||||
})
|
||||
.id();
|
||||
|
||||
queue.apply(&mut world);
|
||||
|
||||
assert!(world
|
||||
.entity(parent)
|
||||
.get::<Children>()
|
||||
.is_some_and(|c| c.contains(&child_clone)));
|
||||
}
|
||||
}
|
||||
|
|
Loading…
Reference in a new issue