Skip to content
New issue

Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.

By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.

Already on GitHub? Sign in to your account

Add support for 'or' in ECS querying for tuple queries #358

Merged
merged 6 commits into from
Sep 2, 2020
Merged
Show file tree
Hide file tree
Changes from 5 commits
Commits
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
3 changes: 2 additions & 1 deletion crates/bevy_ecs/hecs/src/lib.rs
Original file line number Diff line number Diff line change
Expand Up @@ -81,7 +81,8 @@ pub use bundle::{Bundle, DynamicBundle, MissingComponent};
pub use entities::{Entity, Location, NoSuchEntity};
pub use entity_builder::{BuiltEntity, EntityBuilder};
pub use query::{
Access, Added, BatchedIter, Changed, Mut, Mutated, Query, QueryBorrow, QueryIter, With, Without,
Access, Added, BatchedIter, Changed, Mut, Mutated, Or, Query, QueryBorrow, QueryIter, With,
Without,
};
pub use query_one::QueryOne;
pub use world::{ArchetypesGeneration, Component, ComponentError, Iter, SpawnBatchIter, World};
Expand Down
105 changes: 105 additions & 0 deletions crates/bevy_ecs/hecs/src/query.rs
Original file line number Diff line number Diff line change
Expand Up @@ -240,6 +240,85 @@ impl<'a, T: Component> Fetch<'a> for FetchMut<T> {
}
}

macro_rules! impl_or_query {
( $( $T:ident ),+; $( $t:ident ),+ ) => {
impl<$( $T: Query ),+> Query for Or<($( $T ),+)> {
type Fetch = FetchOr<($( $T::Fetch ),+)>;
}

impl<'a, $( $T: Fetch<'a> ),+> Fetch<'a> for FetchOr<($( $T ),+)> {
type Item = ($( $T::Item ),+);

fn access(archetype: &Archetype) -> Option<Access> {
if false $(|| $T::access(archetype).is_none() )+ {
cart marked this conversation as resolved.
Show resolved Hide resolved
return None
}
Some(Access::Read)
}

fn borrow(archetype: &Archetype) {
$(
$T::borrow(archetype);
)+
}

unsafe fn get(archetype: &'a Archetype, offset: usize) -> Option<Self> {
Some(Self(( $( $T::get(archetype, offset)?),+ )))
}

fn release(archetype: &Archetype) {
$(
$T::release(archetype);
)+
}

unsafe fn next(&mut self) -> Self::Item {
let ($( $t ),+) = &mut self.0;
($( $t.next() ),+)
}

unsafe fn should_skip(&self) -> bool {
let ($( $t ),+) = &self.0;
true $( && $t.should_skip() )+
}
}
};
}

impl_or_query!(Q1, Q2; q1, q2);
cart marked this conversation as resolved.
Show resolved Hide resolved
impl_or_query!(Q1, Q2, Q3; q1, q2, q3);
impl_or_query!(Q1, Q2, Q3, Q4; q1, q2, q3, q4);
impl_or_query!(Q1, Q2, Q3, Q4, Q5; q1, q2, q3, q4, q5);
impl_or_query!(Q1, Q2, Q3, Q4, Q5, Q6; q1, q2, q3, q4, q5, q6);
impl_or_query!(Q1, Q2, Q3, Q4, Q5, Q6, Q7; q1, q2, q3, q4, q5, q6, q7);
impl_or_query!(Q1, Q2, Q3, Q4, Q5, Q6, Q7, Q8; q1, q2, q3, q4, q5, q6, q7, q8);
impl_or_query!(Q1, Q2, Q3, Q4, Q5, Q6, Q7, Q8, Q9; q1, q2, q3, q4, q5, q6, q7, q8, q9);
impl_or_query!(Q1, Q2, Q3, Q4, Q5, Q6, Q7, Q8, Q9, Q10; q1, q2, q3, q4, q5, q6, q7, q8, q9, q10);

/// Query transformer performing a logical or on a pair of queries
/// Intended to be used on Mutated or Changed queries.
/// # Example
/// ```
/// # use bevy_hecs::*;
/// let mut world = World::new();
/// world.spawn((123, true, 1., Some(1)));
/// world.spawn((456, false, 2., Some(0)));
/// for mut b in world.query::<Mut<i32>>().iter().skip(1).take(1) {
/// *b += 1;
/// }
/// let components = world
/// .query::<Or<(Mutated<bool>, Mutated<i32>, Mutated<f64>, Mutated<Option<i32>>)>>()
/// .iter()
/// .map(|(b, i, f, o)| (*b, *i))
/// .collect::<Vec<_>>();
/// assert_eq!(components, &[(false, 457)]);
/// ```
pub struct Or<T>(PhantomData<T>);
//pub struct Or<Q1, Q2, Q3>(PhantomData<(Q1, Q2, Q3)>);

#[doc(hidden)]
pub struct FetchOr<T>(T);

/// Query transformer that skips entities that have a `T` component that has
/// not been mutated since the last pass of the system. This does not include
/// components that were added in since the last pass.
Expand Down Expand Up @@ -1065,6 +1144,32 @@ mod tests {
assert_eq!(a_b_changed, vec![e2]);
}

#[test]
fn or_mutated_query() {
let mut world = World::default();
let e1 = world.spawn((A(0), B(0)));
let e2 = world.spawn((A(0), B(0)));
let e3 = world.spawn((A(0), B(0)));
let _e4 = world.spawn((A(0), B(0)));

// Mutate A in entities e1 and e2
for mut a in world.query::<Mut<A>>().iter().take(2) {
a.0 += 1;
}
// Mutate B in entities e2 and e3
for mut b in world.query::<Mut<B>>().iter().skip(1).take(2) {
b.0 += 1;
}

let a_b_changed = world
.query::<(Or<(Mutated<A>, Mutated<B>)>, Entity)>()
.iter()
.map(|((_a, _b), e)| e)
.collect::<Vec<Entity>>();
// e1 has mutated A, e3 has mutated B, e2 has mutated A and B, _e4 has no mutated component
assert_eq!(a_b_changed, vec![e1, e2, e3]);
}

#[test]
fn changed_query() {
let mut world = World::default();
Expand Down