fix parenting of scenes (#2410)
# Objective Fix #2406 Scene parenting was not done completely, leaving the hierarchy maintenance to the standard system. As scene spawning happens in stage `PreUpdate` and hierarchy maintenance in stage `PostUpdate`, this left the scene in an invalid state parent wise for part of a frame ## Solution Also add/update the `Children` component when spawning the scene. I kept the `Children` component as a `SmallVec`, it could be moved to an `HashSet` to guarantee uniqueness Co-authored-by: François <8672791+mockersf@users.noreply.github.com>
This commit is contained in:
parent
08b5234582
commit
5d5d7833f0
@ -4,10 +4,11 @@ use bevy_asset::{AssetEvent, Assets, Handle};
|
|||||||
use bevy_ecs::{
|
use bevy_ecs::{
|
||||||
entity::{Entity, EntityMap},
|
entity::{Entity, EntityMap},
|
||||||
reflect::{ReflectComponent, ReflectMapEntities},
|
reflect::{ReflectComponent, ReflectMapEntities},
|
||||||
|
system::Command,
|
||||||
world::{Mut, World},
|
world::{Mut, World},
|
||||||
};
|
};
|
||||||
use bevy_reflect::TypeRegistryArc;
|
use bevy_reflect::TypeRegistryArc;
|
||||||
use bevy_transform::prelude::Parent;
|
use bevy_transform::{hierarchy::AddChild, prelude::Parent};
|
||||||
use bevy_utils::{tracing::error, HashMap};
|
use bevy_utils::{tracing::error, HashMap};
|
||||||
use thiserror::Error;
|
use thiserror::Error;
|
||||||
use uuid::Uuid;
|
use uuid::Uuid;
|
||||||
@ -268,10 +269,22 @@ impl SceneSpawner {
|
|||||||
for (instance_id, parent) in scenes_with_parent {
|
for (instance_id, parent) in scenes_with_parent {
|
||||||
if let Some(instance) = self.spawned_instances.get(&instance_id) {
|
if let Some(instance) = self.spawned_instances.get(&instance_id) {
|
||||||
for entity in instance.entity_map.values() {
|
for entity in instance.entity_map.values() {
|
||||||
if let Some(mut entity_mut) = world.get_entity_mut(entity) {
|
// Add the `Parent` component to the scene root, and update the `Children` component of
|
||||||
if !entity_mut.contains::<Parent>() {
|
// the scene parent
|
||||||
entity_mut.insert(Parent(parent));
|
if !world
|
||||||
|
.get_entity(entity)
|
||||||
|
// This will filter only the scene root entity, as all other from the
|
||||||
|
// scene have a parent
|
||||||
|
.map(|entity| entity.contains::<Parent>())
|
||||||
|
// Default is true so that it won't run on an entity that wouldn't exist anymore
|
||||||
|
// this case shouldn't happen anyway
|
||||||
|
.unwrap_or(true)
|
||||||
|
{
|
||||||
|
AddChild {
|
||||||
|
parent,
|
||||||
|
child: entity,
|
||||||
}
|
}
|
||||||
|
.write(world);
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
} else {
|
} else {
|
||||||
|
|||||||
@ -7,6 +7,28 @@ use bevy_ecs::{
|
|||||||
};
|
};
|
||||||
use smallvec::SmallVec;
|
use smallvec::SmallVec;
|
||||||
|
|
||||||
|
#[derive(Debug)]
|
||||||
|
pub struct AddChild {
|
||||||
|
pub parent: Entity,
|
||||||
|
pub child: Entity,
|
||||||
|
}
|
||||||
|
|
||||||
|
impl Command for AddChild {
|
||||||
|
fn write(self, world: &mut World) {
|
||||||
|
world
|
||||||
|
.entity_mut(self.child)
|
||||||
|
// FIXME: don't erase the previous parent (see #1545)
|
||||||
|
.insert_bundle((Parent(self.parent), PreviousParent(self.parent)));
|
||||||
|
if let Some(mut children) = world.get_mut::<Children>(self.parent) {
|
||||||
|
children.0.push(self.child);
|
||||||
|
} else {
|
||||||
|
world
|
||||||
|
.entity_mut(self.parent)
|
||||||
|
.insert(Children(smallvec::smallvec![self.child]));
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
#[derive(Debug)]
|
#[derive(Debug)]
|
||||||
pub struct InsertChildren {
|
pub struct InsertChildren {
|
||||||
parent: Entity,
|
parent: Entity,
|
||||||
@ -134,6 +156,7 @@ pub trait BuildChildren {
|
|||||||
fn push_children(&mut self, children: &[Entity]) -> &mut Self;
|
fn push_children(&mut self, children: &[Entity]) -> &mut Self;
|
||||||
fn insert_children(&mut self, index: usize, children: &[Entity]) -> &mut Self;
|
fn insert_children(&mut self, index: usize, children: &[Entity]) -> &mut Self;
|
||||||
fn remove_children(&mut self, children: &[Entity]) -> &mut Self;
|
fn remove_children(&mut self, children: &[Entity]) -> &mut Self;
|
||||||
|
fn add_child(&mut self, child: Entity) -> &mut Self;
|
||||||
}
|
}
|
||||||
|
|
||||||
impl<'w, 's, 'a> BuildChildren for EntityCommands<'w, 's, 'a> {
|
impl<'w, 's, 'a> BuildChildren for EntityCommands<'w, 's, 'a> {
|
||||||
@ -182,6 +205,12 @@ impl<'w, 's, 'a> BuildChildren for EntityCommands<'w, 's, 'a> {
|
|||||||
});
|
});
|
||||||
self
|
self
|
||||||
}
|
}
|
||||||
|
|
||||||
|
fn add_child(&mut self, child: Entity) -> &mut Self {
|
||||||
|
let parent = self.id();
|
||||||
|
self.commands().add(AddChild { child, parent });
|
||||||
|
self
|
||||||
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
#[derive(Debug)]
|
#[derive(Debug)]
|
||||||
|
|||||||
@ -49,11 +49,10 @@ pub fn parent_update_system(
|
|||||||
// `children_additions`).
|
// `children_additions`).
|
||||||
if let Ok(mut new_parent_children) = children_query.get_mut(parent.0) {
|
if let Ok(mut new_parent_children) = children_query.get_mut(parent.0) {
|
||||||
// This is the parent
|
// This is the parent
|
||||||
debug_assert!(
|
// PERF: Ideally we shouldn't need to check for duplicates
|
||||||
!(*new_parent_children).0.contains(&entity),
|
if !(*new_parent_children).0.contains(&entity) {
|
||||||
"children already added"
|
(*new_parent_children).0.push(entity);
|
||||||
);
|
}
|
||||||
(*new_parent_children).0.push(entity);
|
|
||||||
} else {
|
} else {
|
||||||
// The parent doesn't have a children entity, lets add it
|
// The parent doesn't have a children entity, lets add it
|
||||||
children_additions
|
children_additions
|
||||||
|
|||||||
Loading…
Reference in New Issue
Block a user