Tainted\\Coders

Metadata Components

Bevy version: 0.14Last updated:

When spawning tuple bundles it can sometimes difficult to identify their purpose:

// What am I?
commands.spawn((
  Position::new(),
  Velocity::new()
))

If we used an inspector such as the bevy-inspector-egui then it would be hard to identify which component is what just by their component list.

Remember that even when we use a named bundle, the bundle itself is ephemeral and only the components will remain afterwards.

One common alternative is to create components such as Player that can make querying easier. However, some components won't be queried and many component types can clutter up our code base.

So it can be useful to create a Name component that makes this debugging much easier:

commands.spawn((
  Name::new("Player"),
  Position::new(),
  Velocity::new()
))

Another useful metadata component is to create components that tag entities to be cleaned up at specific points in our apps.

commands.spawn((
  Name::new("Player"),
  Position::new(),
  Velocity::new(),
  CleanupOnGameExit
))

This can be used in combination with generic systems to trigger a cleanup for multiple component types:

#![allow(dead_code)]
use bevy::{ecs::component::Component, prelude::*};

#[derive(Debug, Clone, Eq, PartialEq, Hash, Default, States)]
enum AppState {
  #[default]
  MainMenu,
  InGame,
  Paused,
}

impl AppState {
  fn next(&self) -> Self {
    match *self {
      AppState::MainMenu => AppState::InGame,
      AppState::InGame => AppState::Paused,
      AppState::Paused => AppState::InGame,
    }
  }
}

fn cleanup_system<T: Component>(
  mut commands: Commands,
  q: Query<Entity, With<T>>,
) {
  for e in q.iter() {
    commands.entity(e).despawn_recursive();
  }
}

#[derive(Component)]
struct CleanupOnGameExit;

fn main() {
  App::new()
    .add_plugins(DefaultPlugins)
    .init_state::<AppState>()
    .add_systems(
      OnExit(AppState::InGame),
      cleanup_system::<CleanupOnGameExit>,
    );
}