Relevant official examples: ecs_guide.


Bundles

You can think of Bundles like "templates" for creating entities. They make it easy to create entities with a common set of components types.

By creating a bundle type, instead of adding your components one by one, you can make sure that you will never accidentally forget some important component on your entities. The Rust compiler will give an error if you do not set all the fields of a struct, thus helping you make sure your code is correct.

Bevy provides many built-in bundle types that you can use to spawn common kinds of entities.

Creating Bundles

To create your own bundle, derive Bundle on a struct:

#[derive(Bundle)]
struct PlayerBundle {
    xp: PlayerXp,
    name: PlayerName,
    health: Health,
    marker: Player,

    // We can nest/include another bundle.
    // Add the components for a standard Bevy Sprite:
    sprite: SpriteBundle,
}

When you have nested bundles, everything gets flattened. You end up with an entity that has all the included component types. If a type appears more than once, that's an error.

Using Bundles

You can then use your bundle when you spawn your entities:

commands.spawn(PlayerBundle {
    xp: PlayerXp(0),
    name: PlayerName("Player 1".into()),
    health: Health {
        hp: 100.0,
        extra: 0.0,
    },
    marker: Player,
    sprite: SpriteBundle {
        // TODO
        ..Default::default()
    },
});

If you want to have default values (similar to Bevy's bundles):

impl Default for PlayerBundle {
    fn default() -> Self {
        Self {
            xp: PlayerXp(0),
            name: PlayerName("Player".into()),
            health: Health {
                hp: 100.0,
                extra: 0.0,
            },
            marker: Player,
            sprite: Default::default(),
        }
    }
}

Now you can do this:

commands.spawn(PlayerBundle {
    name: PlayerName("Player 1".into()),
    ..Default::default()
});

Bundles for Removal

Bundles can also be useful to represent a set of components that you want to be able to easily remove from an entity.

/// Contains all components to remove when
/// resetting the player between rooms/levels.
#[derive(Bundle)]
struct PlayerResetCleanupBundle {
    status_effect: StatusEffect,
    pending_action: PlayerPendingAction,
    modifier: CurrentModifier,
    low_hp_marker: LowHpMarker,
}
commands.entity(e_player)
    .remove::<PlayerResetCleanupBundle>();

The component types included in the bundle will be removed from the entity, if any of them exist on the entity.

Loose components as bundles

Technically, Bevy also considers arbitrary tuples of components as bundles:

(ComponentA, ComponentB, ComponentC)

This allows you to easily spawn an entity using a loose bunch of components (or bundles), or add more arbitrary components when you spawn entities. However, this way you don't have the compile-time correctness advantages that a well-defined struct gives you.

commands.spawn((
    SpriteBundle {
        // ...
        ..default()
    },
    Health {
        hp: 50.0,
        extra: 0.0,
    },
    Enemy,
    // ...
));

You should strongly consider creating proper structs, especially if you are likely to spawn many similar entities. It will make your code easier to maintain.

Querying

Note that you cannot query for a whole bundle. Bundles are just a convenience when creating the entities. Query for the individual component types that your system needs to access.

This is wrong:

fn my_system(query: Query<&SpriteBundle>) {
  // ...
}

Instead, do this:

fn my_system(query: Query<(&Transform, &Handle<Image>)>) {
  // ...
}

(or whatever specific components you need in that system)