Hierarchical (Parent/Child) Entities
Relevant official examples:
hierarchy
,
parenting
.
Technically, the Entities/Components themselves cannot form a hierarchy (the ECS is a flat data structure). However, logical hierarchies are a common pattern in games.
Bevy supports creating such a logical link between entities, to form
a virtual "hierarchy", by simply adding Parent
and
Children
components on the respective entities.
When using Commands to spawn entities,
Commands
has methods for adding children to entities,
which automatically add the correct components:
// spawn the parent and get its Entity id
let parent = commands.spawn_bundle(MyParentBundle::default())
.id();
// do the same for the child
let child = commands.spawn_bundle(MyChildBundle::default())
.id();
// add the child to the parent
commands.entity(parent).push_children(&[child]);
// you can also use `with_children`:
commands.spawn_bundle(MyParentBundle::default())
.with_children(|parent| {
parent.spawn_bundle(MyChildBundle::default());
});
You can despawn an entire hierarchy with a single command:
fn close_menu(
mut commands: Commands,
query: Query<Entity, With<MainMenuUI>>,
) {
for entity in query.iter() {
// despawn the entity and its children
commands.entity(entity).despawn_recursive();
}
}
Accessing the Parent or Children
To make a system that works with the hierarchy, you typically need two queries:
- one with the components you need from the child entities
- one with the components you need from the parent entities
One of the two queries should include the appropriate component, to obtain the entity ids to use with the other one:
Parent
in the child query, if you want to iterate entities and look up their parents, orChildren
in the parent query, if you want to iterate entities and look up their children
For example, if we want to get the Transform
of cameras (Camera
) that have a parent, and the
GlobalTransform
of their parent:
fn camera_with_parent(
q_child: Query<(&Parent, &Transform), With<Camera>>,
q_parent: Query<&GlobalTransform>,
) {
for (parent, child_transform) in q_child.iter() {
// `parent` contains the Entity ID we can use
// to query components from the parent:
let parent_global_transform = q_parent.get(parent.0);
// do something with the components
}
}
As another example, say we are making a strategy game, and we have Units that are children of a Squad. Say we need to make a system that works on each Squad, and it needs some information about the children:
fn process_squad_damage(
q_parent: Query<(&MySquadDamage, &Children)>,
q_child: Query<&MyUnitHealth>,
) {
// get the properties of each squad
for (squad_dmg, children) in q_parent.iter() {
// `children` is a collection of Entity IDs
for &child in children.iter() {
// get the health of each child unit
let health = q_child.get(child);
// do something
}
}
}
Relative Transforms
If your entities represent "objects in the game world", you probably expect the child to be positioned relative to the parent and move with it.
All Bundles that come with Bevy provide
this behavior automatically. You should at least use the basic
TransformBundle
if you don't need anything else.
For more info, see the dedicated page about transforms.