Extend cloning functionality and add convenience methods to EntityWorldMut
and EntityCommands
(#16826)
## Objective Thanks to @eugineerd's work on entity cloning (#16132), we now have a robust way to copy components between entities. We can extend this to implement some useful functionality that would have been more complicated before. Closes #15350. ## Solution `EntityCloneBuilder` now automatically includes required components alongside any component added/removed from the component filter. Added the following methods to `EntityCloneBuilder`: - `move_components` - `without_required_components` Added the following methods to `EntityWorldMut` and `EntityCommands`: - `clone_with` - `clone_components` - `move_components` Also added `clone_and_spawn` and `clone_and_spawn_with` to `EntityWorldMut` (`EntityCommands` already had them). ## Showcase ``` assert_eq!(world.entity(entity_a).get::<B>(), Some(&B)); assert_eq!(world.entity(entity_b).get::<B>(), None); world.entity_mut(entity_a).clone_components::<B>(entity_b); assert_eq!(world.entity(entity_a).get::<B>(), Some(&B)); assert_eq!(world.entity(entity_b).get::<B>(), Some(&B)); assert_eq!(world.entity(entity_a).get::<C>(), Some(&C(5))); assert_eq!(world.entity(entity_b).get::<C>(), None); world.entity_mut(entity_a).move_components::<C>(entity_b); assert_eq!(world.entity(entity_a).get::<C>(), None); assert_eq!(world.entity(entity_b).get::<C>(), Some(&C(5))); ```
This commit is contained in:
parent
74e793d1e1
commit
5a94beb239
@ -18,6 +18,7 @@ pub struct EntityCloner {
|
||||
filter_allows_components: bool,
|
||||
filter: Arc<HashSet<ComponentId>>,
|
||||
clone_handlers_overrides: Arc<HashMap<ComponentId, ComponentCloneHandler>>,
|
||||
move_components: bool,
|
||||
}
|
||||
|
||||
impl EntityCloner {
|
||||
@ -35,17 +36,21 @@ impl EntityCloner {
|
||||
.filter(|id| self.is_cloning_allowed(id)),
|
||||
);
|
||||
|
||||
for component in components {
|
||||
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),
|
||||
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);
|
||||
self.component_id = Some(*component);
|
||||
(handler)(&mut world.into(), self);
|
||||
}
|
||||
|
||||
if self.move_components {
|
||||
world.entity_mut(self.source).remove_by_ids(&components);
|
||||
}
|
||||
}
|
||||
|
||||
fn is_cloning_allowed(&self, component: &ComponentId) -> bool {
|
||||
@ -145,6 +150,8 @@ pub struct EntityCloneBuilder<'w> {
|
||||
filter_allows_components: bool,
|
||||
filter: HashSet<ComponentId>,
|
||||
clone_handlers_overrides: HashMap<ComponentId, ComponentCloneHandler>,
|
||||
attach_required_components: bool,
|
||||
move_components: bool,
|
||||
}
|
||||
|
||||
impl<'w> EntityCloneBuilder<'w> {
|
||||
@ -155,6 +162,8 @@ impl<'w> EntityCloneBuilder<'w> {
|
||||
filter_allows_components: false,
|
||||
filter: Default::default(),
|
||||
clone_handlers_overrides: Default::default(),
|
||||
attach_required_components: true,
|
||||
move_components: false,
|
||||
}
|
||||
}
|
||||
|
||||
@ -165,6 +174,7 @@ impl<'w> EntityCloneBuilder<'w> {
|
||||
filter_allows_components,
|
||||
filter,
|
||||
clone_handlers_overrides,
|
||||
move_components,
|
||||
..
|
||||
} = self;
|
||||
|
||||
@ -175,29 +185,49 @@ impl<'w> EntityCloneBuilder<'w> {
|
||||
filter_allows_components,
|
||||
filter: Arc::new(filter),
|
||||
clone_handlers_overrides: Arc::new(clone_handlers_overrides),
|
||||
move_components,
|
||||
}
|
||||
.clone_entity(world);
|
||||
|
||||
world.flush_commands();
|
||||
}
|
||||
|
||||
/// By default, any components allowed/denied through the filter will automatically
|
||||
/// allow/deny all of their required components.
|
||||
///
|
||||
/// This method allows for a scoped mode where any changes to the filter
|
||||
/// will not involve required components.
|
||||
pub fn without_required_components(
|
||||
&mut self,
|
||||
builder: impl FnOnce(&mut EntityCloneBuilder) + Send + Sync + 'static,
|
||||
) -> &mut Self {
|
||||
self.attach_required_components = false;
|
||||
builder(self);
|
||||
self.attach_required_components = true;
|
||||
self
|
||||
}
|
||||
|
||||
/// Sets whether the cloner should remove any components that were cloned,
|
||||
/// effectively moving them from the source entity to the target.
|
||||
///
|
||||
/// This is disabled by default.
|
||||
///
|
||||
/// The setting only applies to components that are allowed through the filter
|
||||
/// at the time [`EntityCloneBuilder::clone_entity`] is called.
|
||||
pub fn move_components(&mut self, enable: bool) -> &mut Self {
|
||||
self.move_components = enable;
|
||||
self
|
||||
}
|
||||
|
||||
/// 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);
|
||||
}
|
||||
});
|
||||
let bundle = self.world.register_bundle::<T>();
|
||||
let ids = bundle.explicit_components().to_owned();
|
||||
for id in ids {
|
||||
self.filter_allow(id);
|
||||
}
|
||||
self
|
||||
}
|
||||
@ -207,12 +237,8 @@ impl<'w> EntityCloneBuilder<'w> {
|
||||
/// 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);
|
||||
});
|
||||
for id in ids {
|
||||
self.filter_allow(id);
|
||||
}
|
||||
self
|
||||
}
|
||||
@ -222,15 +248,10 @@ impl<'w> EntityCloneBuilder<'w> {
|
||||
/// 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);
|
||||
});
|
||||
for type_id in ids {
|
||||
if let Some(id) = self.world.components().get_id(type_id) {
|
||||
self.filter_allow(id);
|
||||
}
|
||||
}
|
||||
self
|
||||
}
|
||||
@ -244,45 +265,28 @@ impl<'w> EntityCloneBuilder<'w> {
|
||||
|
||||
/// 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);
|
||||
}
|
||||
});
|
||||
let bundle = self.world.register_bundle::<T>();
|
||||
let ids = bundle.explicit_components().to_owned();
|
||||
for id in ids {
|
||||
self.filter_deny(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);
|
||||
for id in ids {
|
||||
self.filter_deny(id);
|
||||
}
|
||||
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);
|
||||
for type_id in ids {
|
||||
if let Some(id) = self.world.components().get_id(type_id) {
|
||||
self.filter_deny(id);
|
||||
}
|
||||
}
|
||||
self
|
||||
}
|
||||
@ -315,11 +319,52 @@ impl<'w> EntityCloneBuilder<'w> {
|
||||
}
|
||||
self
|
||||
}
|
||||
|
||||
/// Helper function that allows a component through the filter.
|
||||
fn filter_allow(&mut self, id: ComponentId) {
|
||||
if self.filter_allows_components {
|
||||
self.filter.insert(id);
|
||||
} else {
|
||||
self.filter.remove(&id);
|
||||
}
|
||||
if self.attach_required_components {
|
||||
if let Some(info) = self.world.components().get_info(id) {
|
||||
for required_id in info.required_components().iter_ids() {
|
||||
if self.filter_allows_components {
|
||||
self.filter.insert(required_id);
|
||||
} else {
|
||||
self.filter.remove(&required_id);
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
/// Helper function that disallows a component through the filter.
|
||||
fn filter_deny(&mut self, id: ComponentId) {
|
||||
if self.filter_allows_components {
|
||||
self.filter.remove(&id);
|
||||
} else {
|
||||
self.filter.insert(id);
|
||||
}
|
||||
if self.attach_required_components {
|
||||
if let Some(info) = self.world.components().get_info(id) {
|
||||
for required_id in info.required_components().iter_ids() {
|
||||
if self.filter_allows_components {
|
||||
self.filter.remove(&required_id);
|
||||
} else {
|
||||
self.filter.insert(required_id);
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
#[cfg(test)]
|
||||
mod tests {
|
||||
use crate::{self as bevy_ecs, component::Component, entity::EntityCloneBuilder, world::World};
|
||||
use bevy_ecs_macros::require;
|
||||
|
||||
#[cfg(feature = "bevy_reflect")]
|
||||
#[test]
|
||||
@ -520,4 +565,34 @@ mod tests {
|
||||
assert!(world.get::<B>(e_clone).is_none());
|
||||
assert!(world.get::<C>(e_clone).is_none());
|
||||
}
|
||||
|
||||
#[test]
|
||||
fn clone_entity_with_required_components() {
|
||||
#[derive(Component, Clone, PartialEq, Debug)]
|
||||
#[require(B)]
|
||||
struct A;
|
||||
|
||||
#[derive(Component, Clone, PartialEq, Debug, Default)]
|
||||
#[require(C(|| C(5)))]
|
||||
struct B;
|
||||
|
||||
#[derive(Component, Clone, PartialEq, Debug)]
|
||||
struct C(u32);
|
||||
|
||||
let mut world = World::default();
|
||||
|
||||
let e = world.spawn(A).id();
|
||||
let e_clone = world.spawn_empty().id();
|
||||
|
||||
let mut builder = EntityCloneBuilder::new(&mut world);
|
||||
builder.deny_all();
|
||||
builder.without_required_components(|builder| {
|
||||
builder.allow::<B>();
|
||||
});
|
||||
builder.clone_entity(e, e_clone);
|
||||
|
||||
assert_eq!(world.entity(e_clone).get::<A>(), None);
|
||||
assert_eq!(world.entity(e_clone).get::<B>(), Some(&B));
|
||||
assert_eq!(world.entity(e_clone).get::<C>(), Some(&C(5)));
|
||||
}
|
||||
}
|
||||
|
@ -1738,7 +1738,53 @@ impl<'a> EntityCommands<'a> {
|
||||
self.queue(observe(system))
|
||||
}
|
||||
|
||||
/// Clones an entity and returns the [`EntityCommands`] of the clone.
|
||||
/// Clones parts of an entity (components, observers, etc.) onto another entity,
|
||||
/// configured through [`EntityCloneBuilder`].
|
||||
///
|
||||
/// By default, the other entity will receive all the components of the original that implement
|
||||
/// [`Clone`] or [`Reflect`](bevy_reflect::Reflect).
|
||||
///
|
||||
/// Configure through [`EntityCloneBuilder`] as follows:
|
||||
/// ```
|
||||
/// # use bevy_ecs::prelude::*;
|
||||
///
|
||||
/// #[derive(Component, Clone)]
|
||||
/// struct ComponentA(u32);
|
||||
/// #[derive(Component, Clone)]
|
||||
/// struct ComponentB(u32);
|
||||
///
|
||||
/// fn example_system(mut commands: Commands) {
|
||||
/// // Create an empty entity
|
||||
/// let target = commands.spawn_empty().id();
|
||||
///
|
||||
/// // Create a new entity and keep its EntityCommands
|
||||
/// let mut entity = commands.spawn((ComponentA(10), ComponentB(20)));
|
||||
///
|
||||
/// // Clone only ComponentA onto the target
|
||||
/// entity.clone_with(target, |builder| {
|
||||
/// builder.deny::<ComponentB>();
|
||||
/// });
|
||||
/// }
|
||||
/// # bevy_ecs::system::assert_is_system(example_system);
|
||||
/// ```
|
||||
///
|
||||
/// See the following for more options:
|
||||
/// - [`EntityCloneBuilder`]
|
||||
/// - [`CloneEntityWithObserversExt`](crate::observer::CloneEntityWithObserversExt)
|
||||
/// - `CloneEntityHierarchyExt`
|
||||
///
|
||||
/// # Panics
|
||||
///
|
||||
/// The command will panic when applied if either of the entities do not exist.
|
||||
pub fn clone_with(
|
||||
&mut self,
|
||||
target: Entity,
|
||||
config: impl FnOnce(&mut EntityCloneBuilder) + Send + Sync + 'static,
|
||||
) -> &mut Self {
|
||||
self.queue(clone_with(target, config))
|
||||
}
|
||||
|
||||
/// Spawns a clone of this entity and returns the [`EntityCommands`] of the clone.
|
||||
///
|
||||
/// The clone will receive all the components of the original that implement
|
||||
/// [`Clone`] or [`Reflect`](bevy_reflect::Reflect).
|
||||
@ -1772,8 +1818,8 @@ impl<'a> EntityCommands<'a> {
|
||||
self.clone_and_spawn_with(|_| {})
|
||||
}
|
||||
|
||||
/// Clones an entity and allows configuring cloning behavior using [`EntityCloneBuilder`],
|
||||
/// returning the [`EntityCommands`] of the clone.
|
||||
/// Spawns a clone of this entity and allows configuring cloning behavior
|
||||
/// using [`EntityCloneBuilder`], returning the [`EntityCommands`] of the clone.
|
||||
///
|
||||
/// By default, the clone will receive all the components of the original that implement
|
||||
/// [`Clone`] or [`Reflect`](bevy_reflect::Reflect).
|
||||
@ -1782,6 +1828,8 @@ impl<'a> EntityCommands<'a> {
|
||||
/// To only include specific components, use [`EntityCloneBuilder::deny_all`]
|
||||
/// followed by [`EntityCloneBuilder::allow`].
|
||||
///
|
||||
/// See the methods on [`EntityCloneBuilder`] for more options.
|
||||
///
|
||||
/// # Panics
|
||||
///
|
||||
/// The command will panic when applied if the original entity does not exist.
|
||||
@ -1808,15 +1856,40 @@ impl<'a> EntityCommands<'a> {
|
||||
/// # bevy_ecs::system::assert_is_system(example_system);
|
||||
pub fn clone_and_spawn_with(
|
||||
&mut self,
|
||||
f: impl FnOnce(&mut EntityCloneBuilder) + Send + Sync + 'static,
|
||||
config: impl FnOnce(&mut EntityCloneBuilder) + Send + Sync + 'static,
|
||||
) -> EntityCommands<'_> {
|
||||
let entity_clone = self.commands().spawn_empty().id();
|
||||
self.queue(clone_and_spawn_with(entity_clone, f));
|
||||
self.queue(clone_with(entity_clone, config));
|
||||
EntityCommands {
|
||||
commands: self.commands_mut().reborrow(),
|
||||
entity: entity_clone,
|
||||
}
|
||||
}
|
||||
|
||||
/// Clones the specified components of this entity and inserts them into another entity.
|
||||
///
|
||||
/// Components can only be cloned if they implement
|
||||
/// [`Clone`] or [`Reflect`](bevy_reflect::Reflect).
|
||||
///
|
||||
/// # Panics
|
||||
///
|
||||
/// The command will panic when applied if either of the entities do not exist.
|
||||
pub fn clone_components<B: Bundle>(&mut self, target: Entity) -> &mut Self {
|
||||
self.queue(clone_components::<B>(target))
|
||||
}
|
||||
|
||||
/// Clones the specified components of this entity and inserts them into another entity,
|
||||
/// then removes the components from this entity.
|
||||
///
|
||||
/// Components can only be cloned if they implement
|
||||
/// [`Clone`] or [`Reflect`](bevy_reflect::Reflect).
|
||||
///
|
||||
/// # Panics
|
||||
///
|
||||
/// The command will panic when applied if either of the entities do not exist.
|
||||
pub fn move_components<B: Bundle>(&mut self, target: Entity) -> &mut Self {
|
||||
self.queue(move_components::<B>(target))
|
||||
}
|
||||
}
|
||||
|
||||
/// A wrapper around [`EntityCommands`] with convenience methods for working with a specified component type.
|
||||
@ -2291,14 +2364,34 @@ fn observe<E: Event, B: Bundle, M>(
|
||||
}
|
||||
}
|
||||
|
||||
fn clone_and_spawn_with(
|
||||
entity_clone: Entity,
|
||||
f: impl FnOnce(&mut EntityCloneBuilder) + Send + Sync + 'static,
|
||||
/// An [`EntityCommand`] that clones an entity with configurable cloning behavior.
|
||||
fn clone_with(
|
||||
target: Entity,
|
||||
config: impl FnOnce(&mut EntityCloneBuilder) + Send + Sync + 'static,
|
||||
) -> impl EntityCommand {
|
||||
move |entity: Entity, world: &mut World| {
|
||||
let mut builder = EntityCloneBuilder::new(world);
|
||||
f(&mut builder);
|
||||
builder.clone_entity(entity, entity_clone);
|
||||
if let Ok(mut entity) = world.get_entity_mut(entity) {
|
||||
entity.clone_with(target, config);
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
/// An [`EntityCommand`] that clones the specified components into another entity.
|
||||
fn clone_components<B: Bundle>(target: Entity) -> impl EntityCommand {
|
||||
move |entity: Entity, world: &mut World| {
|
||||
if let Ok(mut entity) = world.get_entity_mut(entity) {
|
||||
entity.clone_components::<B>(target);
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
/// An [`EntityCommand`] that clones the specified components into another entity
|
||||
/// and removes them from the original entity.
|
||||
fn move_components<B: Bundle>(target: Entity) -> impl EntityCommand {
|
||||
move |entity: Entity, world: &mut World| {
|
||||
if let Ok(mut entity) = world.get_entity_mut(entity) {
|
||||
entity.move_components::<B>(target);
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
|
@ -3,7 +3,7 @@ use crate::{
|
||||
bundle::{Bundle, BundleId, BundleInfo, BundleInserter, DynamicBundle, InsertMode},
|
||||
change_detection::MutUntyped,
|
||||
component::{Component, ComponentId, ComponentTicks, Components, Mutable, StorageType},
|
||||
entity::{Entities, Entity, EntityLocation},
|
||||
entity::{Entities, Entity, EntityCloneBuilder, EntityLocation},
|
||||
event::Event,
|
||||
observer::Observer,
|
||||
query::{Access, ReadOnlyQueryData},
|
||||
@ -1819,6 +1819,31 @@ impl<'w> EntityWorldMut<'w> {
|
||||
self
|
||||
}
|
||||
|
||||
/// Removes a dynamic bundle from the entity if it exists.
|
||||
///
|
||||
/// You should prefer to use the typed API [`EntityWorldMut::remove`] where possible.
|
||||
///
|
||||
/// # Panics
|
||||
///
|
||||
/// Panics if any of the provided [`ComponentId`]s do not exist in the [`World`] or if the
|
||||
/// entity has been despawned while this `EntityWorldMut` is still alive.
|
||||
pub fn remove_by_ids(&mut self, component_ids: &[ComponentId]) -> &mut Self {
|
||||
self.assert_not_despawned();
|
||||
let components = &mut self.world.components;
|
||||
|
||||
let bundle_id = self
|
||||
.world
|
||||
.bundles
|
||||
.init_dynamic_info(components, component_ids);
|
||||
|
||||
// SAFETY: the `BundleInfo` for this `bundle_id` is initialized above
|
||||
unsafe { self.remove_bundle(bundle_id) };
|
||||
|
||||
self.world.flush();
|
||||
self.update_location();
|
||||
self
|
||||
}
|
||||
|
||||
/// Removes all components associated with the entity.
|
||||
///
|
||||
/// # Panics
|
||||
@ -2107,6 +2132,161 @@ impl<'w> EntityWorldMut<'w> {
|
||||
self.update_location();
|
||||
self
|
||||
}
|
||||
|
||||
/// Clones parts of an entity (components, observers, etc.) onto another entity,
|
||||
/// configured through [`EntityCloneBuilder`].
|
||||
///
|
||||
/// By default, the other entity will receive all the components of the original that implement
|
||||
/// [`Clone`] or [`Reflect`](bevy_reflect::Reflect).
|
||||
///
|
||||
/// Configure through [`EntityCloneBuilder`] as follows:
|
||||
/// ```
|
||||
/// # use bevy_ecs::prelude::*;
|
||||
/// # #[derive(Component, Clone, PartialEq, Debug)]
|
||||
/// # struct ComponentA;
|
||||
/// # #[derive(Component, Clone, PartialEq, Debug)]
|
||||
/// # struct ComponentB;
|
||||
/// # let mut world = World::new();
|
||||
/// # let entity = world.spawn((ComponentA, ComponentB)).id();
|
||||
/// # let target = world.spawn_empty().id();
|
||||
/// world.entity_mut(entity).clone_with(target, |builder| {
|
||||
/// builder.deny::<ComponentB>();
|
||||
/// });
|
||||
/// # assert_eq!(world.get::<ComponentA>(target), Some(&ComponentA));
|
||||
/// # assert_eq!(world.get::<ComponentB>(target), None);
|
||||
/// ```
|
||||
///
|
||||
/// See the following for more options:
|
||||
/// - [`EntityCloneBuilder`]
|
||||
/// - [`CloneEntityWithObserversExt`](crate::observer::CloneEntityWithObserversExt)
|
||||
/// - `CloneEntityHierarchyExt`
|
||||
///
|
||||
/// # Panics
|
||||
///
|
||||
/// - If this entity has been despawned while this `EntityWorldMut` is still alive.
|
||||
/// - If the target entity does not exist.
|
||||
pub fn clone_with(
|
||||
&mut self,
|
||||
target: Entity,
|
||||
config: impl FnOnce(&mut EntityCloneBuilder) + Send + Sync + 'static,
|
||||
) -> &mut Self {
|
||||
self.assert_not_despawned();
|
||||
|
||||
let mut builder = EntityCloneBuilder::new(self.world);
|
||||
config(&mut builder);
|
||||
builder.clone_entity(self.entity, target);
|
||||
|
||||
self.world.flush();
|
||||
self.update_location();
|
||||
self
|
||||
}
|
||||
|
||||
/// Spawns a clone of this entity and returns the [`Entity`] of the clone.
|
||||
///
|
||||
/// The clone will receive all the components of the original that implement
|
||||
/// [`Clone`] or [`Reflect`](bevy_reflect::Reflect).
|
||||
///
|
||||
/// To configure cloning behavior (such as only cloning certain components),
|
||||
/// use [`EntityWorldMut::clone_and_spawn_with`].
|
||||
///
|
||||
/// # Panics
|
||||
///
|
||||
/// If this entity has been despawned while this `EntityWorldMut` is still alive.
|
||||
pub fn clone_and_spawn(&mut self) -> Entity {
|
||||
self.clone_and_spawn_with(|_| {})
|
||||
}
|
||||
|
||||
/// Spawns a clone of this entity and allows configuring cloning behavior
|
||||
/// using [`EntityCloneBuilder`], returning the [`Entity`] of the clone.
|
||||
///
|
||||
/// By default, the clone will receive all the components of the original that implement
|
||||
/// [`Clone`] or [`Reflect`](bevy_reflect::Reflect).
|
||||
///
|
||||
/// Configure through [`EntityCloneBuilder`] as follows:
|
||||
/// ```
|
||||
/// # use bevy_ecs::prelude::*;
|
||||
/// # #[derive(Component, Clone, PartialEq, Debug)]
|
||||
/// # struct ComponentA;
|
||||
/// # #[derive(Component, Clone, PartialEq, Debug)]
|
||||
/// # struct ComponentB;
|
||||
/// # let mut world = World::new();
|
||||
/// # let entity = world.spawn((ComponentA, ComponentB)).id();
|
||||
/// let entity_clone = world.entity_mut(entity).clone_and_spawn_with(|builder| {
|
||||
/// builder.deny::<ComponentB>();
|
||||
/// });
|
||||
/// # assert_eq!(world.get::<ComponentA>(entity_clone), Some(&ComponentA));
|
||||
/// # assert_eq!(world.get::<ComponentB>(entity_clone), None);
|
||||
/// ```
|
||||
///
|
||||
/// See the following for more options:
|
||||
/// - [`EntityCloneBuilder`]
|
||||
/// - [`CloneEntityWithObserversExt`](crate::observer::CloneEntityWithObserversExt)
|
||||
/// - `CloneEntityHierarchyExt`
|
||||
///
|
||||
/// # Panics
|
||||
///
|
||||
/// If this entity has been despawned while this `EntityWorldMut` is still alive.
|
||||
pub fn clone_and_spawn_with(
|
||||
&mut self,
|
||||
config: impl FnOnce(&mut EntityCloneBuilder) + Send + Sync + 'static,
|
||||
) -> Entity {
|
||||
self.assert_not_despawned();
|
||||
|
||||
let entity_clone = self.world.entities.reserve_entity();
|
||||
self.world.flush();
|
||||
|
||||
let mut builder = EntityCloneBuilder::new(self.world);
|
||||
config(&mut builder);
|
||||
builder.clone_entity(self.entity, entity_clone);
|
||||
|
||||
self.world.flush();
|
||||
self.update_location();
|
||||
entity_clone
|
||||
}
|
||||
|
||||
/// Clones the specified components of this entity and inserts them into another entity.
|
||||
///
|
||||
/// Components can only be cloned if they implement
|
||||
/// [`Clone`] or [`Reflect`](bevy_reflect::Reflect).
|
||||
///
|
||||
/// # Panics
|
||||
///
|
||||
/// - If this entity has been despawned while this `EntityWorldMut` is still alive.
|
||||
/// - If the target entity does not exist.
|
||||
pub fn clone_components<B: Bundle>(&mut self, target: Entity) -> &mut Self {
|
||||
self.assert_not_despawned();
|
||||
|
||||
let mut builder = EntityCloneBuilder::new(self.world);
|
||||
builder.deny_all().allow::<B>();
|
||||
builder.clone_entity(self.entity, target);
|
||||
|
||||
self.world.flush();
|
||||
self.update_location();
|
||||
self
|
||||
}
|
||||
|
||||
/// Clones the specified components of this entity and inserts them into another entity,
|
||||
/// then removes the components from this entity.
|
||||
///
|
||||
/// Components can only be cloned if they implement
|
||||
/// [`Clone`] or [`Reflect`](bevy_reflect::Reflect).
|
||||
///
|
||||
/// # Panics
|
||||
///
|
||||
/// - If this entity has been despawned while this `EntityWorldMut` is still alive.
|
||||
/// - If the target entity does not exist.
|
||||
pub fn move_components<B: Bundle>(&mut self, target: Entity) -> &mut Self {
|
||||
self.assert_not_despawned();
|
||||
|
||||
let mut builder = EntityCloneBuilder::new(self.world);
|
||||
builder.deny_all().allow::<B>();
|
||||
builder.move_components(true);
|
||||
builder.clone_entity(self.entity, target);
|
||||
|
||||
self.world.flush();
|
||||
self.update_location();
|
||||
self
|
||||
}
|
||||
}
|
||||
|
||||
/// # Safety
|
||||
@ -4776,4 +4956,75 @@ mod tests {
|
||||
world.flush();
|
||||
assert_eq!(world.resource_mut::<TestVec>().0.as_slice(), &expected[..]);
|
||||
}
|
||||
|
||||
#[test]
|
||||
fn entity_world_mut_clone_and_move_components() {
|
||||
#[derive(Component, Clone, PartialEq, Debug)]
|
||||
struct A;
|
||||
|
||||
#[derive(Component, Clone, PartialEq, Debug)]
|
||||
struct B;
|
||||
|
||||
#[derive(Component, Clone, PartialEq, Debug)]
|
||||
struct C(u32);
|
||||
|
||||
#[derive(Component, Clone, PartialEq, Debug, Default)]
|
||||
struct D;
|
||||
|
||||
let mut world = World::new();
|
||||
let entity_a = world.spawn((A, B, C(5))).id();
|
||||
let entity_b = world.spawn((A, C(4))).id();
|
||||
|
||||
world.entity_mut(entity_a).clone_components::<B>(entity_b);
|
||||
assert_eq!(world.entity(entity_a).get::<B>(), Some(&B));
|
||||
assert_eq!(world.entity(entity_b).get::<B>(), Some(&B));
|
||||
|
||||
world.entity_mut(entity_a).move_components::<C>(entity_b);
|
||||
assert_eq!(world.entity(entity_a).get::<C>(), None);
|
||||
assert_eq!(world.entity(entity_b).get::<C>(), Some(&C(5)));
|
||||
|
||||
assert_eq!(world.entity(entity_a).get::<A>(), Some(&A));
|
||||
assert_eq!(world.entity(entity_b).get::<A>(), Some(&A));
|
||||
}
|
||||
|
||||
#[test]
|
||||
fn entity_world_mut_clone_with_move_and_require() {
|
||||
#[derive(Component, Clone, PartialEq, Debug)]
|
||||
#[require(B)]
|
||||
struct A;
|
||||
|
||||
#[derive(Component, Clone, PartialEq, Debug, Default)]
|
||||
#[require(C(|| C(3)))]
|
||||
struct B;
|
||||
|
||||
#[derive(Component, Clone, PartialEq, Debug, Default)]
|
||||
#[require(D)]
|
||||
struct C(u32);
|
||||
|
||||
#[derive(Component, Clone, PartialEq, Debug, Default)]
|
||||
struct D;
|
||||
|
||||
let mut world = World::new();
|
||||
let entity_a = world.spawn(A).id();
|
||||
let entity_b = world.spawn_empty().id();
|
||||
|
||||
world.entity_mut(entity_a).clone_with(entity_b, |builder| {
|
||||
builder.move_components(true);
|
||||
builder.without_required_components(|builder| {
|
||||
builder.deny::<A>();
|
||||
});
|
||||
});
|
||||
|
||||
assert_eq!(world.entity(entity_a).get::<A>(), Some(&A));
|
||||
assert_eq!(world.entity(entity_b).get::<A>(), None);
|
||||
|
||||
assert_eq!(world.entity(entity_a).get::<B>(), None);
|
||||
assert_eq!(world.entity(entity_b).get::<B>(), Some(&B));
|
||||
|
||||
assert_eq!(world.entity(entity_a).get::<C>(), None);
|
||||
assert_eq!(world.entity(entity_b).get::<C>(), Some(&C(3)));
|
||||
|
||||
assert_eq!(world.entity(entity_a).get::<D>(), None);
|
||||
assert_eq!(world.entity(entity_b).get::<D>(), Some(&D));
|
||||
}
|
||||
}
|
||||
|
Loading…
Reference in New Issue
Block a user