Add insert_children and push_children to EntityMut (#1728)

The only API to add a parent/child relationship between existing entities is through commands, there is no easy way to do it from `World`. Manually inserting the components is not completely possible since `PreviousParent` has no public constructor.

This PR adds two methods to set entities as children of an `EntityMut`: `insert_children` and `push_children`. ~~The API is similar to the one on `Commands`, except that the parent is the `EntityMut`.~~ The API is the same as in #1703.
However, the `Parent` and `Children` components are defined in `bevy_transform` which depends on `bevy_ecs`, while `EntityMut` is defined in `bevy_ecs`, so the methods are added to the `BuildWorldChildren` trait instead.
If #1545 is merged this should be fixed too.

I'm aware cart was experimenting with entity hierarchies, but unless it's a coming soon this PR would be useful to have meanwhile.

Co-authored-by: Carter Anderson <mcanders1@gmail.com>
This commit is contained in:
davier 2021-03-26 20:03:12 +00:00
parent d90d19f1c7
commit 0c374df712

View File

@ -19,6 +19,7 @@ impl Command for InsertChildren {
for child in self.children.iter() {
world
.entity_mut(*child)
// FIXME: don't erase the previous parent (see #1545)
.insert_bundle((Parent(self.parent), PreviousParent(self.parent)));
}
{
@ -55,6 +56,7 @@ impl Command for PushChildren {
for child in self.children.iter() {
world
.entity_mut(*child)
// FIXME: don't erase the previous parent (see #1545)
.insert_bundle((Parent(self.parent), PreviousParent(self.parent)));
}
{
@ -198,6 +200,8 @@ impl<'w> WorldChildBuilder<'w> {
pub trait BuildWorldChildren {
fn with_children(&mut self, spawn_children: impl FnOnce(&mut WorldChildBuilder)) -> &mut Self;
fn push_children(&mut self, children: &[Entity]) -> &mut Self;
fn insert_children(&mut self, index: usize, children: &[Entity]) -> &mut Self;
}
impl<'w> BuildWorldChildren for EntityMut<'w> {
@ -217,6 +221,47 @@ impl<'w> BuildWorldChildren for EntityMut<'w> {
self.update_location();
self
}
fn push_children(&mut self, children: &[Entity]) -> &mut Self {
let parent = self.id();
{
// SAFE: parent entity is not modified
let world = unsafe { self.world_mut() };
for child in children.iter() {
world
.entity_mut(*child)
// FIXME: don't erase the previous parent (see #1545)
.insert_bundle((Parent(parent), PreviousParent(parent)));
}
}
if let Some(mut children_component) = self.get_mut::<Children>() {
children_component.0.extend(children.iter().cloned());
} else {
self.insert(Children::with(children));
}
self
}
fn insert_children(&mut self, index: usize, children: &[Entity]) -> &mut Self {
let parent = self.id();
{
// SAFE: parent entity is not modified
let world = unsafe { self.world_mut() };
for child in children.iter() {
world
.entity_mut(*child)
// FIXME: don't erase the previous parent (see #1545)
.insert_bundle((Parent(parent), PreviousParent(parent)));
}
}
if let Some(mut children_component) = self.get_mut::<Children>() {
children_component.0.insert_from_slice(index, children);
} else {
self.insert(Children::with(children));
}
self
}
}
impl<'w> BuildWorldChildren for WorldChildBuilder<'w> {
@ -235,11 +280,52 @@ impl<'w> BuildWorldChildren for WorldChildBuilder<'w> {
self.current_entity = self.parent_entities.pop();
self
}
fn push_children(&mut self, children: &[Entity]) -> &mut Self {
let parent = self
.current_entity
.expect("Cannot add children without a parent. Try creating an entity first.");
for child in children.iter() {
self.world
.entity_mut(*child)
// FIXME: don't erase the previous parent (see #1545)
.insert_bundle((Parent(parent), PreviousParent(parent)));
}
if let Some(mut children_component) = self.world.get_mut::<Children>(parent) {
children_component.0.extend(children.iter().cloned());
} else {
self.world
.entity_mut(parent)
.insert(Children::with(children));
}
self
}
fn insert_children(&mut self, index: usize, children: &[Entity]) -> &mut Self {
let parent = self
.current_entity
.expect("Cannot add children without a parent. Try creating an entity first.");
for child in children.iter() {
self.world
.entity_mut(*child)
// FIXME: don't erase the previous parent (see #1545)
.insert_bundle((Parent(parent), PreviousParent(parent)));
}
if let Some(mut children_component) = self.world.get_mut::<Children>(parent) {
children_component.0.insert_from_slice(index, children);
} else {
self.world
.entity_mut(parent)
.insert(Children::with(children));
}
self
}
}
#[cfg(test)]
mod tests {
use super::BuildChildren;
use super::{BuildChildren, BuildWorldChildren};
use crate::prelude::{Children, Parent, PreviousParent};
use bevy_ecs::{
entity::Entity,
@ -281,7 +367,7 @@ mod tests {
}
#[test]
fn push_and_insert_children() {
fn push_and_insert_children_commands() {
let mut world = World::default();
let entities = world
@ -340,4 +426,55 @@ mod tests {
PreviousParent(parent)
);
}
#[test]
fn push_and_insert_children_world() {
let mut world = World::default();
let entities = world
.spawn_batch(vec![(1,), (2,), (3,), (4,), (5,)])
.collect::<Vec<Entity>>();
world.entity_mut(entities[0]).push_children(&entities[1..3]);
let parent = entities[0];
let child1 = entities[1];
let child2 = entities[2];
let child3 = entities[3];
let child4 = entities[4];
let expected_children: SmallVec<[Entity; 8]> = smallvec![child1, child2];
assert_eq!(
world.get::<Children>(parent).unwrap().0.clone(),
expected_children
);
assert_eq!(*world.get::<Parent>(child1).unwrap(), Parent(parent));
assert_eq!(*world.get::<Parent>(child2).unwrap(), Parent(parent));
assert_eq!(
*world.get::<PreviousParent>(child1).unwrap(),
PreviousParent(parent)
);
assert_eq!(
*world.get::<PreviousParent>(child2).unwrap(),
PreviousParent(parent)
);
world.entity_mut(parent).insert_children(1, &entities[3..]);
let expected_children: SmallVec<[Entity; 8]> = smallvec![child1, child3, child4, child2];
assert_eq!(
world.get::<Children>(parent).unwrap().0.clone(),
expected_children
);
assert_eq!(*world.get::<Parent>(child3).unwrap(), Parent(parent));
assert_eq!(*world.get::<Parent>(child4).unwrap(), Parent(parent));
assert_eq!(
*world.get::<PreviousParent>(child3).unwrap(),
PreviousParent(parent)
);
assert_eq!(
*world.get::<PreviousParent>(child4).unwrap(),
PreviousParent(parent)
);
}
}