Skip to content

Commit 7604665

Browse files
TheRawMeatballcart
andcommitted
Implement AnyOf queries (#2889)
Implements a new Queryable called AnyOf, which will return an item as long as at least one of it's requested Queryables returns something. For example, a `Query<AnyOf<(&A, &B, &C)>>` will return items with type `(Option<&A>, Option<&B>, Option<&C>)`, and will guarantee that for every element at least one of the option s is Some. This is a shorthand for queries like `Query<(Option<&A>, Option<&B>, Option<&C>), Or<(With<A>, With<B>, With&C>)>>`. Co-authored-by: Carter Anderson <[email protected]>
1 parent a0af066 commit 7604665

File tree

3 files changed

+134
-1
lines changed

3 files changed

+134
-1
lines changed

crates/bevy_ecs/src/lib.rs

Lines changed: 1 addition & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -26,7 +26,7 @@ pub mod prelude {
2626
component::Component,
2727
entity::Entity,
2828
event::{EventReader, EventWriter},
29-
query::{Added, ChangeTrackers, Changed, Or, QueryState, With, Without},
29+
query::{Added, AnyOf, ChangeTrackers, Changed, Or, QueryState, With, Without},
3030
schedule::{
3131
AmbiguitySetLabel, ExclusiveSystemDescriptorCoercion, ParallelSystemDescriptorCoercion,
3232
RunCriteria, RunCriteriaDescriptorCoercion, RunCriteriaLabel, RunCriteriaPiping,

crates/bevy_ecs/src/query/fetch.rs

Lines changed: 113 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -1103,7 +1103,120 @@ macro_rules! impl_tuple_fetch {
11031103
};
11041104
}
11051105

1106+
/// The `AnyOf` query parameter fetches entities with any of the component types included in T.
1107+
///
1108+
/// `Query<AnyOf<(&A, &B, &mut C)>>` is equivalent to `Query<(Option<&A>, Option<&B>, Option<&mut C>), (Or(With<A>, With<B>, With<C>)>`.
1109+
/// Each of the components in `T` is returned as an `Option`, as with `Option<A>` queries.
1110+
/// Entities are guaranteed to have at least one of the components in `T`.
1111+
pub struct AnyOf<T>(T);
1112+
1113+
macro_rules! impl_anytuple_fetch {
1114+
($(($name: ident, $state: ident)),*) => {
1115+
#[allow(non_snake_case)]
1116+
impl<'w, 's, $($name: Fetch<'w, 's>),*> Fetch<'w, 's> for AnyOf<($(($name, bool),)*)> {
1117+
type Item = ($(Option<$name::Item>,)*);
1118+
type State = AnyOf<($($name::State,)*)>;
1119+
1120+
#[allow(clippy::unused_unit)]
1121+
unsafe fn init(_world: &World, state: &Self::State, _last_change_tick: u32, _change_tick: u32) -> Self {
1122+
let ($($name,)*) = &state.0;
1123+
AnyOf(($(($name::init(_world, $name, _last_change_tick, _change_tick), false),)*))
1124+
}
1125+
1126+
1127+
const IS_DENSE: bool = true $(&& $name::IS_DENSE)*;
1128+
1129+
#[inline]
1130+
unsafe fn set_archetype(&mut self, _state: &Self::State, _archetype: &Archetype, _tables: &Tables) {
1131+
let ($($name,)*) = &mut self.0;
1132+
let ($($state,)*) = &_state.0;
1133+
$(
1134+
$name.1 = $state.matches_archetype(_archetype);
1135+
if $name.1 {
1136+
$name.0.set_archetype($state, _archetype, _tables);
1137+
}
1138+
)*
1139+
}
1140+
1141+
#[inline]
1142+
unsafe fn set_table(&mut self, _state: &Self::State, _table: &Table) {
1143+
let ($($name,)*) = &mut self.0;
1144+
let ($($state,)*) = &_state.0;
1145+
$(
1146+
$name.1 = $state.matches_table(_table);
1147+
if $name.1 {
1148+
$name.0.set_table($state, _table);
1149+
}
1150+
)*
1151+
}
1152+
1153+
#[inline]
1154+
#[allow(clippy::unused_unit)]
1155+
unsafe fn table_fetch(&mut self, _table_row: usize) -> Self::Item {
1156+
let ($($name,)*) = &mut self.0;
1157+
($(
1158+
$name.1.then(|| $name.0.table_fetch(_table_row)),
1159+
)*)
1160+
}
1161+
1162+
#[inline]
1163+
#[allow(clippy::unused_unit)]
1164+
unsafe fn archetype_fetch(&mut self, _archetype_index: usize) -> Self::Item {
1165+
let ($($name,)*) = &mut self.0;
1166+
($(
1167+
$name.1.then(|| $name.0.archetype_fetch(_archetype_index)),
1168+
)*)
1169+
}
1170+
}
1171+
1172+
// SAFETY: update_component_access and update_archetype_component_access are called for each item in the tuple
1173+
#[allow(non_snake_case)]
1174+
#[allow(clippy::unused_unit)]
1175+
unsafe impl<$($name: FetchState),*> FetchState for AnyOf<($($name,)*)> {
1176+
fn init(_world: &mut World) -> Self {
1177+
AnyOf(($($name::init(_world),)*))
1178+
}
1179+
1180+
fn update_component_access(&self, _access: &mut FilteredAccess<ComponentId>) {
1181+
let ($($name,)*) = &self.0;
1182+
$($name.update_component_access(_access);)*
1183+
}
1184+
1185+
fn update_archetype_component_access(&self, _archetype: &Archetype, _access: &mut Access<ArchetypeComponentId>) {
1186+
let ($($name,)*) = &self.0;
1187+
$(
1188+
if $name.matches_archetype(_archetype) {
1189+
$name.update_archetype_component_access(_archetype, _access);
1190+
}
1191+
)*
1192+
}
1193+
1194+
fn matches_archetype(&self, _archetype: &Archetype) -> bool {
1195+
let ($($name,)*) = &self.0;
1196+
false $(|| $name.matches_archetype(_archetype))*
1197+
}
1198+
1199+
fn matches_table(&self, _table: &Table) -> bool {
1200+
let ($($name,)*) = &self.0;
1201+
false $(|| $name.matches_table(_table))*
1202+
}
1203+
}
1204+
1205+
impl<$($name: WorldQuery),*> WorldQuery for AnyOf<($($name,)*)> {
1206+
type Fetch = AnyOf<($(($name::Fetch, bool),)*)>;
1207+
type ReadOnlyFetch = AnyOf<($(($name::ReadOnlyFetch, bool),)*)>;
1208+
1209+
type State = AnyOf<($($name::State,)*)>;
1210+
}
1211+
1212+
/// SAFETY: each item in the tuple is read only
1213+
unsafe impl<$($name: ReadOnlyFetch),*> ReadOnlyFetch for AnyOf<($(($name, bool),)*)> {}
1214+
1215+
};
1216+
}
1217+
11061218
all_tuples!(impl_tuple_fetch, 0, 15, F, S);
1219+
all_tuples!(impl_anytuple_fetch, 0, 15, F, S);
11071220

11081221
/// [`Fetch`] that does not actually fetch anything
11091222
///

crates/bevy_ecs/src/query/mod.rs

Lines changed: 20 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -12,12 +12,15 @@ pub use state::*;
1212

1313
#[cfg(test)]
1414
mod tests {
15+
use super::AnyOf;
1516
use crate::{self as bevy_ecs, component::Component, world::World};
1617

1718
#[derive(Component, Debug, Eq, PartialEq)]
1819
struct A(usize);
1920
#[derive(Component, Debug, Eq, PartialEq)]
2021
struct B(usize);
22+
#[derive(Component, Debug, Eq, PartialEq)]
23+
struct C(usize);
2124

2225
#[derive(Component, Debug, Eq, PartialEq)]
2326
#[component(storage = "SparseSet")]
@@ -184,4 +187,21 @@ mod tests {
184187
let values = world.query::<&B>().iter(&world).collect::<Vec<&B>>();
185188
assert_eq!(values, vec![&B(3)]);
186189
}
190+
191+
#[test]
192+
fn any_query() {
193+
let mut world = World::new();
194+
195+
world.spawn().insert_bundle((A(1), B(2)));
196+
world.spawn().insert_bundle((A(2),));
197+
world.spawn().insert_bundle((C(3),));
198+
199+
let values: Vec<(Option<&A>, Option<&B>)> =
200+
world.query::<AnyOf<(&A, &B)>>().iter(&world).collect();
201+
202+
assert_eq!(
203+
values,
204+
vec![(Some(&A(1)), Some(&B(2))), (Some(&A(2)), None),]
205+
);
206+
}
187207
}

0 commit comments

Comments
 (0)