Bevy Version: | 0.15 | (upcoming / release candidate) |
---|
Relevant official examples:
ecs_guide
.
Entities
See here for more explanation on how storing data in the ECS works.
Conceptually, an entity represents a set of values for different components.
Each component is a Rust type (struct
or enum
) and an entity can be used to
store a value of that type.
Technically, an entity is just a simple integer ID (imagine the "row number" in a table/spreadsheet) that can be used to find related data values (in different "columns" of that table).
In Bevy, Entity
is this value. It consists of two integers:
the ID and the "generation" (allowing IDs to be reused, after you despawn old
entities).
You can create ("spawn") new entities and destroy ("despawn") entities using
Commands
or exclusive World
access.
fn setup(mut commands: Commands) {
// create a new entity
commands.spawn((
// Initialize all your components and bundles here
Enemy,
Health {
hp: 100.0,
extra: 25.0,
},
AiMode::Passive,
// ...
));
// If you want to get the Entity ID, just call `.id()` after spawn
let my_entity = commands.spawn((/* ... */)).id();
// destroy an entity, removing all data associated with it
commands.entity(my_entity).despawn();
}
Many of your entities might need to have the same common components. You can use Bundles to make it easier to spawn your entities.
Components
Components are the data associated with entities.
To create a new component type, simply define a Rust struct
or enum
, and
derive the Component
trait.
#[derive(Component)]
struct Health {
hp: f32,
extra: f32,
}
#[derive(Component)]
enum AiMode {
Passive,
ChasingPlayer,
}
Types must be unique – an entity can only have one component per Rust type.
Newtype Components
Use wrapper (newtype) structs to make unique components out of simpler types:
#[derive(Component)]
struct PlayerXp(u32);
#[derive(Component)]
struct PlayerName(String);
Marker Components
You can use empty structs to help you identify specific entities. These are known as "marker components". Useful with query filters.
/// Add this to all menu ui entities to help identify them
#[derive(Component)]
struct MainMenuUI;
/// Marker for hostile game units
#[derive(Component)]
struct Enemy;
/// This will be used to identify the main player entity
#[derive(Component)]
struct Player;
/// Tag all creatures that are currently friendly towards the player
#[derive(Component)]
struct Friendly;
Accessing Components
Components can be accessed from systems, using queries.
You can think of the query as the "specification" for the data you want to access. It gives you access to specific component values from entities that match the query's signature.
fn level_up_player(
// get the relevant data. some components read-only, some mutable
mut query_player: Query<(&PlayerName, &mut PlayerXp, &mut Health), With<Player>>,
) {
// `single` assumes only one entity exists that matches the query
let (name, mut xp, mut health) = query_player.single_mut();
if xp.0 > 1000 {
xp.0 = 0;
health.hp = 100.0;
health.extra += 25.0;
info!("Player {} leveled up!", name.0);
}
}
fn die(
// `Entity` can be used to get the ID of things that match the query
query_health: Query<(Entity, &Health)>,
// we also need Commands, so we can despawn entities if we have to
mut commands: Commands,
) {
// we can have many such entities (enemies, player, whatever)
// so we loop to check all of them
for (entity_id, health) in query_health.iter() {
if health.hp <= 0.0 {
commands.entity(entity_id).despawn();
}
}
}
Adding/removing Components
You can add/remove components on existing entities, using Commands
or
exclusive World
access.
fn make_enemies_friendly(
query_enemy: Query<Entity, With<Enemy>>,
mut commands: Commands,
) {
for entity_id in query_enemy.iter() {
commands.entity(entity_id)
.remove::<Enemy>()
.insert(Friendly);
}
}
Required Components
Components can require other components. When you spawn an entity, Bevy will ensure that any additional components required by the components you provide are also added to the entity. If you do not provide a value for those components, Bevy will automatically insert a default value.
For example, the way to create a 3D camera in Bevy is to spawn
an entity with the Camera3d
component. Bevy will automatically make sure to
also add any other necessary components, such as Camera
(where Bevy stores
general camera properties), Transform
(transforms), and others.
These dependencies are recursive. For example, Transform
requires
GlobalTransform
, so our camera entity will also get that automatically.
To learn what components are required by a
specific component type, look for the impl Component for …
under "Trait Implementations" in the API Docs.
If you want to customize the values in any of those components, you can just add them yourself.
fn setup_camera(
mut commands: Commands,
) {
commands.spawn((
// Add Camera2d to pull in everything needed for a 2D camera
Camera2d,
// Add our own transform
Transform::from_xyz(1.0, 2.0, 3.0),
// By just adding the above 2 components, our entity
// will also automatically get other things, like:
// - `Camera`: required by `Camera2d`
// - `GlobalTransform`: required by `Transform`
// - and various others … (see the API docs)
// They will be initialized with default values.
//
// `Camera2d` also requires `Transform`, but we provided
// our own value for that, so Bevy will use our value
// instead of the default.
));
}
You can add required components to your own component types as follows:
#[derive(Component)]
#[require(Enemy, Health, Sprite, Transform, Visibility)]
struct RegularEnemy;
#[derive(Component)]
#[require(
Enemy,
// init using a custom function instead of `Default`
Health(big_boss_health),
Sprite,
Transform,
Visibility,
)]
struct BigBoss;
You need to make sure that all the required components impl Default
. Or
alternatively, you can use a custom constructor function for initialization.
#[derive(Component, Default)]
struct Enemy;
#[derive(Component)]
struct Health(f32);
impl Default for Health {
fn default() -> Self {
Health(100.0)
}
}
fn big_boss_health() -> Health {
Health(500.0)
}
And now, with all the above, we can do:
fn spawn_enemies(
mut commands: Commands,
) {
commands.spawn(BigBoss);
commands.spawn((
RegularEnemy,
Transform::from_xyz(10.0, 20.0, 30.0),
));
}