
# Objective - Hierarchy tools are not just used for `Transform`: they are also used for scenes. - In the future there's interest in using them for other features, such as visiibility inheritance. - The fact that these tools are found in `bevy_transform` causes a great deal of user and developer confusion - Fixes #2758. ## Solution - Split `bevy_transform` into two! - Make everything work again. Note that this is a very tightly scoped PR: I *know* there are code quality and docs issues that existed in bevy_transform that I've just moved around. We should fix those in a seperate PR and try to merge this ASAP to reduce the bitrot involved in splitting an entire crate. ## Frustrations The API around `GlobalTransform` is a mess: we have massive code and docs duplication, no link between the two types and no clear way to extend this to other forms of inheritance. In the medium-term, I feel pretty strongly that `GlobalTransform` should be replaced by something like `Inherited<Transform>`, which lives in `bevy_hierarchy`: - avoids code duplication - makes the inheritance pattern extensible - links the types at the type-level - allows us to remove all references to inheritance from `bevy_transform`, making it more useful as a standalone crate and cleaning up its docs ## Additional context - double-blessed by @cart in https://github.com/bevyengine/bevy/issues/4141#issuecomment-1063592414 and https://github.com/bevyengine/bevy/issues/2758#issuecomment-913810963 - preparation for more advanced / cleaner hierarchy tools: go read https://github.com/bevyengine/rfcs/pull/53 ! - originally attempted by @finegeometer in #2789. It was a great idea, just needed more discussion! Co-authored-by: Carter Anderson <mcanders1@gmail.com>
73 lines
2.9 KiB
Rust
73 lines
2.9 KiB
Rust
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));
|
|
});
|
|
}
|