diff --git a/content/learn/book/ecs/queries/_index.md b/content/learn/book/ecs/queries/_index.md index edc16b65a3..8cd86cbb14 100644 --- a/content/learn/book/ecs/queries/_index.md +++ b/content/learn/book/ecs/queries/_index.md @@ -5,3 +5,459 @@ template = "book-section.html" page_template = "book-section.html" insert_anchor_links = "right" +++ + + + +Once we have data stored on our entities in the form of components, we need to be able to get the data back out in a principled way. +**Queries** are system parameters that allow us to carefully request sets of entities from the [`World`] that meet the criteria we care about and then retrieve the data we need to operate on. + +The [`Query`] type has two type parameters: `Q` describes which data should be requested, while `F` type parameter describes how it should be filtered. +By default, `F` is set to `()`, the "unit type", indicating that we do not want a filter. + +The simplest case is where we need to access the data of a single component, for example `Query<&Life`>. +In this case, `Q` is the type `&Life`, and `F` is `()`, because we didn't specify a value for it. + +This will request a shared reference to the `Life` component on every entity that has that component. +This reference is read-only: we cannot mutate the values returned by our query. +If we want to be able to change these values, we need to request a mutable reference using `Query<&mut Life>` instead. + +```rust +# use bevy::ecs::prelude::*; +# #[derive(Component)] +# struct Life; + +// A system that has read-access to the Life component of each entity +fn read_life(query: Query<&Life>) {} + +// A system that has write-access to the Life component of each entity +// Remember to set your query argument as mutable +fn write_life(mut query: Query<&mut Life>) {} +``` + +In order to access multiple components at once, we need to replace our `&Life` type with a tuple type that bundles many types into one. + +```rust +# use bevy::ecs::prelude::*; +# #[derive(Component)] +# struct Life; +# +# #derive(Component) +# struct Defense; + +// A system that has write-access to the Life component, and read-access to the Defense component +// Only entities with both Life and Defense will be included in this Query +fn life_and_defense(mut query: Query<(&mut Life, &Defense)>) {} +``` + +Here, the type of `Q` is `(&mut Life, &Defense)` (and `F` is still `()`). + +Queries operate using "AND" logic (unless you use an `Option<&C>` query parameter): adding more components will always strictly reduce the number of entities returned by your query. + +[`Query`]: https://docs.rs/bevy/latest/bevy/ecs/system/struct.Query.html +[`World`]: https://docs.rs/bevy/latest/bevy/ecs/world/struct.World.html + +## Filtering queries + +When components are fetched in queries, the data of *every* component in `Q` passed will be fetched and made available to the system. +However, this isn't always what you want! +In many cases, you just want to filter the query based on the presence (or absence) of a component, and don't want to deal with unpacking data you're never going to use. +This is particularly true when working with **marker components**: dataless structs designed to convey the identity or current state of an entity. + +The two most important query filter types are [`With`] and [`Without`], which filter based on whether or not an entity has a component of the type `C`. + +[`With`]: https://docs.rs/bevy/latest/bevy/ecs/query/struct.With.html +[`Without`]: https://docs.rs/bevy/latest/bevy/ecs/query/struct.Without.html + +```rust +# use bevy::ecs::prelude::*; +# #[derive(Component)] +# struct Life; +# +# #derive(Component) +# struct Player; + +// A system that has write-access to the Life component of entities with the Player component +// Q is &mut Life, and F is With +fn regenerate_player_life(mut query: Query<&mut Life, With>) {} + +// This query requests the Entity identifier of all entities that don't have the Player component, +// so then they can be passed into our Commands system parameter +// Q is Entity, and F is Without +fn despawn_all_non_player_entities(mut commands: Commands, query: Query>) {} + +// This systems has two Query system parameters +// For player_query, Q is (&Position, &mut Targeting) and F is With +// For target_query, Q is (Entity, &Position, &TargetPriority) and F is (With, Without) +fn select_target( + mut player_query: Query<(&Position, &mut Targeting), With>, + target_query: Query<(Entity, &Position, &TargetPriority), (With, Without)> +) { +} +``` + +As with requested data, filters combine in an "and" fashion. +Only entities with the `Position`, `TargetPriority`, and `Enemy` components which don't have a `Player` component will be returned by `target_query` in the example above. + +## Iterating over queries + +Once we have a query, the most common pattern is perform some logic on every entity returned. +To do so, we can use straightforward for-loops: + +```rust +#[derive(Component, Debug)] +struct Life { + val: u8, +} + +#[derive(Component)] +struct IncomingDamge { + val: u8, +} + +/// Prints the current life total of every entity with the Life component +fn report_life(query: Query<&Life>) { + for life in query.iter() { + dbg!(life); + } +} + +#[derive(Component)] +struct Age(u64); + +fn increment_age(mut query: Query<&mut Age>) { + // We need to use mut query, &mut Age, mut age, and .iter_mut() here because we need mutable access + for mut age in query.iter_mut() { + // age.0 refers to the first (only) field on our tuple type + // We could make this more ergonomic by implementing the Add trait + // or the AddAssign trait on our Age component type + age.0 = age.0 + 1; + } +} + +fn take_damage(mut query: Query<(&mut Life, &mut IncomingDamage)>) { + // You can unpack your query iterator into several variables + for (mut life, mut incoming_damage) in query.iter_mut() { + life.val -= incoming_damage.val; + incoming_damage.val = 0; + } +} +``` + +If you're more experienced with Rust, you will be unsurprised to discover that you can also use common iterator tools like `.for_each`, `.map`, and `.filter` to work with your queries. + +If you find yourself needing to iterate over all pairs (or triples or...) of a query (perhaps for collision detection), turn to the `iter_combinations` function demonstrated in the [corresponding example](https://github.com/bevyengine/bevy/blob/latest/examples/ecs/iter_combinations.rs) to avoid borrow-checker headaches. + +## Queries that return exactly one entity + +When we have a query that we *know* will always return a single entity, iterating over the query tends to result in unclear code. +To get around this, we can use [`Query::single()`] and [`Query::single_mut()`], depending on whether or not we need to mutate the returned data. + +```rust +# #[derive(Component, Debug)] +# struct Life { +# val: u8, +# } +# +# #[derive(Component)] +# struct Player; +fn report_player_life(query: Query<&Life, With>) { + // This will panic unless exactly one entity matching the query was found + // If you want to handle the error yourself, use Query::get_single + let life = query.single(); + dbg!(life); +} +``` + +[`Query::single()`]: https://docs.rs/bevy/latest/bevy/ecs/system/struct.Query.html#method.single +[`Query::single_mut()`]: https://docs.rs/bevy/latest/bevy/ecs/system/struct.Query.html#method.single_mut + +## Looking up specific entities + +Each entity in our ECS data storages has a unique, arbitrarily assigned `[Entity`] identifier. +We can fetch this for each entity returned by our queries by including it in `Q`, the first type parameter of [`Query`]: + +```rust +# use bevy::prelude::ecs::*; +// This system reports the Entity of every entity in your World +fn all_entities(query: Query) { + for entity in query.iter() { + dbg!(entity); + } +} +``` + +Here's a more practical example of how this might be used: + +```rust +# use bevy::ecs::prelude::*; +#[derive(Component)] +struct Player; + +#[derive(Component)] +struct Enemy; + +#[derive(Component, Debug)] +struct PowerLevel(u8); + +#[derive(Component)] +struct Target(Option); + +// Typically you'll combine this pattern with query filters +// to extract the entities of a relevant subset, +// and then store it somewhere where you can access it later +fn target_strongest_enemy(enemy_query: Query<(Entity, &PowerLevel), With>, mut player_query: Query<&mut Target, With>) { + let mut current_strongest = 0; + let mut target = player_query.single_mut(); + + // If you prefer iterator adaptors, this could be replaced by + // target.0 = query.iter().max_by(|(_, power_level)| power_level.0); + for (entity, power_level) in enemy_query.iter(){ + if power_level.0 > current_strongest { + target.0 = Some(entity); + current_strongest = power_level.0; + } + } +} +``` + +Once we have a particular entity in mind, we can grab its data using [`Query::get()`] and the related methods on [`Query`]. +This is fallible, and so it returns a [`Result`] that you must handle. + +```rust +# use bevy::ecs::prelude::*; +# +# #[derive(Component)] +# struct Player; +# +# #[derive(Component)] +# struct Enemy; +# +# #[derive(Component, Debug)] +# struct PowerLevel(u8); +# +# #[derive(Component)] +# struct Target(Option); +use bevy::log::{info, warn}; + +fn display_power_level_of_target(player_query: Query<&Target, With>, target_query: Query<&PowerLevel>){ + let target = player.single(); + + // If we're not targeting anything, we don't need to report the power level + if let Some(targeted_entity) = target.0 { + + // Query::get is fallible, so we must handle the Result + // or panic with a `.unwrap` or `.expect` call + match query.get(targeted_entity){ + Ok(power_level) => info!(power_level), + Err(e) => warn!("Getting the target failed with error: {:?}", e), + }; + } + +} +``` + +[`Entity`]: https://docs.rs/bevy/latest/bevy/ecs/entity/struct.Entity.html +[`u32`]: https://doc.rust-lang.org/std/primitive.u32.html +[`Result`]: https://doc.rust-lang.org/std/result/ +[`Query::get()`]: https://docs.rs/bevy/latest/bevy/ecs/system/struct.Query.html#method.get + +## Optional components in queries + +By default, query filters (just like query data requests) operate on a "and" basis: if you have a filter for `With` and another filter for `With`, only entities with both the `A` and `B` components will be fetched. +We can change this behavior by using the [`Or`] type, nesting primitive query filters like [`With`], [`Without`] and [`Changed`] inside of it to return entities that meet any of the criteria inside. + +```rust +#[derive(Component)] +struct Fruit; + +#[derive(Component)] +struct Delicious; + +#[derive(Component)] +struct Cheap; + +#[derive(Component)] +struct ToBuy; + +// This query will match any entities with the Fruit component, +// and either the Delicious or Cheap components +fn select_fruits_to_buy(query: Query, Or, With>)>, mut commands: Commands){ + for fruit_entity in query.iter(){ + commands.entity(fruit_entity).insert(ToBuy); + } +} +``` + +If we wanted to purchase fruits that were either `Delicious` or `Cheap`, we would use `Query<&mut Owner, (Or, With>)>` as the type of our query, allowing us to change the owner of any delicious and cheap fruit that we found. + +Note that the `Or` type (and other query tuples) can be nested indefinitely, allowing you to construct very complex logic if needed. + +If we want a query to include a component's data if it exists, we can use an `Option<&MyComponent>` query parameter in the first type parameter of [`Query`]. + +```rust +#[derive(Component)] +struct Fruit { + cooked: bool, + // Other fields omitted for brevity +} + +impl Fruit { + fn cook(&mut self) { + self.cooked = true; + } + + fn prepare(&mut self, step: PreparationStep){ + todo!() + } +} + + +#[derive(Component)] +struct SpecialPreparation { + steps: Vec, +} + +enum PreparationStep { + Slice, + Peel, + Mince, + Juice, + Deseed, +} + +#[derive(Component)] +struct NeedsCooking; + +fn prepare_fruits(mut query: Query<(&mut Fruit, Option<&SpecialPreparation>>, Option<&NeedsCooking>)>){ + for (fruit, maybe_preparation, maybe_needs_cooking) in query.iter() { + if let Some(preparation) = maybe_preparation { + for step in preparation.steps { + fruit.prepare(step); + } + } + + if maybe_needs_cooking.is_some(){ + fruit.cook(); + } + + fruit.serve(); + } +} + +``` + +If you need multiple optional components in a single query, consider using the [`AnyOf`] query combinator, which fetches data if at least one of the matching components is found. + +[`Or`]: https://docs.rs/bevy/latest/bevy/ecs/query/struct.Or.html +[`AnyOf`]: https://docs.rs/bevy/latest/bevy/ecs/query/struct.AnyOf.html +[`Changed`]: https://docs.rs/bevy/latest/bevy/ecs/query/struct.Changed.html +[`match`]: https://doc.rust-lang.org/std/keyword.match.html +[`Option`]: https://doc.rust-lang.org/std/option/enum.Option.html + +### Conflicting queries + +As the logic in your systems become more complex, you may find that you want to access data from two different queries at once. +In most cases, simply adding a second query as another system parameter works perfectly fine: + +```rust +# use bevy::prelude::*; +# +# #[derive(Component)] +# struct Aura; +# +# #[derive(Component)] +# struct Creature; + +fn defense_aura_system( + aura_query: Query<&Transform, With>, + mut target_query: Query<(&mut Defense, &Transform), With>) { + } +``` + +But as you use this pattern more, you may encounter an error that looks something like: + +```ignore + Query<&mut Transform, With> in system move_player accesses component(s) &mut Transform in a way that conflicts with a previous system parameter. Consider using `Without` to create disjoint Queries or merging conflicting Queries into a `ParamSet`. +``` + +What went wrong? It worked just fine before! + +Well, it turns out that Rust, in its infinite wisdom, +does not like it when you access the same data in multiple places at once, +if at least one of those accesses is mutable. +That's a result of its [ownership rules](https://doc.rust-lang.org/book/ch04-01-what-is-ownership.html): we could mutate data in the first query while the second query is trying to read the data, resulting in undefined behavior. +Which is bad. + +Of course, you already knew that, and have carefully thought about the architecture of your system, designing your system something like this: + +```rust +# use bevy::prelude::*; +# +# #[derive(Component)] +# struct Player; + +fn camera_follow_system( + player_query: Query<&Transform, With>, + mut camera_query: Query<&mut Transform, With>, +) { + let player_transform = player_query.single(); + let camera_query = camera_query.single_mut(); + todo!() +} +``` + +You know that there's never going to be an entity that has both `Player` and [`Camera`] on it, so there's no way that you're ever accessing the same [`Transform`] component twice. +Unfortunately, Rust *doesn't* know that. +We can fix this by making *sure* our queries our disjoint, no matter what bizarre entities might exist, through the judicious application of `Without` queries. + +```rust +# use bevy::prelude::*; +# +# #[derive(Component)] +# struct Player; + +fn camera_follow_system( + player_query: Query<&Transform, With>, + mut camera_query: Query<&mut Transform, (With, Without)>, +) { + let player_transform = player_query.single(); + let camera_query = camera_query.single_mut(); + todo!() +} +``` + +The other way to get around this issue is to use a [`ParamSet`], which permits multiple conflicting system parameters to exist in a single system. +The catch is that you can only access one of the parameters at a time. +Parameter sets can be useful when you need to access genuinely conflicting data, such as if we truly had an entity with both `Player` and [`Camera`] that we wanted to operate on in both loops of our system. +Let's try this approach: + +```rust +# use bevy::prelude::*; +# +# #[derive(Component)] +# struct Player; + +fn camera_follow_system( + mut queries: ParamSet<( + Query<&Transform, With>, + Query<&mut Transform, With> + )> +) { + // These references cannot be used at the same time + // We must complete our work with the first reference + // (including by cloning its data) + // before we can access the second parameter in our set + let player_transform = queries.p0().single(); + let camera_transform = camera_query.p1().single_mut(); + todo!() +} +``` + +Bevy's systems automatically run in parallel by default, so long as the scheduler can guarantee that the same data is never accessed in another place while it is being mutated. + +As a result, we can use the same query filtering techniques described to allow our *systems* to safely run in parallel. +In addition to improving parallelism, this also reduces the false positives when checking for [system execution order ambiguities](https://docs.rs/bevy/latest/bevy/ecs/schedule/struct.ReportExecutionOrderAmbiguities.html), as we can guarantee that the relative order of two systems that do not share data never changes the final outcome. + +[`Camera`]: https://docs.rs/bevy/latest/bevy/render/camera/struct.Camera.html +[`Transform`]: https://docs.rs/bevy/latest/bevy/transform/components/struct.Transform.html +[`ParamSet`]: https://docs.rs/bevy/latest/bevy/ecs/system/struct.ParamSet.html