Hierarchy commandization (#4197)
## Objective Implement absolute minimum viable product for the changes proposed in bevyengine/rfcs#53. ## Solution - Remove public mutative access to `Parent` (Children is already publicly read-only). This includes public construction methods like `Copy`, `Clone`, and `Default`. - Remove `PreviousParent` - Remove `parent_update_system` - Update all hierarchy related commands to immediately update both `Parent` and `Children` references. ## Remaining TODOs - [ ] Update documentation for both `Parent` and `Children`. Discourage using `EntityCommands::remove` - [x] Add `HierarchyEvent` to notify listeners of hierarchy updates. This is meant to replace listening on `PreviousParent` ## Followup - These changes should be best moved to the hooks mentioned in #3742. - Backing storage for both might be best moved to indexes mentioned in the same relations.
This commit is contained in:
parent
518408dfda
commit
8eb0440f1e
@ -15,7 +15,7 @@ use bevy_ecs::{
|
|||||||
schedule::ParallelSystemDescriptorCoercion,
|
schedule::ParallelSystemDescriptorCoercion,
|
||||||
system::{Query, Res},
|
system::{Query, Res},
|
||||||
};
|
};
|
||||||
use bevy_hierarchy::{Children, HierarchySystem};
|
use bevy_hierarchy::Children;
|
||||||
use bevy_math::{Quat, Vec3};
|
use bevy_math::{Quat, Vec3};
|
||||||
use bevy_reflect::{Reflect, TypeUuid};
|
use bevy_reflect::{Reflect, TypeUuid};
|
||||||
use bevy_time::Time;
|
use bevy_time::Time;
|
||||||
@ -295,9 +295,7 @@ impl Plugin for AnimationPlugin {
|
|||||||
.register_type::<AnimationPlayer>()
|
.register_type::<AnimationPlayer>()
|
||||||
.add_system_to_stage(
|
.add_system_to_stage(
|
||||||
CoreStage::PostUpdate,
|
CoreStage::PostUpdate,
|
||||||
animation_player
|
animation_player.before(TransformSystem::TransformPropagate),
|
||||||
.before(TransformSystem::TransformPropagate)
|
|
||||||
.after(HierarchySystem::ParentUpdate),
|
|
||||||
);
|
);
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
@ -1,13 +1,88 @@
|
|||||||
use smallvec::SmallVec;
|
use crate::{
|
||||||
|
prelude::{Children, Parent},
|
||||||
|
HierarchyEvent,
|
||||||
|
};
|
||||||
use bevy_ecs::{
|
use bevy_ecs::{
|
||||||
bundle::Bundle,
|
bundle::Bundle,
|
||||||
entity::Entity,
|
entity::Entity,
|
||||||
|
event::Events,
|
||||||
system::{Command, Commands, EntityCommands},
|
system::{Command, Commands, EntityCommands},
|
||||||
world::{EntityMut, World},
|
world::{EntityMut, World},
|
||||||
};
|
};
|
||||||
|
use smallvec::SmallVec;
|
||||||
|
|
||||||
use crate::prelude::{Children, Parent, PreviousParent};
|
fn push_events(world: &mut World, events: SmallVec<[HierarchyEvent; 8]>) {
|
||||||
|
if let Some(mut moved) = world.get_resource_mut::<Events<HierarchyEvent>>() {
|
||||||
|
for evt in events {
|
||||||
|
moved.send(evt);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
fn push_child_unchecked(world: &mut World, parent: Entity, child: Entity) {
|
||||||
|
let mut parent = world.entity_mut(parent);
|
||||||
|
if let Some(mut children) = parent.get_mut::<Children>() {
|
||||||
|
children.0.push(child);
|
||||||
|
} else {
|
||||||
|
parent.insert(Children(smallvec::smallvec![child]));
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
fn update_parent(world: &mut World, child: Entity, new_parent: Entity) -> Option<Entity> {
|
||||||
|
let mut child = world.entity_mut(child);
|
||||||
|
if let Some(mut parent) = child.get_mut::<Parent>() {
|
||||||
|
let previous = parent.0;
|
||||||
|
*parent = Parent(new_parent);
|
||||||
|
Some(previous)
|
||||||
|
} else {
|
||||||
|
child.insert(Parent(new_parent));
|
||||||
|
None
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
fn remove_from_children(world: &mut World, parent: Entity, child: Entity) {
|
||||||
|
let mut parent = world.entity_mut(parent);
|
||||||
|
if let Some(mut children) = parent.get_mut::<Children>() {
|
||||||
|
children.0.retain(|x| *x != child);
|
||||||
|
if children.is_empty() {
|
||||||
|
parent.remove::<Children>();
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
fn update_old_parents(world: &mut World, parent: Entity, children: &[Entity]) {
|
||||||
|
let mut moved: SmallVec<[HierarchyEvent; 8]> = SmallVec::with_capacity(children.len());
|
||||||
|
for child in children {
|
||||||
|
if let Some(previous) = update_parent(world, *child, parent) {
|
||||||
|
debug_assert!(parent != previous);
|
||||||
|
remove_from_children(world, previous, *child);
|
||||||
|
moved.push(HierarchyEvent::ChildMoved {
|
||||||
|
child: *child,
|
||||||
|
previous_parent: previous,
|
||||||
|
new_parent: parent,
|
||||||
|
});
|
||||||
|
}
|
||||||
|
}
|
||||||
|
push_events(world, moved);
|
||||||
|
}
|
||||||
|
|
||||||
|
fn remove_children(parent: Entity, children: &[Entity], world: &mut World) {
|
||||||
|
let mut events: SmallVec<[HierarchyEvent; 8]> = SmallVec::new();
|
||||||
|
for child in children {
|
||||||
|
world.entity_mut(*child).remove::<Parent>();
|
||||||
|
events.push(HierarchyEvent::ChildRemoved {
|
||||||
|
child: *child,
|
||||||
|
parent,
|
||||||
|
});
|
||||||
|
}
|
||||||
|
push_events(world, events);
|
||||||
|
|
||||||
|
if let Some(mut parent_children) = world.get_mut::<Children>(parent) {
|
||||||
|
parent_children
|
||||||
|
.0
|
||||||
|
.retain(|parent_child| !children.contains(parent_child));
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
/// Command that adds a child to an entity
|
/// Command that adds a child to an entity
|
||||||
#[derive(Debug)]
|
#[derive(Debug)]
|
||||||
@ -20,16 +95,32 @@ pub struct AddChild {
|
|||||||
|
|
||||||
impl Command for AddChild {
|
impl Command for AddChild {
|
||||||
fn write(self, world: &mut World) {
|
fn write(self, world: &mut World) {
|
||||||
world
|
let previous = update_parent(world, self.child, self.parent);
|
||||||
.entity_mut(self.child)
|
if let Some(previous) = previous {
|
||||||
// FIXME: don't erase the previous parent (see #1545)
|
if previous == self.parent {
|
||||||
.insert_bundle((Parent(self.parent), PreviousParent(self.parent)));
|
return;
|
||||||
if let Some(mut children) = world.get_mut::<Children>(self.parent) {
|
}
|
||||||
children.0.push(self.child);
|
remove_from_children(world, previous, self.child);
|
||||||
|
if let Some(mut events) = world.get_resource_mut::<Events<HierarchyEvent>>() {
|
||||||
|
events.send(HierarchyEvent::ChildMoved {
|
||||||
|
child: self.child,
|
||||||
|
previous_parent: previous,
|
||||||
|
new_parent: self.parent,
|
||||||
|
});
|
||||||
|
}
|
||||||
|
} else if let Some(mut events) = world.get_resource_mut::<Events<HierarchyEvent>>() {
|
||||||
|
events.send(HierarchyEvent::ChildAdded {
|
||||||
|
child: self.child,
|
||||||
|
parent: self.parent,
|
||||||
|
});
|
||||||
|
}
|
||||||
|
let mut parent = world.entity_mut(self.parent);
|
||||||
|
if let Some(mut children) = parent.get_mut::<Children>() {
|
||||||
|
if !children.contains(&self.child) {
|
||||||
|
children.0.push(self.child);
|
||||||
|
}
|
||||||
} else {
|
} else {
|
||||||
world
|
parent.insert(Children(smallvec::smallvec![self.child]));
|
||||||
.entity_mut(self.parent)
|
|
||||||
.insert(Children(smallvec::smallvec![self.child]));
|
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
@ -44,20 +135,13 @@ pub struct InsertChildren {
|
|||||||
|
|
||||||
impl Command for InsertChildren {
|
impl Command for InsertChildren {
|
||||||
fn write(self, world: &mut World) {
|
fn write(self, world: &mut World) {
|
||||||
for child in self.children.iter() {
|
update_old_parents(world, self.parent, &self.children);
|
||||||
world
|
let mut parent = world.entity_mut(self.parent);
|
||||||
.entity_mut(*child)
|
if let Some(mut children) = parent.get_mut::<Children>() {
|
||||||
// FIXME: don't erase the previous parent (see #1545)
|
children.0.retain(|value| !self.children.contains(value));
|
||||||
.insert_bundle((Parent(self.parent), PreviousParent(self.parent)));
|
children.0.insert_from_slice(self.index, &self.children);
|
||||||
}
|
} else {
|
||||||
{
|
parent.insert(Children(self.children));
|
||||||
if let Some(mut children) = world.get_mut::<Children>(self.parent) {
|
|
||||||
children.0.insert_from_slice(self.index, &self.children);
|
|
||||||
} else {
|
|
||||||
world
|
|
||||||
.entity_mut(self.parent)
|
|
||||||
.insert(Children(self.children));
|
|
||||||
}
|
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
@ -70,27 +154,14 @@ pub struct PushChildren {
|
|||||||
}
|
}
|
||||||
|
|
||||||
impl Command for PushChildren {
|
impl Command for PushChildren {
|
||||||
fn write(self, world: &mut World) {
|
fn write(mut self, world: &mut World) {
|
||||||
for child in self.children.iter() {
|
update_old_parents(world, self.parent, &self.children);
|
||||||
world
|
let mut parent = world.entity_mut(self.parent);
|
||||||
.entity_mut(*child)
|
if let Some(mut children) = parent.get_mut::<Children>() {
|
||||||
// FIXME: don't erase the previous parent (see #1545)
|
children.0.retain(|child| !self.children.contains(child));
|
||||||
.insert_bundle((Parent(self.parent), PreviousParent(self.parent)));
|
children.0.append(&mut self.children);
|
||||||
}
|
} else {
|
||||||
{
|
parent.insert(Children(self.children));
|
||||||
let mut added = false;
|
|
||||||
if let Some(mut children) = world.get_mut::<Children>(self.parent) {
|
|
||||||
children.0.extend(self.children.iter().cloned());
|
|
||||||
added = true;
|
|
||||||
}
|
|
||||||
|
|
||||||
// NOTE: ideally this is just an else statement, but currently that _incorrectly_ fails
|
|
||||||
// borrow-checking
|
|
||||||
if !added {
|
|
||||||
world
|
|
||||||
.entity_mut(self.parent)
|
|
||||||
.insert(Children(self.children));
|
|
||||||
}
|
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
@ -101,32 +172,8 @@ pub struct RemoveChildren {
|
|||||||
children: SmallVec<[Entity; 8]>,
|
children: SmallVec<[Entity; 8]>,
|
||||||
}
|
}
|
||||||
|
|
||||||
fn remove_children(parent: Entity, children: &[Entity], world: &mut World) {
|
|
||||||
for child in children.iter() {
|
|
||||||
let mut child = world.entity_mut(*child);
|
|
||||||
let mut remove_parent = false;
|
|
||||||
if let Some(child_parent) = child.get_mut::<Parent>() {
|
|
||||||
if child_parent.0 == parent {
|
|
||||||
remove_parent = true;
|
|
||||||
}
|
|
||||||
}
|
|
||||||
if remove_parent {
|
|
||||||
if let Some(parent) = child.remove::<Parent>() {
|
|
||||||
child.insert(PreviousParent(parent.0));
|
|
||||||
}
|
|
||||||
}
|
|
||||||
}
|
|
||||||
// Remove the children from the parents.
|
|
||||||
if let Some(mut parent_children) = world.get_mut::<Children>(parent) {
|
|
||||||
parent_children
|
|
||||||
.0
|
|
||||||
.retain(|parent_child| !children.contains(parent_child));
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
impl Command for RemoveChildren {
|
impl Command for RemoveChildren {
|
||||||
fn write(self, world: &mut World) {
|
fn write(self, world: &mut World) {
|
||||||
// Remove any matching Parent components from the children
|
|
||||||
remove_children(self.parent, &self.children, world);
|
remove_children(self.parent, &self.children, world);
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
@ -285,15 +332,15 @@ impl<'w> WorldChildBuilder<'w> {
|
|||||||
.world
|
.world
|
||||||
.spawn()
|
.spawn()
|
||||||
.insert_bundle(bundle)
|
.insert_bundle(bundle)
|
||||||
.insert_bundle((Parent(parent_entity), PreviousParent(parent_entity)))
|
.insert(Parent(parent_entity))
|
||||||
.id();
|
.id();
|
||||||
|
push_child_unchecked(self.world, parent_entity, entity);
|
||||||
self.current_entity = Some(entity);
|
self.current_entity = Some(entity);
|
||||||
if let Some(mut parent) = self.world.get_entity_mut(parent_entity) {
|
if let Some(mut added) = self.world.get_resource_mut::<Events<HierarchyEvent>>() {
|
||||||
if let Some(mut children) = parent.get_mut::<Children>() {
|
added.send(HierarchyEvent::ChildAdded {
|
||||||
children.0.push(entity);
|
child: entity,
|
||||||
} else {
|
parent: parent_entity,
|
||||||
parent.insert(Children(smallvec::smallvec![entity]));
|
});
|
||||||
}
|
|
||||||
}
|
}
|
||||||
self.world.entity_mut(entity)
|
self.world.entity_mut(entity)
|
||||||
}
|
}
|
||||||
@ -301,18 +348,14 @@ impl<'w> WorldChildBuilder<'w> {
|
|||||||
/// Spawns an [`Entity`] with no components and inserts it into the children defined by the [`WorldChildBuilder`] which adds the [`Parent`] component to it.
|
/// Spawns an [`Entity`] with no components and inserts it into the children defined by the [`WorldChildBuilder`] which adds the [`Parent`] component to it.
|
||||||
pub fn spawn(&mut self) -> EntityMut<'_> {
|
pub fn spawn(&mut self) -> EntityMut<'_> {
|
||||||
let parent_entity = self.parent_entity();
|
let parent_entity = self.parent_entity();
|
||||||
let entity = self
|
let entity = self.world.spawn().insert(Parent(parent_entity)).id();
|
||||||
.world
|
push_child_unchecked(self.world, parent_entity, entity);
|
||||||
.spawn()
|
|
||||||
.insert_bundle((Parent(parent_entity), PreviousParent(parent_entity)))
|
|
||||||
.id();
|
|
||||||
self.current_entity = Some(entity);
|
self.current_entity = Some(entity);
|
||||||
if let Some(mut parent) = self.world.get_entity_mut(parent_entity) {
|
if let Some(mut added) = self.world.get_resource_mut::<Events<HierarchyEvent>>() {
|
||||||
if let Some(mut children) = parent.get_mut::<Children>() {
|
added.send(HierarchyEvent::ChildAdded {
|
||||||
children.0.push(entity);
|
child: entity,
|
||||||
} else {
|
parent: parent_entity,
|
||||||
parent.insert(Children(smallvec::smallvec![entity]));
|
});
|
||||||
}
|
|
||||||
}
|
}
|
||||||
self.world.entity_mut(entity)
|
self.world.entity_mut(entity)
|
||||||
}
|
}
|
||||||
@ -361,16 +404,14 @@ impl<'w> BuildWorldChildren for EntityMut<'w> {
|
|||||||
{
|
{
|
||||||
// SAFETY: parent entity is not modified and its location is updated manually
|
// SAFETY: parent entity is not modified and its location is updated manually
|
||||||
let world = unsafe { self.world_mut() };
|
let world = unsafe { self.world_mut() };
|
||||||
for child in children.iter() {
|
update_old_parents(world, parent, children);
|
||||||
world
|
|
||||||
.entity_mut(*child)
|
|
||||||
// FIXME: don't erase the previous parent (see #1545)
|
|
||||||
.insert_bundle((Parent(parent), PreviousParent(parent)));
|
|
||||||
}
|
|
||||||
// Inserting a bundle in the children entities may change the parent entity's location if they were of the same archetype
|
// Inserting a bundle in the children entities may change the parent entity's location if they were of the same archetype
|
||||||
self.update_location();
|
self.update_location();
|
||||||
}
|
}
|
||||||
if let Some(mut children_component) = self.get_mut::<Children>() {
|
if let Some(mut children_component) = self.get_mut::<Children>() {
|
||||||
|
children_component
|
||||||
|
.0
|
||||||
|
.retain(|value| !children.contains(value));
|
||||||
children_component.0.extend(children.iter().cloned());
|
children_component.0.extend(children.iter().cloned());
|
||||||
} else {
|
} else {
|
||||||
self.insert(Children::with(children));
|
self.insert(Children::with(children));
|
||||||
@ -383,17 +424,15 @@ impl<'w> BuildWorldChildren for EntityMut<'w> {
|
|||||||
{
|
{
|
||||||
// SAFETY: parent entity is not modified and its location is updated manually
|
// SAFETY: parent entity is not modified and its location is updated manually
|
||||||
let world = unsafe { self.world_mut() };
|
let world = unsafe { self.world_mut() };
|
||||||
for child in children.iter() {
|
update_old_parents(world, parent, children);
|
||||||
world
|
|
||||||
.entity_mut(*child)
|
|
||||||
// FIXME: don't erase the previous parent (see #1545)
|
|
||||||
.insert_bundle((Parent(parent), PreviousParent(parent)));
|
|
||||||
}
|
|
||||||
// Inserting a bundle in the children entities may change the parent entity's location if they were of the same archetype
|
// Inserting a bundle in the children entities may change the parent entity's location if they were of the same archetype
|
||||||
self.update_location();
|
self.update_location();
|
||||||
}
|
}
|
||||||
|
|
||||||
if let Some(mut children_component) = self.get_mut::<Children>() {
|
if let Some(mut children_component) = self.get_mut::<Children>() {
|
||||||
|
children_component
|
||||||
|
.0
|
||||||
|
.retain(|value| !children.contains(value));
|
||||||
children_component.0.insert_from_slice(index, children);
|
children_component.0.insert_from_slice(index, children);
|
||||||
} else {
|
} else {
|
||||||
self.insert(Children::with(children));
|
self.insert(Children::with(children));
|
||||||
@ -405,26 +444,7 @@ impl<'w> BuildWorldChildren for EntityMut<'w> {
|
|||||||
let parent = self.id();
|
let parent = self.id();
|
||||||
// SAFETY: This doesn't change the parent's location
|
// SAFETY: This doesn't change the parent's location
|
||||||
let world = unsafe { self.world_mut() };
|
let world = unsafe { self.world_mut() };
|
||||||
for child in children.iter() {
|
remove_children(parent, children, world);
|
||||||
let mut child = world.entity_mut(*child);
|
|
||||||
let mut remove_parent = false;
|
|
||||||
if let Some(child_parent) = child.get_mut::<Parent>() {
|
|
||||||
if child_parent.0 == parent {
|
|
||||||
remove_parent = true;
|
|
||||||
}
|
|
||||||
}
|
|
||||||
if remove_parent {
|
|
||||||
if let Some(parent) = child.remove::<Parent>() {
|
|
||||||
child.insert(PreviousParent(parent.0));
|
|
||||||
}
|
|
||||||
}
|
|
||||||
}
|
|
||||||
// Remove the children from the parents.
|
|
||||||
if let Some(mut parent_children) = world.get_mut::<Children>(parent) {
|
|
||||||
parent_children
|
|
||||||
.0
|
|
||||||
.retain(|parent_child| !children.contains(parent_child));
|
|
||||||
}
|
|
||||||
self
|
self
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
@ -450,13 +470,11 @@ impl<'w> BuildWorldChildren for WorldChildBuilder<'w> {
|
|||||||
let parent = self
|
let parent = self
|
||||||
.current_entity
|
.current_entity
|
||||||
.expect("Cannot add children without a parent. Try creating an entity first.");
|
.expect("Cannot add children without a parent. Try creating an entity first.");
|
||||||
for child in children.iter() {
|
update_old_parents(self.world, parent, children);
|
||||||
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) {
|
if let Some(mut children_component) = self.world.get_mut::<Children>(parent) {
|
||||||
|
children_component
|
||||||
|
.0
|
||||||
|
.retain(|value| !children.contains(value));
|
||||||
children_component.0.extend(children.iter().cloned());
|
children_component.0.extend(children.iter().cloned());
|
||||||
} else {
|
} else {
|
||||||
self.world
|
self.world
|
||||||
@ -470,14 +488,11 @@ impl<'w> BuildWorldChildren for WorldChildBuilder<'w> {
|
|||||||
let parent = self
|
let parent = self
|
||||||
.current_entity
|
.current_entity
|
||||||
.expect("Cannot add children without a parent. Try creating an entity first.");
|
.expect("Cannot add children without a parent. Try creating an entity first.");
|
||||||
|
update_old_parents(self.world, parent, children);
|
||||||
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) {
|
if let Some(mut children_component) = self.world.get_mut::<Children>(parent) {
|
||||||
|
children_component
|
||||||
|
.0
|
||||||
|
.retain(|value| !children.contains(value));
|
||||||
children_component.0.insert_from_slice(index, children);
|
children_component.0.insert_from_slice(index, children);
|
||||||
} else {
|
} else {
|
||||||
self.world
|
self.world
|
||||||
@ -499,6 +514,8 @@ impl<'w> BuildWorldChildren for WorldChildBuilder<'w> {
|
|||||||
|
|
||||||
#[cfg(test)]
|
#[cfg(test)]
|
||||||
mod tests {
|
mod tests {
|
||||||
|
use super::{BuildChildren, BuildWorldChildren};
|
||||||
|
use crate::prelude::{Children, Parent};
|
||||||
use smallvec::{smallvec, SmallVec};
|
use smallvec::{smallvec, SmallVec};
|
||||||
|
|
||||||
use bevy_ecs::{
|
use bevy_ecs::{
|
||||||
@ -508,10 +525,6 @@ mod tests {
|
|||||||
world::World,
|
world::World,
|
||||||
};
|
};
|
||||||
|
|
||||||
use crate::prelude::{Children, Parent, PreviousParent};
|
|
||||||
|
|
||||||
use super::{BuildChildren, BuildWorldChildren};
|
|
||||||
|
|
||||||
#[derive(Component)]
|
#[derive(Component)]
|
||||||
struct C(u32);
|
struct C(u32);
|
||||||
|
|
||||||
@ -538,20 +551,13 @@ mod tests {
|
|||||||
assert_eq!(*world.get::<Parent>(children[0]).unwrap(), Parent(parent));
|
assert_eq!(*world.get::<Parent>(children[0]).unwrap(), Parent(parent));
|
||||||
assert_eq!(*world.get::<Parent>(children[1]).unwrap(), Parent(parent));
|
assert_eq!(*world.get::<Parent>(children[1]).unwrap(), Parent(parent));
|
||||||
|
|
||||||
assert_eq!(
|
assert_eq!(*world.get::<Parent>(children[0]).unwrap(), Parent(parent));
|
||||||
*world.get::<PreviousParent>(children[0]).unwrap(),
|
assert_eq!(*world.get::<Parent>(children[1]).unwrap(), Parent(parent));
|
||||||
PreviousParent(parent)
|
|
||||||
);
|
|
||||||
assert_eq!(
|
|
||||||
*world.get::<PreviousParent>(children[1]).unwrap(),
|
|
||||||
PreviousParent(parent)
|
|
||||||
);
|
|
||||||
}
|
}
|
||||||
|
|
||||||
#[test]
|
#[test]
|
||||||
fn push_and_insert_and_remove_children_commands() {
|
fn push_and_insert_and_remove_children_commands() {
|
||||||
let mut world = World::default();
|
let mut world = World::default();
|
||||||
|
|
||||||
let entities = world
|
let entities = world
|
||||||
.spawn_batch(vec![(C(1),), (C(2),), (C(3),), (C(4),), (C(5),)])
|
.spawn_batch(vec![(C(1),), (C(2),), (C(3),), (C(4),), (C(5),)])
|
||||||
.collect::<Vec<Entity>>();
|
.collect::<Vec<Entity>>();
|
||||||
@ -577,14 +583,8 @@ mod tests {
|
|||||||
assert_eq!(*world.get::<Parent>(child1).unwrap(), Parent(parent));
|
assert_eq!(*world.get::<Parent>(child1).unwrap(), Parent(parent));
|
||||||
assert_eq!(*world.get::<Parent>(child2).unwrap(), Parent(parent));
|
assert_eq!(*world.get::<Parent>(child2).unwrap(), Parent(parent));
|
||||||
|
|
||||||
assert_eq!(
|
assert_eq!(*world.get::<Parent>(child1).unwrap(), Parent(parent));
|
||||||
*world.get::<PreviousParent>(child1).unwrap(),
|
assert_eq!(*world.get::<Parent>(child2).unwrap(), Parent(parent));
|
||||||
PreviousParent(parent)
|
|
||||||
);
|
|
||||||
assert_eq!(
|
|
||||||
*world.get::<PreviousParent>(child2).unwrap(),
|
|
||||||
PreviousParent(parent)
|
|
||||||
);
|
|
||||||
|
|
||||||
{
|
{
|
||||||
let mut commands = Commands::new(&mut queue, &world);
|
let mut commands = Commands::new(&mut queue, &world);
|
||||||
@ -599,14 +599,8 @@ mod tests {
|
|||||||
);
|
);
|
||||||
assert_eq!(*world.get::<Parent>(child3).unwrap(), Parent(parent));
|
assert_eq!(*world.get::<Parent>(child3).unwrap(), Parent(parent));
|
||||||
assert_eq!(*world.get::<Parent>(child4).unwrap(), Parent(parent));
|
assert_eq!(*world.get::<Parent>(child4).unwrap(), Parent(parent));
|
||||||
assert_eq!(
|
assert_eq!(*world.get::<Parent>(child3).unwrap(), Parent(parent));
|
||||||
*world.get::<PreviousParent>(child3).unwrap(),
|
assert_eq!(*world.get::<Parent>(child4).unwrap(), Parent(parent));
|
||||||
PreviousParent(parent)
|
|
||||||
);
|
|
||||||
assert_eq!(
|
|
||||||
*world.get::<PreviousParent>(child4).unwrap(),
|
|
||||||
PreviousParent(parent)
|
|
||||||
);
|
|
||||||
|
|
||||||
let remove_children = [child1, child4];
|
let remove_children = [child1, child4];
|
||||||
{
|
{
|
||||||
@ -622,20 +616,11 @@ mod tests {
|
|||||||
);
|
);
|
||||||
assert!(world.get::<Parent>(child1).is_none());
|
assert!(world.get::<Parent>(child1).is_none());
|
||||||
assert!(world.get::<Parent>(child4).is_none());
|
assert!(world.get::<Parent>(child4).is_none());
|
||||||
assert_eq!(
|
|
||||||
*world.get::<PreviousParent>(child1).unwrap(),
|
|
||||||
PreviousParent(parent)
|
|
||||||
);
|
|
||||||
assert_eq!(
|
|
||||||
*world.get::<PreviousParent>(child4).unwrap(),
|
|
||||||
PreviousParent(parent)
|
|
||||||
);
|
|
||||||
}
|
}
|
||||||
|
|
||||||
#[test]
|
#[test]
|
||||||
fn push_and_insert_and_remove_children_world() {
|
fn push_and_insert_and_remove_children_world() {
|
||||||
let mut world = World::default();
|
let mut world = World::default();
|
||||||
|
|
||||||
let entities = world
|
let entities = world
|
||||||
.spawn_batch(vec![(C(1),), (C(2),), (C(3),), (C(4),), (C(5),)])
|
.spawn_batch(vec![(C(1),), (C(2),), (C(3),), (C(4),), (C(5),)])
|
||||||
.collect::<Vec<Entity>>();
|
.collect::<Vec<Entity>>();
|
||||||
@ -656,14 +641,8 @@ mod tests {
|
|||||||
assert_eq!(*world.get::<Parent>(child1).unwrap(), Parent(parent));
|
assert_eq!(*world.get::<Parent>(child1).unwrap(), Parent(parent));
|
||||||
assert_eq!(*world.get::<Parent>(child2).unwrap(), Parent(parent));
|
assert_eq!(*world.get::<Parent>(child2).unwrap(), Parent(parent));
|
||||||
|
|
||||||
assert_eq!(
|
assert_eq!(*world.get::<Parent>(child1).unwrap(), Parent(parent));
|
||||||
*world.get::<PreviousParent>(child1).unwrap(),
|
assert_eq!(*world.get::<Parent>(child2).unwrap(), Parent(parent));
|
||||||
PreviousParent(parent)
|
|
||||||
);
|
|
||||||
assert_eq!(
|
|
||||||
*world.get::<PreviousParent>(child2).unwrap(),
|
|
||||||
PreviousParent(parent)
|
|
||||||
);
|
|
||||||
|
|
||||||
world.entity_mut(parent).insert_children(1, &entities[3..]);
|
world.entity_mut(parent).insert_children(1, &entities[3..]);
|
||||||
let expected_children: SmallVec<[Entity; 8]> = smallvec![child1, child3, child4, child2];
|
let expected_children: SmallVec<[Entity; 8]> = smallvec![child1, child3, child4, child2];
|
||||||
@ -673,14 +652,8 @@ mod tests {
|
|||||||
);
|
);
|
||||||
assert_eq!(*world.get::<Parent>(child3).unwrap(), Parent(parent));
|
assert_eq!(*world.get::<Parent>(child3).unwrap(), Parent(parent));
|
||||||
assert_eq!(*world.get::<Parent>(child4).unwrap(), Parent(parent));
|
assert_eq!(*world.get::<Parent>(child4).unwrap(), Parent(parent));
|
||||||
assert_eq!(
|
assert_eq!(*world.get::<Parent>(child3).unwrap(), Parent(parent));
|
||||||
*world.get::<PreviousParent>(child3).unwrap(),
|
assert_eq!(*world.get::<Parent>(child4).unwrap(), Parent(parent));
|
||||||
PreviousParent(parent)
|
|
||||||
);
|
|
||||||
assert_eq!(
|
|
||||||
*world.get::<PreviousParent>(child4).unwrap(),
|
|
||||||
PreviousParent(parent)
|
|
||||||
);
|
|
||||||
|
|
||||||
let remove_children = [child1, child4];
|
let remove_children = [child1, child4];
|
||||||
world.entity_mut(parent).remove_children(&remove_children);
|
world.entity_mut(parent).remove_children(&remove_children);
|
||||||
@ -691,14 +664,6 @@ mod tests {
|
|||||||
);
|
);
|
||||||
assert!(world.get::<Parent>(child1).is_none());
|
assert!(world.get::<Parent>(child1).is_none());
|
||||||
assert!(world.get::<Parent>(child4).is_none());
|
assert!(world.get::<Parent>(child4).is_none());
|
||||||
assert_eq!(
|
|
||||||
*world.get::<PreviousParent>(child1).unwrap(),
|
|
||||||
PreviousParent(parent)
|
|
||||||
);
|
|
||||||
assert_eq!(
|
|
||||||
*world.get::<PreviousParent>(child4).unwrap(),
|
|
||||||
PreviousParent(parent)
|
|
||||||
);
|
|
||||||
}
|
}
|
||||||
|
|
||||||
#[test]
|
#[test]
|
||||||
|
@ -1,7 +1,9 @@
|
|||||||
use bevy_ecs::{
|
use bevy_ecs::{
|
||||||
component::Component,
|
component::Component,
|
||||||
entity::{Entity, EntityMap, MapEntities, MapEntitiesError},
|
entity::{Entity, EntityMap, MapEntities, MapEntitiesError},
|
||||||
|
prelude::FromWorld,
|
||||||
reflect::{ReflectComponent, ReflectMapEntities},
|
reflect::{ReflectComponent, ReflectMapEntities},
|
||||||
|
world::World,
|
||||||
};
|
};
|
||||||
use bevy_reflect::Reflect;
|
use bevy_reflect::Reflect;
|
||||||
use core::slice;
|
use core::slice;
|
||||||
@ -9,7 +11,7 @@ use smallvec::SmallVec;
|
|||||||
use std::ops::Deref;
|
use std::ops::Deref;
|
||||||
|
|
||||||
/// Contains references to the child entities of this entity
|
/// Contains references to the child entities of this entity
|
||||||
#[derive(Component, Default, Clone, Debug, Reflect)]
|
#[derive(Component, Debug, Reflect)]
|
||||||
#[reflect(Component, MapEntities)]
|
#[reflect(Component, MapEntities)]
|
||||||
pub struct Children(pub(crate) SmallVec<[Entity; 8]>);
|
pub struct Children(pub(crate) SmallVec<[Entity; 8]>);
|
||||||
|
|
||||||
@ -23,6 +25,16 @@ impl MapEntities for Children {
|
|||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
// 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
|
||||||
|
// into better ways to handle cases like this.
|
||||||
|
impl FromWorld for Children {
|
||||||
|
fn from_world(_world: &mut World) -> Self {
|
||||||
|
Children(SmallVec::new())
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
impl Children {
|
impl Children {
|
||||||
/// Builds and returns a [`Children`] component with the given entities
|
/// Builds and returns a [`Children`] component with the given entities
|
||||||
pub fn with(entity: &[Entity]) -> Self {
|
pub fn with(entity: &[Entity]) -> Self {
|
||||||
|
@ -2,4 +2,4 @@ mod children;
|
|||||||
mod parent;
|
mod parent;
|
||||||
|
|
||||||
pub use children::Children;
|
pub use children::Children;
|
||||||
pub use parent::{Parent, PreviousParent};
|
pub use parent::Parent;
|
||||||
|
@ -5,16 +5,23 @@ use bevy_ecs::{
|
|||||||
world::{FromWorld, World},
|
world::{FromWorld, World},
|
||||||
};
|
};
|
||||||
use bevy_reflect::Reflect;
|
use bevy_reflect::Reflect;
|
||||||
use std::ops::{Deref, DerefMut};
|
use std::ops::Deref;
|
||||||
|
|
||||||
/// Holds a reference to the parent entity of this entity.
|
/// Holds a reference to the parent entity of this entity.
|
||||||
/// This component should only be present on entities that actually have a parent entity.
|
/// This component should only be present on entities that actually have a parent entity.
|
||||||
#[derive(Component, Debug, Copy, Clone, Eq, PartialEq, Reflect)]
|
#[derive(Component, Debug, Eq, PartialEq, Reflect)]
|
||||||
#[reflect(Component, MapEntities, PartialEq)]
|
#[reflect(Component, MapEntities, PartialEq)]
|
||||||
pub struct Parent(pub Entity);
|
pub struct Parent(pub(crate) Entity);
|
||||||
|
|
||||||
// TODO: We need to impl either FromWorld or Default so Parent can be registered as Properties.
|
impl Parent {
|
||||||
// This is because Properties deserialize by creating an instance and apply a patch on top.
|
/// Gets the [`Entity`] ID of the parent.
|
||||||
|
pub fn get(&self) -> Entity {
|
||||||
|
self.0
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
// TODO: We need to impl either FromWorld or Default so Parent can be registered as Reflect.
|
||||||
|
// This is because Reflect deserialize by creating an instance and apply a patch on top.
|
||||||
// However Parent should only ever be set with a real user-defined entity. Its worth looking into
|
// However Parent should only ever be set with a real user-defined entity. Its worth looking into
|
||||||
// better ways to handle cases like this.
|
// better ways to handle cases like this.
|
||||||
impl FromWorld for Parent {
|
impl FromWorld for Parent {
|
||||||
@ -41,32 +48,3 @@ impl Deref for Parent {
|
|||||||
&self.0
|
&self.0
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
impl DerefMut for Parent {
|
|
||||||
fn deref_mut(&mut self) -> &mut Self::Target {
|
|
||||||
&mut self.0
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
/// Component that holds the [`Parent`] this entity had previously
|
|
||||||
#[derive(Component, Debug, Copy, Clone, Eq, PartialEq, Reflect)]
|
|
||||||
#[reflect(Component, MapEntities, PartialEq)]
|
|
||||||
pub struct PreviousParent(pub(crate) Entity);
|
|
||||||
|
|
||||||
impl MapEntities for PreviousParent {
|
|
||||||
fn map_entities(&mut self, entity_map: &EntityMap) -> Result<(), MapEntitiesError> {
|
|
||||||
// PreviousParent of an entity in the new world can be in outside world, in which
|
|
||||||
// case it should not be mapped.
|
|
||||||
if let Ok(mapped_entity) = entity_map.get(self.0) {
|
|
||||||
self.0 = mapped_entity;
|
|
||||||
}
|
|
||||||
Ok(())
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
// TODO: Better handle this case see `impl FromWorld for Parent`
|
|
||||||
impl FromWorld for PreviousParent {
|
|
||||||
fn from_world(_world: &mut World) -> Self {
|
|
||||||
PreviousParent(Entity::from_raw(u32::MAX))
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
32
crates/bevy_hierarchy/src/events.rs
Normal file
32
crates/bevy_hierarchy/src/events.rs
Normal file
@ -0,0 +1,32 @@
|
|||||||
|
use bevy_ecs::prelude::Entity;
|
||||||
|
|
||||||
|
/// A [`Event`] that is fired whenever there is a change in the world's
|
||||||
|
/// hierarchy.
|
||||||
|
///
|
||||||
|
/// [`Event`]: bevy_ecs::event::Event
|
||||||
|
#[derive(Debug, Clone)]
|
||||||
|
pub enum HierarchyEvent {
|
||||||
|
/// Fired whenever an [`Entity`] is added as a child to a new parent.
|
||||||
|
ChildAdded {
|
||||||
|
/// The child that added
|
||||||
|
child: Entity,
|
||||||
|
/// The parent the child was added to
|
||||||
|
parent: Entity,
|
||||||
|
},
|
||||||
|
/// Fired whenever an child [`Entity`] is removed from is parent.
|
||||||
|
ChildRemoved {
|
||||||
|
/// The child that removed
|
||||||
|
child: Entity,
|
||||||
|
/// The parent the child was removed from
|
||||||
|
parent: Entity,
|
||||||
|
},
|
||||||
|
/// Fired whenever an child [`Entity`] is moved to a new parent.
|
||||||
|
ChildMoved {
|
||||||
|
/// The child that moved
|
||||||
|
child: Entity,
|
||||||
|
/// The parent the child was removed from
|
||||||
|
previous_parent: Entity,
|
||||||
|
/// The parent the child was added to
|
||||||
|
new_parent: Entity,
|
||||||
|
},
|
||||||
|
}
|
@ -13,8 +13,8 @@ pub use hierarchy::*;
|
|||||||
mod child_builder;
|
mod child_builder;
|
||||||
pub use child_builder::*;
|
pub use child_builder::*;
|
||||||
|
|
||||||
mod systems;
|
mod events;
|
||||||
pub use systems::*;
|
pub use events::*;
|
||||||
|
|
||||||
#[doc(hidden)]
|
#[doc(hidden)]
|
||||||
pub mod prelude {
|
pub mod prelude {
|
||||||
@ -23,31 +23,15 @@ pub mod prelude {
|
|||||||
}
|
}
|
||||||
|
|
||||||
use bevy_app::prelude::*;
|
use bevy_app::prelude::*;
|
||||||
use bevy_ecs::prelude::*;
|
|
||||||
|
|
||||||
/// The base plugin for handling [`Parent`] and [`Children`] components
|
/// The base plugin for handling [`Parent`] and [`Children`] components
|
||||||
#[derive(Default)]
|
#[derive(Default)]
|
||||||
pub struct HierarchyPlugin;
|
pub struct HierarchyPlugin;
|
||||||
|
|
||||||
/// Label enum for the systems relating to hierarchy upkeep
|
|
||||||
#[derive(Debug, Hash, PartialEq, Eq, Clone, SystemLabel)]
|
|
||||||
pub enum HierarchySystem {
|
|
||||||
/// Updates [`Parent`] when changes in the hierarchy occur
|
|
||||||
ParentUpdate,
|
|
||||||
}
|
|
||||||
|
|
||||||
impl Plugin for HierarchyPlugin {
|
impl Plugin for HierarchyPlugin {
|
||||||
fn build(&self, app: &mut App) {
|
fn build(&self, app: &mut App) {
|
||||||
app.register_type::<Children>()
|
app.register_type::<Children>()
|
||||||
.register_type::<Parent>()
|
.register_type::<Parent>()
|
||||||
.register_type::<PreviousParent>()
|
.add_event::<HierarchyEvent>();
|
||||||
.add_startup_system_to_stage(
|
|
||||||
StartupStage::PostStartup,
|
|
||||||
parent_update_system.label(HierarchySystem::ParentUpdate),
|
|
||||||
)
|
|
||||||
.add_system_to_stage(
|
|
||||||
CoreStage::PostUpdate,
|
|
||||||
parent_update_system.label(HierarchySystem::ParentUpdate),
|
|
||||||
);
|
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
@ -1,72 +0,0 @@
|
|||||||
use crate::components::*;
|
|
||||||
use bevy_ecs::{
|
|
||||||
entity::Entity,
|
|
||||||
prelude::Changed,
|
|
||||||
query::Without,
|
|
||||||
system::{Commands, Query},
|
|
||||||
};
|
|
||||||
use bevy_utils::HashMap;
|
|
||||||
use smallvec::SmallVec;
|
|
||||||
|
|
||||||
/// Updates parents when the hierarchy is changed
|
|
||||||
pub fn parent_update_system(
|
|
||||||
mut commands: Commands,
|
|
||||||
removed_parent_query: Query<(Entity, &PreviousParent), Without<Parent>>,
|
|
||||||
mut parent_query: Query<(Entity, &Parent, Option<&mut PreviousParent>), Changed<Parent>>,
|
|
||||||
mut children_query: Query<&mut Children>,
|
|
||||||
) {
|
|
||||||
// Entities with a missing `Parent` (ie. ones that have a `PreviousParent`), remove
|
|
||||||
// them from the `Children` of the `PreviousParent`.
|
|
||||||
for (entity, previous_parent) in removed_parent_query.iter() {
|
|
||||||
if let Ok(mut previous_parent_children) = children_query.get_mut(previous_parent.0) {
|
|
||||||
previous_parent_children.0.retain(|e| *e != entity);
|
|
||||||
commands.entity(entity).remove::<PreviousParent>();
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
// Tracks all newly created `Children` Components this frame.
|
|
||||||
let mut children_additions = HashMap::<Entity, SmallVec<[Entity; 8]>>::default();
|
|
||||||
|
|
||||||
// Entities with a changed Parent (that also have a PreviousParent, even if None)
|
|
||||||
for (entity, parent, possible_previous_parent) in parent_query.iter_mut() {
|
|
||||||
if let Some(mut previous_parent) = possible_previous_parent {
|
|
||||||
// New and previous point to the same Entity, carry on, nothing to see here.
|
|
||||||
if previous_parent.0 == parent.0 {
|
|
||||||
continue;
|
|
||||||
}
|
|
||||||
|
|
||||||
// Remove from `PreviousParent.Children`.
|
|
||||||
if let Ok(mut previous_parent_children) = children_query.get_mut(previous_parent.0) {
|
|
||||||
(*previous_parent_children).0.retain(|e| *e != entity);
|
|
||||||
}
|
|
||||||
|
|
||||||
// Set `PreviousParent = Parent`.
|
|
||||||
*previous_parent = PreviousParent(parent.0);
|
|
||||||
} else {
|
|
||||||
commands.entity(entity).insert(PreviousParent(parent.0));
|
|
||||||
};
|
|
||||||
|
|
||||||
// Add to the parent's `Children` (either the real component, or
|
|
||||||
// `children_additions`).
|
|
||||||
if let Ok(mut new_parent_children) = children_query.get_mut(parent.0) {
|
|
||||||
// This is the parent
|
|
||||||
// PERF: Ideally we shouldn't need to check for duplicates
|
|
||||||
if !(*new_parent_children).0.contains(&entity) {
|
|
||||||
(*new_parent_children).0.push(entity);
|
|
||||||
}
|
|
||||||
} else {
|
|
||||||
// The parent doesn't have a children entity, lets add it
|
|
||||||
children_additions
|
|
||||||
.entry(parent.0)
|
|
||||||
.or_insert_with(Default::default)
|
|
||||||
.push(entity);
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
// Flush the `children_additions` to the command buffer. It is stored separate to
|
|
||||||
// collect multiple new children that point to the same parent into the same
|
|
||||||
// SmallVec, and to prevent redundant add+remove operations.
|
|
||||||
children_additions.iter().for_each(|(e, v)| {
|
|
||||||
commands.entity(*e).insert(Children::with(v));
|
|
||||||
});
|
|
||||||
}
|
|
@ -14,7 +14,6 @@ pub mod prelude {
|
|||||||
|
|
||||||
use bevy_app::prelude::*;
|
use bevy_app::prelude::*;
|
||||||
use bevy_ecs::prelude::*;
|
use bevy_ecs::prelude::*;
|
||||||
use bevy_hierarchy::HierarchySystem;
|
|
||||||
use prelude::{GlobalTransform, Transform};
|
use prelude::{GlobalTransform, Transform};
|
||||||
|
|
||||||
/// A [`Bundle`] of the [`Transform`] and [`GlobalTransform`]
|
/// A [`Bundle`] of the [`Transform`] and [`GlobalTransform`]
|
||||||
@ -92,18 +91,14 @@ impl Plugin for TransformPlugin {
|
|||||||
fn build(&self, app: &mut App) {
|
fn build(&self, app: &mut App) {
|
||||||
app.register_type::<Transform>()
|
app.register_type::<Transform>()
|
||||||
.register_type::<GlobalTransform>()
|
.register_type::<GlobalTransform>()
|
||||||
// Adding these to startup ensures the first update is "correct"
|
// add transform systems to startup so the first update is "correct"
|
||||||
.add_startup_system_to_stage(
|
.add_startup_system_to_stage(
|
||||||
StartupStage::PostStartup,
|
StartupStage::PostStartup,
|
||||||
systems::transform_propagate_system
|
systems::transform_propagate_system.label(TransformSystem::TransformPropagate),
|
||||||
.label(TransformSystem::TransformPropagate)
|
|
||||||
.after(HierarchySystem::ParentUpdate),
|
|
||||||
)
|
)
|
||||||
.add_system_to_stage(
|
.add_system_to_stage(
|
||||||
CoreStage::PostUpdate,
|
CoreStage::PostUpdate,
|
||||||
systems::transform_propagate_system
|
systems::transform_propagate_system.label(TransformSystem::TransformPropagate),
|
||||||
.label(TransformSystem::TransformPropagate)
|
|
||||||
.after(HierarchySystem::ParentUpdate),
|
|
||||||
);
|
);
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
@ -67,7 +67,7 @@ fn propagate_recursive(
|
|||||||
transform_query.get_mut(entity).map_err(drop)?;
|
transform_query.get_mut(entity).map_err(drop)?;
|
||||||
// Note that for parallelising, this check cannot occur here, since there is an `&mut GlobalTransform` (in global_transform)
|
// Note that for parallelising, this check cannot occur here, since there is an `&mut GlobalTransform` (in global_transform)
|
||||||
assert_eq!(
|
assert_eq!(
|
||||||
child_parent.0, expected_parent,
|
child_parent.get(), expected_parent,
|
||||||
"Malformed hierarchy. This probably means that your hierarchy has been improperly maintained, or contains a cycle"
|
"Malformed hierarchy. This probably means that your hierarchy has been improperly maintained, or contains a cycle"
|
||||||
);
|
);
|
||||||
changed |= transform_changed;
|
changed |= transform_changed;
|
||||||
@ -103,17 +103,14 @@ mod test {
|
|||||||
use crate::components::{GlobalTransform, Transform};
|
use crate::components::{GlobalTransform, Transform};
|
||||||
use crate::systems::transform_propagate_system;
|
use crate::systems::transform_propagate_system;
|
||||||
use crate::TransformBundle;
|
use crate::TransformBundle;
|
||||||
use bevy_hierarchy::{
|
use bevy_hierarchy::{BuildChildren, BuildWorldChildren, Children, Parent};
|
||||||
parent_update_system, BuildChildren, BuildWorldChildren, Children, Parent,
|
|
||||||
};
|
|
||||||
|
|
||||||
#[test]
|
#[test]
|
||||||
fn did_propagate() {
|
fn did_propagate() {
|
||||||
let mut world = World::default();
|
let mut world = World::default();
|
||||||
|
|
||||||
let mut update_stage = SystemStage::parallel();
|
let mut update_stage = SystemStage::parallel();
|
||||||
update_stage.add_system(parent_update_system);
|
update_stage.add_system(transform_propagate_system);
|
||||||
update_stage.add_system(transform_propagate_system.after(parent_update_system));
|
|
||||||
|
|
||||||
let mut schedule = Schedule::default();
|
let mut schedule = Schedule::default();
|
||||||
schedule.add_stage("update", update_stage);
|
schedule.add_stage("update", update_stage);
|
||||||
@ -155,10 +152,8 @@ mod test {
|
|||||||
#[test]
|
#[test]
|
||||||
fn did_propagate_command_buffer() {
|
fn did_propagate_command_buffer() {
|
||||||
let mut world = World::default();
|
let mut world = World::default();
|
||||||
|
|
||||||
let mut update_stage = SystemStage::parallel();
|
let mut update_stage = SystemStage::parallel();
|
||||||
update_stage.add_system(parent_update_system);
|
update_stage.add_system(transform_propagate_system);
|
||||||
update_stage.add_system(transform_propagate_system.after(parent_update_system));
|
|
||||||
|
|
||||||
let mut schedule = Schedule::default();
|
let mut schedule = Schedule::default();
|
||||||
schedule.add_stage("update", update_stage);
|
schedule.add_stage("update", update_stage);
|
||||||
@ -200,36 +195,38 @@ mod test {
|
|||||||
let mut world = World::default();
|
let mut world = World::default();
|
||||||
|
|
||||||
let mut update_stage = SystemStage::parallel();
|
let mut update_stage = SystemStage::parallel();
|
||||||
update_stage.add_system(parent_update_system);
|
update_stage.add_system(transform_propagate_system);
|
||||||
update_stage.add_system(transform_propagate_system.after(parent_update_system));
|
|
||||||
|
|
||||||
let mut schedule = Schedule::default();
|
let mut schedule = Schedule::default();
|
||||||
schedule.add_stage("update", update_stage);
|
schedule.add_stage("update", update_stage);
|
||||||
|
|
||||||
// Add parent entities
|
// Add parent entities
|
||||||
let mut command_queue = CommandQueue::default();
|
|
||||||
let mut commands = Commands::new(&mut command_queue, &world);
|
|
||||||
let mut children = Vec::new();
|
let mut children = Vec::new();
|
||||||
let parent = commands
|
let parent = {
|
||||||
.spawn()
|
let mut command_queue = CommandQueue::default();
|
||||||
.insert(Transform::from_xyz(1.0, 0.0, 0.0))
|
let mut commands = Commands::new(&mut command_queue, &world);
|
||||||
.id();
|
let parent = commands
|
||||||
commands.entity(parent).with_children(|parent| {
|
.spawn()
|
||||||
children.push(
|
.insert(Transform::from_xyz(1.0, 0.0, 0.0))
|
||||||
parent
|
.id();
|
||||||
.spawn()
|
commands.entity(parent).with_children(|parent| {
|
||||||
.insert(Transform::from_xyz(0.0, 2.0, 0.0))
|
children.push(
|
||||||
.id(),
|
parent
|
||||||
);
|
.spawn()
|
||||||
children.push(
|
.insert(Transform::from_xyz(0.0, 2.0, 0.0))
|
||||||
parent
|
.id(),
|
||||||
.spawn()
|
);
|
||||||
.insert(Transform::from_xyz(0.0, 3.0, 0.0))
|
children.push(
|
||||||
.id(),
|
parent
|
||||||
);
|
.spawn()
|
||||||
});
|
.insert(Transform::from_xyz(0.0, 3.0, 0.0))
|
||||||
command_queue.apply(&mut world);
|
.id(),
|
||||||
schedule.run(&mut world);
|
);
|
||||||
|
});
|
||||||
|
command_queue.apply(&mut world);
|
||||||
|
schedule.run(&mut world);
|
||||||
|
parent
|
||||||
|
};
|
||||||
|
|
||||||
assert_eq!(
|
assert_eq!(
|
||||||
world
|
world
|
||||||
@ -242,9 +239,13 @@ mod test {
|
|||||||
);
|
);
|
||||||
|
|
||||||
// Parent `e1` to `e2`.
|
// Parent `e1` to `e2`.
|
||||||
(*world.get_mut::<Parent>(children[0]).unwrap()).0 = children[1];
|
{
|
||||||
|
let mut command_queue = CommandQueue::default();
|
||||||
schedule.run(&mut world);
|
let mut commands = Commands::new(&mut command_queue, &world);
|
||||||
|
commands.entity(children[1]).add_child(children[0]);
|
||||||
|
command_queue.apply(&mut world);
|
||||||
|
schedule.run(&mut world);
|
||||||
|
}
|
||||||
|
|
||||||
assert_eq!(
|
assert_eq!(
|
||||||
world
|
world
|
||||||
@ -285,36 +286,28 @@ mod test {
|
|||||||
fn correct_transforms_when_no_children() {
|
fn correct_transforms_when_no_children() {
|
||||||
let mut app = App::new();
|
let mut app = App::new();
|
||||||
|
|
||||||
app.add_system(parent_update_system);
|
app.add_system(transform_propagate_system);
|
||||||
app.add_system(transform_propagate_system.after(parent_update_system));
|
|
||||||
|
|
||||||
let translation = vec3(1.0, 0.0, 0.0);
|
let translation = vec3(1.0, 0.0, 0.0);
|
||||||
|
|
||||||
|
// These will be overwritten.
|
||||||
|
let mut child = Entity::from_raw(0);
|
||||||
|
let mut grandchild = Entity::from_raw(1);
|
||||||
let parent = app
|
let parent = app
|
||||||
.world
|
.world
|
||||||
.spawn()
|
.spawn()
|
||||||
.insert(Transform::from_translation(translation))
|
.insert(Transform::from_translation(translation))
|
||||||
.insert(GlobalTransform::default())
|
.insert(GlobalTransform::default())
|
||||||
.id();
|
.with_children(|builder| {
|
||||||
|
child = builder
|
||||||
let child = app
|
.spawn_bundle((Transform::identity(), GlobalTransform::default()))
|
||||||
.world
|
.with_children(|builder| {
|
||||||
.spawn()
|
grandchild = builder
|
||||||
.insert_bundle((
|
.spawn_bundle((Transform::identity(), GlobalTransform::default()))
|
||||||
Transform::identity(),
|
.id();
|
||||||
GlobalTransform::default(),
|
})
|
||||||
Parent(parent),
|
.id();
|
||||||
))
|
})
|
||||||
.id();
|
|
||||||
|
|
||||||
let grandchild = app
|
|
||||||
.world
|
|
||||||
.spawn()
|
|
||||||
.insert_bundle((
|
|
||||||
Transform::identity(),
|
|
||||||
GlobalTransform::default(),
|
|
||||||
Parent(child),
|
|
||||||
))
|
|
||||||
.id();
|
.id();
|
||||||
|
|
||||||
app.update();
|
app.update();
|
||||||
@ -336,37 +329,47 @@ mod test {
|
|||||||
);
|
);
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
#[test]
|
#[test]
|
||||||
#[should_panic]
|
#[should_panic]
|
||||||
fn panic_when_hierarchy_cycle() {
|
fn panic_when_hierarchy_cycle() {
|
||||||
let mut world = World::default();
|
// We cannot directly edit Parent and Children, so we use a temp world to break
|
||||||
// This test is run on a single thread in order to avoid breaking the global task pool by panicking
|
// the hierarchy's invariants.
|
||||||
// This fixes the flaky tests reported in https://github.com/bevyengine/bevy/issues/4996
|
let mut temp = World::new();
|
||||||
let mut update_stage = SystemStage::single_threaded();
|
let mut app = App::new();
|
||||||
|
|
||||||
update_stage.add_system(parent_update_system);
|
app.add_system(transform_propagate_system);
|
||||||
update_stage.add_system(transform_propagate_system.after(parent_update_system));
|
|
||||||
|
|
||||||
let child = world
|
fn setup_world(world: &mut World) -> (Entity, Entity) {
|
||||||
|
let mut grandchild = Entity::from_raw(0);
|
||||||
|
let child = world
|
||||||
|
.spawn()
|
||||||
|
.insert_bundle((Transform::identity(), GlobalTransform::default()))
|
||||||
|
.with_children(|builder| {
|
||||||
|
grandchild = builder
|
||||||
|
.spawn()
|
||||||
|
.insert_bundle((Transform::identity(), GlobalTransform::default()))
|
||||||
|
.id();
|
||||||
|
})
|
||||||
|
.id();
|
||||||
|
(child, grandchild)
|
||||||
|
}
|
||||||
|
|
||||||
|
let (temp_child, temp_grandchild) = setup_world(&mut temp);
|
||||||
|
let (child, grandchild) = setup_world(&mut app.world);
|
||||||
|
|
||||||
|
assert_eq!(temp_child, child);
|
||||||
|
assert_eq!(temp_grandchild, grandchild);
|
||||||
|
|
||||||
|
app.world
|
||||||
.spawn()
|
.spawn()
|
||||||
.insert_bundle((Transform::identity(), GlobalTransform::default()))
|
.insert_bundle((Transform::default(), GlobalTransform::default()))
|
||||||
.id();
|
.push_children(&[child]);
|
||||||
|
std::mem::swap(
|
||||||
|
&mut *app.world.get_mut::<Parent>(child).unwrap(),
|
||||||
|
&mut *temp.get_mut::<Parent>(grandchild).unwrap(),
|
||||||
|
);
|
||||||
|
|
||||||
let grandchild = world
|
app.update();
|
||||||
.spawn()
|
|
||||||
.insert_bundle((
|
|
||||||
Transform::identity(),
|
|
||||||
GlobalTransform::default(),
|
|
||||||
Parent(child),
|
|
||||||
))
|
|
||||||
.id();
|
|
||||||
world.spawn().insert_bundle((
|
|
||||||
Transform::default(),
|
|
||||||
GlobalTransform::default(),
|
|
||||||
Children::with(&[child]),
|
|
||||||
));
|
|
||||||
world.entity_mut(child).insert(Parent(grandchild));
|
|
||||||
|
|
||||||
update_stage.run(&mut world);
|
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
@ -279,7 +279,7 @@ pub fn flex_node_system(
|
|||||||
new_position.x = to_logical(layout.location.x + layout.size.width / 2.0);
|
new_position.x = to_logical(layout.location.x + layout.size.width / 2.0);
|
||||||
new_position.y = to_logical(layout.location.y + layout.size.height / 2.0);
|
new_position.y = to_logical(layout.location.y + layout.size.height / 2.0);
|
||||||
if let Some(parent) = parent {
|
if let Some(parent) = parent {
|
||||||
if let Ok(parent_layout) = flex_surface.get_layout(parent.0) {
|
if let Ok(parent_layout) = flex_surface.get_layout(**parent) {
|
||||||
new_position.x -= to_logical(parent_layout.size.width / 2.0);
|
new_position.x -= to_logical(parent_layout.size.width / 2.0);
|
||||||
new_position.y -= to_logical(parent_layout.size.height / 2.0);
|
new_position.y -= to_logical(parent_layout.size.height / 2.0);
|
||||||
}
|
}
|
||||||
|
@ -52,7 +52,7 @@ fn joint_animation(
|
|||||||
// Iter skinned mesh entity
|
// Iter skinned mesh entity
|
||||||
for skinned_mesh_parent in parent_query.iter() {
|
for skinned_mesh_parent in parent_query.iter() {
|
||||||
// Mesh node is the parent of the skinned mesh entity.
|
// Mesh node is the parent of the skinned mesh entity.
|
||||||
let mesh_node_entity = skinned_mesh_parent.0;
|
let mesh_node_entity = skinned_mesh_parent.get();
|
||||||
// Get `Children` in the mesh node.
|
// Get `Children` in the mesh node.
|
||||||
let mesh_node_children = children_query.get(mesh_node_entity).unwrap();
|
let mesh_node_children = children_query.get(mesh_node_entity).unwrap();
|
||||||
|
|
||||||
|
@ -41,27 +41,6 @@ fn setup(mut commands: Commands, asset_server: Res<AssetServer>) {
|
|||||||
// Store parent entity for next sections
|
// Store parent entity for next sections
|
||||||
.id();
|
.id();
|
||||||
|
|
||||||
// Another way to create a hierarchy is to add a Parent component to an entity,
|
|
||||||
// which would be added automatically to parents with other methods.
|
|
||||||
// Similarly, adding a Parent component will automatically add a Children component to the
|
|
||||||
// parent.
|
|
||||||
commands
|
|
||||||
.spawn_bundle(SpriteBundle {
|
|
||||||
transform: Transform {
|
|
||||||
translation: Vec3::new(-250.0, 0.0, 0.0),
|
|
||||||
scale: Vec3::splat(0.75),
|
|
||||||
..default()
|
|
||||||
},
|
|
||||||
texture: texture.clone(),
|
|
||||||
sprite: Sprite {
|
|
||||||
color: Color::RED,
|
|
||||||
..default()
|
|
||||||
},
|
|
||||||
..default()
|
|
||||||
})
|
|
||||||
// Using the entity from the previous section as the parent:
|
|
||||||
.insert(Parent(parent));
|
|
||||||
|
|
||||||
// Another way is to use the push_children function to add children after the parent
|
// Another way is to use the push_children function to add children after the parent
|
||||||
// entity has already been spawned.
|
// entity has already been spawned.
|
||||||
let child = commands
|
let child = commands
|
||||||
|
Loading…
Reference in New Issue
Block a user