From 68f03fc15c4191be5539caec7b033e0c288f6b3e Mon Sep 17 00:00:00 2001 From: CGMossa Date: Wed, 28 Apr 2021 21:03:10 +0000 Subject: [PATCH] Adding `WorldQuery` for `WithBundle` (#2024) In response to #2023, here is a draft for a PR. Fixes #2023 I've added an example to show how to use `WithBundle`, and also to test it out. Right now there is a bug: If a bundle and a query are "the same", then it doesn't filter out what it needs to filter out. Example: ``` Print component initated from bundle. [examples/ecs/query_bundle.rs:57] x = Dummy( <========= This should not get printed 111, ) [examples/ecs/query_bundle.rs:57] x = Dummy( 222, ) Show all components [examples/ecs/query_bundle.rs:50] x = Dummy( 111, ) [examples/ecs/query_bundle.rs:50] x = Dummy( 222, ) ``` However, it behaves the right way, if I add one more component to the bundle, so the query and the bundle doesn't look the same: ``` Print component initated from bundle. [examples/ecs/query_bundle.rs:57] x = Dummy( 222, ) Show all components [examples/ecs/query_bundle.rs:50] x = Dummy( 111, ) [examples/ecs/query_bundle.rs:50] x = Dummy( 222, ) ``` I hope this helps. I'm definitely up for tinkering with this, and adding anything that I'm asked to add or change. Co-authored-by: Carter Anderson --- Cargo.toml | 4 +++ crates/bevy_ecs/src/bundle.rs | 2 +- crates/bevy_ecs/src/query/filter.rs | 5 +++ examples/README.md | 10 ++++++ examples/ecs/query_bundle.rs | 50 +++++++++++++++++++++++++++++ 5 files changed, 70 insertions(+), 1 deletion(-) create mode 100644 examples/ecs/query_bundle.rs diff --git a/Cargo.toml b/Cargo.toml index 7cdb578729b67..6606679059a00 100644 --- a/Cargo.toml +++ b/Cargo.toml @@ -302,6 +302,10 @@ path = "examples/ecs/system_sets.rs" name = "timers" path = "examples/ecs/timers.rs" +[[example]] +name = "query_bundle" +path = "examples/ecs/query_bundle.rs" + # Games [[example]] name = "alien_cake_addict" diff --git a/crates/bevy_ecs/src/bundle.rs b/crates/bevy_ecs/src/bundle.rs index 3c2d7e39184ef..d640581c969aa 100644 --- a/crates/bevy_ecs/src/bundle.rs +++ b/crates/bevy_ecs/src/bundle.rs @@ -12,7 +12,7 @@ use std::{any::TypeId, collections::HashMap}; /// An ordered collection of components, commonly used for spawning entities, and adding and /// removing components in bulk. /// -/// You cannot query for a bundle, only individual components within it. +/// In order to query for components in a bundle use [crate::query::WithBundle]. /// /// Typically, you will simply use `#[derive(Bundle)]` when creating your own `Bundle`. /// The `Bundle` trait is automatically implemented for tuples of components: diff --git a/crates/bevy_ecs/src/query/filter.rs b/crates/bevy_ecs/src/query/filter.rs index f4a48a2633dd2..2d51028311674 100644 --- a/crates/bevy_ecs/src/query/filter.rs +++ b/crates/bevy_ecs/src/query/filter.rs @@ -295,6 +295,11 @@ impl<'a, T: Component> Fetch<'a> for WithoutFetch { pub struct WithBundle(PhantomData); +impl WorldQuery for WithBundle { + type Fetch = WithBundleFetch; + type State = WithBundleState; +} + pub struct WithBundleFetch { is_dense: bool, marker: PhantomData, diff --git a/examples/README.md b/examples/README.md index 6a3a513b236bc..ef0687d5e9991 100644 --- a/examples/README.md +++ b/examples/README.md @@ -32,6 +32,8 @@ git checkout v0.4.0 ## Table of Contents +- [Examples](#examples) + - [Table of Contents](#table-of-contents) - [The Bare Minimum](#the-bare-minimum) - [Hello, World!](#hello-world) - [Cross-Platform Examples](#cross-platform-examples) @@ -53,8 +55,15 @@ git checkout v0.4.0 - [Window](#window) - [Platform-Specific Examples](#platform-specific-examples) - [Android](#android) + - [Setup](#setup) + - [Build & Run](#build--run) + - [Old phones](#old-phones) - [iOS](#ios) + - [Setup](#setup-1) + - [Build & Run](#build--run-1) - [WASM](#wasm) + - [Setup](#setup-2) + - [Build & Run](#build--run-2) # The Bare Minimum @@ -145,6 +154,7 @@ Example | File | Description `fixed_timestep` | [`ecs/fixed_timestep.rs`](./ecs/fixed_timestep.rs) | Shows how to create systems that run every fixed timestep, rather than every tick `hierarchy` | [`ecs/hierarchy.rs`](./ecs/hierarchy.rs) | Creates a hierarchy of parents and children entities `parallel_query` | [`ecs/parallel_query.rs`](./ecs/parallel_query.rs) | Illustrates parallel queries with `ParallelIterator` +`query_bundle` | [`ecs/query_bundle.rs`](./ecs/query_bundle.rs) | Shows how to query entities that contain components in a `Bundle` `removal_detection` | [`ecs/removal_detection.rs`](./ecs/removal_detection.rs) | Query for entities that had a specific component removed in a previous stage during the current frame. `startup_system` | [`ecs/startup_system.rs`](./ecs/startup_system.rs) | Demonstrates a startup system (one that runs once when the app starts up) `state` | [`ecs/state.rs`](./ecs/state.rs) | Illustrates how to use States to control transitioning from a Menu state to an InGame state diff --git a/examples/ecs/query_bundle.rs b/examples/ecs/query_bundle.rs new file mode 100644 index 0000000000000..0691c0d849234 --- /dev/null +++ b/examples/ecs/query_bundle.rs @@ -0,0 +1,50 @@ +use bevy::{log::LogPlugin, prelude::*}; + +fn main() { + App::build() + .add_plugin(LogPlugin) + .add_startup_system(setup.system()) + .add_system(log_names.system().label(LogNamesSystem)) + .add_system(log_person_bundles.system().after(LogNamesSystem)) + .run(); +} + +#[derive(Debug, Hash, PartialEq, Eq, Clone, SystemLabel)] +struct LogNamesSystem; + +#[derive(Debug)] +struct Name(String); + +#[derive(Debug)] +struct Age(usize); + +#[derive(Debug, Bundle)] +struct PersonBundle { + name: Name, + age: Age, +} + +/// Sets up two entities, one with a [Name] component as part of a bundle, +/// and one entity with [Name] only. +fn setup(mut commands: Commands) { + commands.spawn().insert(Name("Steve".to_string())); + commands.spawn().insert_bundle(PersonBundle { + name: Name("Bob".to_string()), + age: Age(40), + }); +} + +fn log_names(query: Query<&Name>) { + info!("Log all entities with `Name` component"); + // this will necessarily have to print both components. + for name in query.iter() { + info!("{:?}", name); + } +} +fn log_person_bundles(query: Query<&Name, WithBundle>) { + info!("Log `Name` components from entities that have all components in `PersonBundle`."); + // this should only print `Name("Bob")`. + for name in query.iter() { + info!("{:?}", name); + } +}