Tainted\\Coders

Bevy Hierarchy

Bevy version: 0.14Last updated:

Bevy stores both entities and components in completely flat arrays and uses indexes to link identities. This can make it difficult to represent parent/child relationships.

In other engines like Godot these relationships are expressed in the way the data structures lay them out in memory. But in an ECS everything is completely flat.

Bevy provides this ability by letting you tag entities with Parent and Children components provided from the bevy_hierarchy crate.

Parents and children

A parent just holds an Entity

struct Parent(pub Entity);

While Children are a vector of Entity

struct Children(pub SmallVec<[Entity; 8]>);

The SmallVec here means that we can store up to 8 entities inline before falling back to allocating them on the heap.

When something has a Parent it changes its Transform to be positioned relative to its parent's. This can be useful for having something follow the player around.

These components are used to propagate properties through to different levels of our entity hierarchy.

Creating a hierarchy

To create a parent/child relationship we can spawn our entities and use add_child on our EntityCommands

fn spawn_entities(mut commands: Commands) {
  let parent = commands.spawn_empty().id();
  let child = commands.spawn_empty().id();

  // Add to the front of the parents Children
  commands.entity(parent).add_child(child);

  // Add children the end of Children
  commands.entity(parent).push_children(&[child]);

  // Insert children at a particular position pushing the rest back
  commands.entity(parent).insert_children(0, &[child]);

  // Set children by removing the old children
  commands.entity(parent).replace_children(&[child]);
}

Each of these entity commands will do two things:

  1. Update the Parent component of our child entity to be the parent
  2. Add the child entity to the parent entity's Children component

If we wanted to build them all at once instead of one at a time we can use EntityCommands::with_children and pass a closure:

fn spawn_child(mut commands: Commands) {
  commands.spawn_empty().with_children(|parent| {
    parent.spawn_empty();
  });
}

Removing children

We can remove children from a parent by adding or removing Parent and changing the members of our Children component.

fn remove_children(mut commands: Commands) {
  let parent = commands.spawn_empty().id();
  let child = commands.spawn_empty().id();

  // Remove all children
  commands.entity(parent).clear_children();

  // Remove specific children
  commands.entity(parent).remove_children(&[child]);

  // Removes like clear_children but from the other way
  commands.entity(child).remove_parent();

  // Despawn the entity and its children
  commands.entity(entity).despawn_recursive();
}

Removing means removing the Parent component on all children and removing the Children component from the parent.

When you remove_parent:

  1. The Parent component gets removed from the child
  2. The child is removed from the Children of the parent.

Iterating ancestors

When we want to enumerate over our parents, grandparents, etc, we use iter_ancestors on a query of Parent.

This will iterate finding the parent and then its parent until no further Parent component is found.

fn iterate_ancestors(
  query: Query<Entity, With<Player>>,
  parent_query: Query<&Parent>
) {
  let player = query.single();
  for ancestor in parent_query.iter_ancestors(player) {
    info!("{:?} is an ancestor of {:?}", ancestor, player);
  }
}

Iterating children

From the other way, we can fetch the children using iter_descendants.

This will perform a breadth-first search down the children, and their children etc.

fn iterate_children(
  query: Query<Entity, With<Player>>,
  children_query: Query<&Children>
) {
  let player = query.single();
  for child in children_query.iter_descendants(player) {
    info!("{:?} is a descendant of {:?}", child, player);
  }
}

Child changed events

Hierarchy events are automatically added to your app through the hierarchy plugin. When you add, remove or move children these events will fire allowing you to react to them in your systems.

Moving children relative to their parents

Transform and Visibility components will be effected by your hierarchies.

This is provided automatically by the propagate_transforms system added from the bevy_transform crate. This crate is aware of our hierarchies (the Parent and Children components).

When you have Children their transform be local to their Parent so long as they both have the component. Same goes for Visibility, if a parent is not visible then neither will its children.

Aery

This crate provides an early implementation of entity relationships that differs from bevy_hierarchy in some key ways:

Aery stores Edges component on each related entity, which has 8 empty HashMap in it by default. These hash maps will have TypeId as key and IndexSet<Entity> as values.

This is quite different to bevy_hierarchy where Parent is just Entity and Children has a smallvec of four entities.

Aery is also without any ChildAdded, ChildRemoved etc. events.

But in exchange Aery provides breadth first searches, joins, diamond diagrams and other goodies we can use along side the bevy hierarchy crate.