Bevy Version: | 0.14 | (current) |
---|
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);
}
}