Bevy Version: | 0.13 | (current) |
---|
Queries
Relevant official examples:
ecs_guide
.
Queries let you access components of entities.
Use the Query
system parameter, where you can specify the data
you want to access, and optionally additional filters.
Think of the types you put in your Query
as a "specification" for selecting
what entities you want to access. Queries will match only those entities in the
ECS World that fit your specification. You are then able to access the relevant
data from any such entities.
The first type parameter for a query is the data you want to access. Use &
for
shared/readonly access and &mut
for exclusive/mutable access. Use Option
if
the component is not required (you want to find entities with or without that
component. If you want multiple components, put them in a tuple.
Iterating
The most common operation is to simply iterate to access the component values of every entity that matches the query:
fn check_zero_health(
// access entities that have `Health` and `Transform` components
// get read-only access to `Health` and mutable access to `Transform`
// optional component: get access to `Player` if it exists
mut query: Query<(&Health, &mut Transform, Option<&Player>)>,
) {
// get all matching entities
for (health, mut transform, player) in query.iter_mut() {
eprintln!("Entity at {} has {} HP.", transform.translation, health.hp);
// center if hp is zero
if health.hp <= 0.0 {
transform.translation = Vec3::ZERO;
}
if let Some(player) = player {
// the current entity is the player!
// do something special!
}
}
}
If you want to know the entity IDs of the entities you are accessing, you can
put the special Entity
type in your query. This is useful if you need
to later perform specific operations on those entities.
// add `Entity` to `Query` to get Entity IDs
fn query_entities(q: Query<(Entity, /* ... */)>) {
for (e, /* ... */) in q.iter() {
// `e` is the Entity ID of the entity we are accessing
}
}
Accessing Specific Entities
To access the components from one specific entity
only, you need to know the Entity
ID:
if let Ok((health, mut transform)) = query.get_mut(entity) {
// do something with the components
} else {
// the entity does not have the components from the query
}
If you want to access the data from several entities all at once, you can use
many
/many_mut
(panic on error) or get_many
/get_many_mut
(return
Result
). These methods ensure that all the requested entities exist and
match the query, and will produce an error otherwise.
#[derive(Resource)]
struct UiHudIndicators {
// say we have 3 special UI elements
entities_ui: [Entity; 3],
entities_text: [Entity; 3],
}
fn update_ui_hud_indicators(
indicators: Res<UiHudIndicators>,
query_text: Query<&Text>,
query_ui: Query<(&Style, &BackgroundColor)>,
) {
// we can get everything as an array
if let Ok(my_texts) = query_text.get_many(indicators.entities_text) {
// the entities exist and match the query
// TODO: something with `my_texts[0]`, `my_texts[1]`, `my_texts[2]`
} else {
// query unsuccessful
};
// we can use "destructuring syntax"
// if we want to unpack everything into separate variables
let [(style0, color0), (style1, color1), (style2, color2)] =
query_ui.many(indicators.entities_ui);
// TODO: something with all these variables
}
Unique Entities
If you know that only one matching entity is supposed to exist (the query is
expected to only ever match a single entity), you can use single
/single_mut
(panic on error) or get_single
/get_single_mut
(return Result
). These
methods ensure that there exists exactly one candidate entity that can match
your query, and will produce an error otherwise.
You do not need to know the Entity
ID.
fn query_player(mut q: Query<(&Player, &mut Transform)>) {
let (player, mut transform) = q.single_mut();
// do something with the player and its transform
}
Combinations
If you want to iterate over all possible combinations of N entities, Bevy provides a method for that too. Be careful: with a lot of entities, this can easily become very slow!
fn print_potential_friends(
q_player_names: Query<&PlayerName>,
) {
// this will iterate over every possible pair of two entities
// (that have the PlayerName component)
for [player1, player2] in q_player_names.iter_combinations() {
println!("Maybe {} could be friends with {}?", player1.0, player2.0);
}
}
fn apply_gravity_to_planets(
mut query: Query<&mut Transform, With<Planet>>,
) {
// this will iterate over every possible pair of two planets
// For mutability, we need a different syntax
let mut combinations = query.iter_combinations_mut();
while let Some([planet1, planet2]) = combinations.fetch_next() {
// TODO: calculate the gravity force between the planets
}
}
Bundles
Queries work with individual components. If you created an entity using a bundle, you need to query for the specific components from that bundle that you care about.
A common beginner mistake is to query for the bundle type!
Query Filters
Add query filters to narrow down the entities you get from the query.
This is done using the second (optional) generic type parameter of the
Query
type.
Note the syntax of the query: first you specify the data you want to access (using a tuple to access multiple things), and then you add any additional filters (can also be a tuple, to add multiple).
Use With
/Without
to only get entities that have specific components.
fn debug_player_hp(
// access the health (and optionally the PlayerName, if present), only for friendly players
query: Query<(&Health, Option<&PlayerName>), (With<Player>, Without<Enemy>)>,
) {
// get all matching entities
for (health, name) in query.iter() {
if let Some(name) = name {
eprintln!("Player {} has {} HP.", name.0, health.hp);
} else {
eprintln!("Unknown player has {} HP.", health.hp);
}
}
}
This is useful if you don't actually care about the data stored inside these components, but you want to make sure that your query only looks for entities that have (or not have) them. If you want the data, then put the component in the first part of the query (as shown previously), instead of using a filter.
Multiple filters can be combined:
- in a tuple to apply all of them (AND logic)
- using the
Or<(…)>
wrapper to detect any of them (OR logic).- (note the tuple inside)
Query Transmutation
If you want one function with a Query
parameter to call another function
with a different (but compatible) Query
parameter, you can create the
needed Query
from the one you have using something called QueryLens
.
fn debug_positions(
query: Query<&Transform>,
) {
for transform in query.iter() {
eprintln!("{:?}", transform.translation);
}
}
fn move_player(
mut query_player: Query<&mut Transform, With<Player>>,
) {
// TODO: mutate the transform to move the player
// say we want to call our debug_positions function
// first, convert into a query for `&Transform`
let mut lens = query_player.transmute_lens::<&Transform>();
debug_positions(lens.query());
}
fn move_enemies(
mut query_enemies: Query<&mut Transform, With<Enemy>>,
) {
// TODO: mutate the transform to move our enemies
let mut lens = query_enemies.transmute_lens::<&Transform>();
debug_positions(lens.query());
}
Note: when we call debug_positions
from each function, it will access
different entities! Even though the Query<&Transform>
parameter type does not
have any additional filters, it was created by transmuting
via QueryLens
, and therefore it can only access the entities and components
of the original Query
that it was derived from. If we were to add
debug_positions
to Bevy as a regular system, it would access the transforms of
all entities.
Also note: this has some performance overhead; the transmute operation is not free. Bevy normally caches some query metadata across multiple runs of a system. When you create the new query, it has to make a copy of it.