mirror of
https://github.com/bevyengine/bevy
synced 2024-11-10 15:14:50 +00:00
query filters (#834)
This commit is contained in:
parent
a266578992
commit
e769974d6a
21 changed files with 470 additions and 558 deletions
|
@ -281,22 +281,24 @@ pub fn impl_query_set(_input: TokenStream) -> TokenStream {
|
|||
let mut tokens = TokenStream::new();
|
||||
let max_queries = 4;
|
||||
let queries = get_idents(|i| format!("Q{}", i), max_queries);
|
||||
let filters = get_idents(|i| format!("F{}", i), max_queries);
|
||||
let lifetimes = get_lifetimes(|i| format!("'q{}", i), max_queries);
|
||||
let mut query_fns = Vec::new();
|
||||
let mut query_fn_muts = Vec::new();
|
||||
for i in 0..max_queries {
|
||||
let query = &queries[i];
|
||||
let lifetime = &lifetimes[i];
|
||||
let filter = &filters[i];
|
||||
let fn_name = Ident::new(&format!("q{}", i), Span::call_site());
|
||||
let fn_name_mut = Ident::new(&format!("q{}_mut", i), Span::call_site());
|
||||
let index = Index::from(i);
|
||||
query_fns.push(quote! {
|
||||
pub fn #fn_name(&self) -> &Query<#lifetime, #query> {
|
||||
pub fn #fn_name(&self) -> &Query<#lifetime, #query, #filter> {
|
||||
&self.value.#index
|
||||
}
|
||||
});
|
||||
query_fn_muts.push(quote! {
|
||||
pub fn #fn_name_mut(&mut self) -> &mut Query<#lifetime, #query> {
|
||||
pub fn #fn_name_mut(&mut self) -> &mut Query<#lifetime, #query, #filter> {
|
||||
&mut self.value.#index
|
||||
}
|
||||
});
|
||||
|
@ -304,15 +306,16 @@ pub fn impl_query_set(_input: TokenStream) -> TokenStream {
|
|||
|
||||
for query_count in 1..=max_queries {
|
||||
let query = &queries[0..query_count];
|
||||
let filter = &filters[0..query_count];
|
||||
let lifetime = &lifetimes[0..query_count];
|
||||
let query_fn = &query_fns[0..query_count];
|
||||
let query_fn_mut = &query_fn_muts[0..query_count];
|
||||
tokens.extend(TokenStream::from(quote! {
|
||||
impl<#(#lifetime,)* #(#query: HecsQuery,)*> QueryTuple for (#(Query<#lifetime, #query>,)*) {
|
||||
impl<#(#lifetime,)* #(#query: HecsQuery,)* #(#filter: QueryFilter,)*> QueryTuple for (#(Query<#lifetime, #query, #filter>,)*) {
|
||||
unsafe fn new(world: &World, component_access: &TypeAccess<ArchetypeComponent>) -> Self {
|
||||
(
|
||||
#(
|
||||
Query::<#query>::new(
|
||||
Query::<#query, #filter>::new(
|
||||
std::mem::transmute(world),
|
||||
std::mem::transmute(component_access),
|
||||
),
|
||||
|
@ -322,12 +325,12 @@ pub fn impl_query_set(_input: TokenStream) -> TokenStream {
|
|||
|
||||
fn get_accesses() -> Vec<QueryAccess> {
|
||||
vec![
|
||||
#(<#query::Fetch as Fetch>::access(),)*
|
||||
#(QueryAccess::union(vec![<#query::Fetch as Fetch>::access(), #filter::access()]),)*
|
||||
]
|
||||
}
|
||||
}
|
||||
|
||||
impl<#(#lifetime,)* #(#query: HecsQuery,)*> QuerySet<(#(Query<#lifetime, #query>,)*)> {
|
||||
impl<#(#lifetime,)* #(#query: HecsQuery,)* #(#filter: QueryFilter,)*> QuerySet<(#(Query<#lifetime, #query, #filter>,)*)> {
|
||||
#(#query_fn)*
|
||||
#(#query_fn_mut)*
|
||||
}
|
||||
|
|
|
@ -282,10 +282,11 @@ impl<T: Hash + Eq + PartialEq + Copy> TypeAccess<T> {
|
|||
|
||||
#[cfg(test)]
|
||||
mod tests {
|
||||
use crate::{ArchetypeComponent, Entity, Fetch, Query, TypeAccess, With, World};
|
||||
use crate::{ArchetypeComponent, Entity, Fetch, Query, QueryAccess, TypeAccess, World};
|
||||
use std::vec;
|
||||
|
||||
struct A;
|
||||
#[derive(Clone, Eq, PartialEq, Debug)]
|
||||
struct B;
|
||||
struct C;
|
||||
|
||||
|
@ -344,7 +345,7 @@ mod tests {
|
|||
);
|
||||
|
||||
let mut a_with_b_type_access = TypeAccess::default();
|
||||
<With<B, &A> as Query>::Fetch::access()
|
||||
QueryAccess::with::<B>(<&A as Query>::Fetch::access())
|
||||
.get_world_archetype_access(&world, Some(&mut a_with_b_type_access));
|
||||
|
||||
assert_eq!(
|
||||
|
@ -353,7 +354,7 @@ mod tests {
|
|||
);
|
||||
|
||||
let mut a_with_b_option_c_type_access = TypeAccess::default();
|
||||
<With<B, (&A, Option<&mut C>)> as Query>::Fetch::access()
|
||||
QueryAccess::with::<B>(<(&A, Option<&mut C>) as Query>::Fetch::access())
|
||||
.get_world_archetype_access(&world, Some(&mut a_with_b_option_c_type_access));
|
||||
|
||||
assert_eq!(
|
||||
|
|
245
crates/bevy_ecs/hecs/src/filter.rs
Normal file
245
crates/bevy_ecs/hecs/src/filter.rs
Normal file
|
@ -0,0 +1,245 @@
|
|||
use crate::{archetype::Archetype, Component, QueryAccess};
|
||||
use core::{any::TypeId, marker::PhantomData, ptr::NonNull};
|
||||
use std::{boxed::Box, vec};
|
||||
|
||||
pub trait QueryFilter: Sized {
|
||||
type EntityFilter: EntityFilter;
|
||||
fn access() -> QueryAccess;
|
||||
fn get_entity_filter(archetype: &Archetype) -> Option<Self::EntityFilter>;
|
||||
}
|
||||
|
||||
pub trait EntityFilter: Sized {
|
||||
const DANGLING: Self;
|
||||
|
||||
/// # Safety
|
||||
/// This might access archetype data in an unsafe manner. In general filters should be read-only and they should only access
|
||||
/// the data they have claimed in `access()`.
|
||||
unsafe fn matches_entity(&self, _offset: usize) -> bool;
|
||||
}
|
||||
|
||||
pub struct AnyEntityFilter;
|
||||
|
||||
impl EntityFilter for AnyEntityFilter {
|
||||
const DANGLING: Self = AnyEntityFilter;
|
||||
|
||||
#[inline]
|
||||
unsafe fn matches_entity(&self, _offset: usize) -> bool {
|
||||
true
|
||||
}
|
||||
}
|
||||
|
||||
pub struct Or<T>(pub T);
|
||||
|
||||
/// Query transformer that retrieves components of type `T` that have been mutated since the start of the frame.
|
||||
/// Added components do not count as mutated.
|
||||
pub struct Mutated<T>(NonNull<bool>, PhantomData<T>);
|
||||
|
||||
/// Query transformer that retrieves components of type `T` that have been added since the start of the frame.
|
||||
pub struct Added<T>(NonNull<bool>, PhantomData<T>);
|
||||
|
||||
/// Query transformer that retrieves components of type `T` that have either been mutated or added since the start of the frame.
|
||||
pub struct Changed<T>(NonNull<bool>, NonNull<bool>, PhantomData<T>);
|
||||
|
||||
impl QueryFilter for () {
|
||||
type EntityFilter = AnyEntityFilter;
|
||||
|
||||
fn access() -> QueryAccess {
|
||||
QueryAccess::None
|
||||
}
|
||||
|
||||
#[inline]
|
||||
fn get_entity_filter(_archetype: &Archetype) -> Option<Self::EntityFilter> {
|
||||
Some(AnyEntityFilter)
|
||||
}
|
||||
}
|
||||
|
||||
impl<T: Component> QueryFilter for Added<T> {
|
||||
type EntityFilter = Self;
|
||||
|
||||
fn access() -> QueryAccess {
|
||||
QueryAccess::read::<T>()
|
||||
}
|
||||
|
||||
#[inline]
|
||||
fn get_entity_filter(archetype: &Archetype) -> Option<Self::EntityFilter> {
|
||||
archetype
|
||||
.get_type_state(TypeId::of::<T>())
|
||||
.map(|state| Added(state.added(), Default::default()))
|
||||
}
|
||||
}
|
||||
|
||||
impl<T: Component> EntityFilter for Added<T> {
|
||||
const DANGLING: Self = Added(NonNull::dangling(), PhantomData::<T>);
|
||||
|
||||
#[inline]
|
||||
unsafe fn matches_entity(&self, offset: usize) -> bool {
|
||||
*self.0.as_ptr().add(offset)
|
||||
}
|
||||
}
|
||||
|
||||
impl<T: Component> QueryFilter for Mutated<T> {
|
||||
type EntityFilter = Self;
|
||||
|
||||
fn access() -> QueryAccess {
|
||||
QueryAccess::read::<T>()
|
||||
}
|
||||
|
||||
#[inline]
|
||||
fn get_entity_filter(archetype: &Archetype) -> Option<Self::EntityFilter> {
|
||||
archetype
|
||||
.get_type_state(TypeId::of::<T>())
|
||||
.map(|state| Mutated(state.mutated(), Default::default()))
|
||||
}
|
||||
}
|
||||
|
||||
impl<T: Component> EntityFilter for Mutated<T> {
|
||||
const DANGLING: Self = Mutated(NonNull::dangling(), PhantomData::<T>);
|
||||
|
||||
unsafe fn matches_entity(&self, offset: usize) -> bool {
|
||||
*self.0.as_ptr().add(offset)
|
||||
}
|
||||
}
|
||||
|
||||
impl<T: Component> QueryFilter for Changed<T> {
|
||||
type EntityFilter = Self;
|
||||
|
||||
fn access() -> QueryAccess {
|
||||
QueryAccess::read::<T>()
|
||||
}
|
||||
|
||||
#[inline]
|
||||
fn get_entity_filter(archetype: &Archetype) -> Option<Self::EntityFilter> {
|
||||
archetype
|
||||
.get_type_state(TypeId::of::<T>())
|
||||
.map(|state| Changed(state.added(), state.mutated(), Default::default()))
|
||||
}
|
||||
}
|
||||
|
||||
impl<T: Component> EntityFilter for Changed<T> {
|
||||
const DANGLING: Self = Changed(NonNull::dangling(), NonNull::dangling(), PhantomData::<T>);
|
||||
|
||||
#[inline]
|
||||
unsafe fn matches_entity(&self, offset: usize) -> bool {
|
||||
*self.0.as_ptr().add(offset) || *self.1.as_ptr().add(offset)
|
||||
}
|
||||
}
|
||||
|
||||
pub struct Without<T>(PhantomData<T>);
|
||||
|
||||
impl<T: Component> QueryFilter for Without<T> {
|
||||
type EntityFilter = AnyEntityFilter;
|
||||
|
||||
fn access() -> QueryAccess {
|
||||
QueryAccess::without::<T>(QueryAccess::None)
|
||||
}
|
||||
|
||||
#[inline]
|
||||
fn get_entity_filter(archetype: &Archetype) -> Option<Self::EntityFilter> {
|
||||
if archetype.has_type(TypeId::of::<T>()) {
|
||||
None
|
||||
} else {
|
||||
Some(AnyEntityFilter)
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
pub struct With<T>(PhantomData<T>);
|
||||
|
||||
impl<T: Component> QueryFilter for With<T> {
|
||||
type EntityFilter = AnyEntityFilter;
|
||||
|
||||
fn access() -> QueryAccess {
|
||||
QueryAccess::with::<T>(QueryAccess::None)
|
||||
}
|
||||
|
||||
#[inline]
|
||||
fn get_entity_filter(archetype: &Archetype) -> Option<Self::EntityFilter> {
|
||||
if archetype.has_type(TypeId::of::<T>()) {
|
||||
Some(AnyEntityFilter)
|
||||
} else {
|
||||
None
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
macro_rules! impl_query_filter_tuple {
|
||||
($($filter: ident),*) => {
|
||||
#[allow(unused_variables)]
|
||||
#[allow(non_snake_case)]
|
||||
impl<$($filter: QueryFilter),*> QueryFilter for ($($filter,)*) {
|
||||
type EntityFilter = ($($filter::EntityFilter,)*);
|
||||
|
||||
fn access() -> QueryAccess {
|
||||
QueryAccess::union(vec![
|
||||
$($filter::access(),)+
|
||||
])
|
||||
}
|
||||
|
||||
fn get_entity_filter(archetype: &Archetype) -> Option<Self::EntityFilter> {
|
||||
Some(($($filter::get_entity_filter(archetype)?,)*))
|
||||
}
|
||||
|
||||
}
|
||||
|
||||
#[allow(unused_variables)]
|
||||
#[allow(non_snake_case)]
|
||||
impl<$($filter: EntityFilter),*> EntityFilter for ($($filter,)*) {
|
||||
const DANGLING: Self = ($($filter::DANGLING,)*);
|
||||
unsafe fn matches_entity(&self, offset: usize) -> bool {
|
||||
let ($($filter,)*) = self;
|
||||
true $(&& $filter.matches_entity(offset))*
|
||||
}
|
||||
}
|
||||
|
||||
#[allow(unused_variables)]
|
||||
#[allow(non_snake_case)]
|
||||
impl<$($filter: QueryFilter),*> QueryFilter for Or<($($filter,)*)> {
|
||||
type EntityFilter = Or<($(Option<<$filter as QueryFilter>::EntityFilter>,)*)>;
|
||||
fn access() -> QueryAccess {
|
||||
QueryAccess::union(vec![
|
||||
$(QueryAccess::Optional(Box::new($filter::access())),)+
|
||||
])
|
||||
}
|
||||
|
||||
fn get_entity_filter(archetype: &Archetype) -> Option<Self::EntityFilter> {
|
||||
let mut matches_something = false;
|
||||
$(
|
||||
let $filter = $filter::get_entity_filter(archetype);
|
||||
matches_something = matches_something || $filter.is_some();
|
||||
)*
|
||||
if matches_something {
|
||||
Some(Or(($($filter,)*)))
|
||||
} else {
|
||||
None
|
||||
}
|
||||
}
|
||||
|
||||
}
|
||||
#[allow(unused_variables)]
|
||||
#[allow(non_snake_case)]
|
||||
impl<$($filter: EntityFilter),*> EntityFilter for Or<($(Option<$filter>,)*)> {
|
||||
const DANGLING: Self = Or(($(Some($filter::DANGLING),)*));
|
||||
unsafe fn matches_entity(&self, offset: usize) -> bool {
|
||||
let Or(($($filter,)*)) = self;
|
||||
false $(|| $filter.as_ref().map_or(false, |filter|filter.matches_entity(offset)))*
|
||||
}
|
||||
}
|
||||
};
|
||||
}
|
||||
|
||||
impl_query_filter_tuple!(A);
|
||||
impl_query_filter_tuple!(A, B);
|
||||
impl_query_filter_tuple!(A, B, C);
|
||||
impl_query_filter_tuple!(A, B, C, D);
|
||||
impl_query_filter_tuple!(A, B, C, D, E);
|
||||
impl_query_filter_tuple!(A, B, C, D, E, F);
|
||||
impl_query_filter_tuple!(A, B, C, D, E, F, G);
|
||||
impl_query_filter_tuple!(A, B, C, D, E, F, G, H);
|
||||
impl_query_filter_tuple!(A, B, C, D, E, F, G, H, I);
|
||||
impl_query_filter_tuple!(A, B, C, D, E, F, G, H, I, J);
|
||||
impl_query_filter_tuple!(A, B, C, D, E, F, G, H, I, J, K);
|
||||
impl_query_filter_tuple!(A, B, C, D, E, F, G, H, I, J, K, L);
|
||||
impl_query_filter_tuple!(A, B, C, D, E, F, G, H, I, J, K, L, M);
|
||||
impl_query_filter_tuple!(A, B, C, D, E, F, G, H, I, J, K, L, M, N);
|
||||
impl_query_filter_tuple!(A, B, C, D, E, F, G, H, I, J, K, L, M, N, O);
|
||||
impl_query_filter_tuple!(A, B, C, D, E, F, G, H, I, J, K, L, M, N, O, P);
|
|
@ -69,6 +69,7 @@ mod borrow;
|
|||
mod bundle;
|
||||
mod entities;
|
||||
mod entity_builder;
|
||||
mod filter;
|
||||
mod query;
|
||||
#[cfg(feature = "serde")]
|
||||
mod serde;
|
||||
|
@ -80,10 +81,8 @@ pub use borrow::{AtomicBorrow, Ref, RefMut};
|
|||
pub use bundle::{Bundle, DynamicBundle, MissingComponent};
|
||||
pub use entities::{Entity, EntityReserver, Location, NoSuchEntity};
|
||||
pub use entity_builder::{BuiltEntity, EntityBuilder};
|
||||
pub use query::{
|
||||
Added, Batch, BatchedIter, Changed, Mut, Mutated, Or, Query, QueryIter, ReadOnlyFetch, With,
|
||||
Without,
|
||||
};
|
||||
pub use filter::{Added, Changed, EntityFilter, Mutated, Or, QueryFilter, With, Without};
|
||||
pub use query::{Batch, BatchedIter, Mut, Query, QueryIter, ReadOnlyFetch};
|
||||
pub use world::{ArchetypesGeneration, Component, ComponentError, SpawnBatchIter, World};
|
||||
|
||||
// Unstable implementation details needed by the macros
|
||||
|
|
|
@ -14,16 +14,17 @@
|
|||
|
||||
// modified by Bevy contributors
|
||||
|
||||
use crate::{
|
||||
access::QueryAccess, archetype::Archetype, Component, Entity, EntityFilter, MissingComponent,
|
||||
QueryFilter,
|
||||
};
|
||||
use core::{
|
||||
marker::PhantomData,
|
||||
ops::{Deref, DerefMut},
|
||||
ptr::NonNull,
|
||||
};
|
||||
|
||||
use std::vec;
|
||||
|
||||
use crate::{access::QueryAccess, archetype::Archetype, Component, Entity, MissingComponent};
|
||||
|
||||
/// A collection of component types to fetch from a `World`
|
||||
pub trait Query {
|
||||
#[doc(hidden)]
|
||||
|
@ -33,9 +34,6 @@ pub trait Query {
|
|||
/// A fetch that is read only. This should only be implemented for read-only fetches.
|
||||
pub unsafe trait ReadOnlyFetch {}
|
||||
|
||||
/// A fetch that will always match every entity in an archetype (aka Fetch::should_skip always returns false)
|
||||
pub trait UnfilteredFetch {}
|
||||
|
||||
/// Streaming iterators over contiguous homogeneous ranges of components
|
||||
pub trait Fetch<'a>: Sized {
|
||||
/// Type of value to be fetched
|
||||
|
@ -54,14 +52,6 @@ pub trait Fetch<'a>: Sized {
|
|||
/// `offset` must be in bounds of `archetype`
|
||||
unsafe fn get(archetype: &'a Archetype, offset: usize) -> Option<Self>;
|
||||
|
||||
/// if this returns true, the nth item should be skipped during iteration
|
||||
///
|
||||
/// # Safety
|
||||
/// shouldn't be called if there is no current item
|
||||
unsafe fn should_skip(&self, _n: usize) -> bool {
|
||||
false
|
||||
}
|
||||
|
||||
/// Access the `n`th item in this archetype without bounds checking
|
||||
///
|
||||
/// # Safety
|
||||
|
@ -75,7 +65,6 @@ pub trait Fetch<'a>: Sized {
|
|||
#[derive(Copy, Clone, Debug)]
|
||||
pub struct EntityFetch(NonNull<Entity>);
|
||||
unsafe impl ReadOnlyFetch for EntityFetch {}
|
||||
impl UnfilteredFetch for EntityFetch {}
|
||||
|
||||
impl Query for Entity {
|
||||
type Fetch = EntityFetch;
|
||||
|
@ -112,7 +101,6 @@ impl<'a, T: Component> Query for &'a T {
|
|||
pub struct FetchRead<T>(NonNull<T>);
|
||||
|
||||
unsafe impl<T> ReadOnlyFetch for FetchRead<T> {}
|
||||
impl<T> UnfilteredFetch for FetchRead<T> {}
|
||||
|
||||
impl<'a, T: Component> Fetch<'a> for FetchRead<T> {
|
||||
type Item = &'a T;
|
||||
|
@ -197,7 +185,6 @@ impl<'a, T: Component> Query for Mut<'a, T> {
|
|||
}
|
||||
#[doc(hidden)]
|
||||
pub struct FetchMut<T>(NonNull<T>, NonNull<bool>);
|
||||
impl<T> UnfilteredFetch for FetchMut<T> {}
|
||||
|
||||
impl<'a, T: Component> Fetch<'a> for FetchMut<T> {
|
||||
type Item = Mut<'a, T>;
|
||||
|
@ -229,257 +216,9 @@ impl<'a, T: Component> Fetch<'a> for FetchMut<T> {
|
|||
}
|
||||
}
|
||||
|
||||
macro_rules! impl_or_query {
|
||||
( $( $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 ),+);
|
||||
|
||||
const DANGLING: Self = Self(($( $T::DANGLING ),+));
|
||||
|
||||
fn access() -> QueryAccess {
|
||||
QueryAccess::union(vec![
|
||||
$($T::access(),)+
|
||||
])
|
||||
}
|
||||
|
||||
|
||||
unsafe fn get(archetype: &'a Archetype, offset: usize) -> Option<Self> {
|
||||
Some(Self(( $( $T::get(archetype, offset)?),+ )))
|
||||
}
|
||||
|
||||
#[allow(non_snake_case)]
|
||||
unsafe fn fetch(&self, n: usize) -> Self::Item {
|
||||
let ($( $T ),+) = &self.0;
|
||||
($( $T.fetch(n) ),+)
|
||||
}
|
||||
|
||||
#[allow(non_snake_case)]
|
||||
unsafe fn should_skip(&self, n: usize) -> bool {
|
||||
let ($( $T ),+) = &self.0;
|
||||
true $( && $T.should_skip(n) )+
|
||||
}
|
||||
}
|
||||
|
||||
unsafe impl<$( $T: ReadOnlyFetch ),+> ReadOnlyFetch for Or<($( $T ),+)> {}
|
||||
unsafe impl<$( $T: ReadOnlyFetch ),+> ReadOnlyFetch for FetchOr<($( $T ),+)> {}
|
||||
};
|
||||
}
|
||||
|
||||
impl_or_query!(Q1, Q2);
|
||||
impl_or_query!(Q1, Q2, Q3);
|
||||
impl_or_query!(Q1, Q2, Q3, Q4);
|
||||
impl_or_query!(Q1, Q2, Q3, Q4, Q5);
|
||||
impl_or_query!(Q1, Q2, Q3, Q4, Q5, Q6);
|
||||
impl_or_query!(Q1, Q2, Q3, Q4, Q5, Q6, Q7);
|
||||
impl_or_query!(Q1, Q2, Q3, Q4, Q5, Q6, Q7, Q8);
|
||||
impl_or_query!(Q1, Q2, Q3, Q4, Q5, Q6, Q7, Q8, Q9);
|
||||
impl_or_query!(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::<Mut<i32>>().skip(1).take(1) {
|
||||
/// *b += 1;
|
||||
/// }
|
||||
/// let components = world
|
||||
/// .query_mut::<Or<(Mutated<bool>, Mutated<i32>, Mutated<f64>, Mutated<Option<i32>>)>>()
|
||||
/// .map(|(b, i, f, o)| (*b, *i))
|
||||
/// .collect::<Vec<_>>();
|
||||
/// assert_eq!(components, &[(false, 457)]);
|
||||
/// ```
|
||||
pub struct Or<T>(pub T);
|
||||
//pub struct Or<Q1, Q2, Q3>(PhantomData<(Q1, Q2, Q3)>);
|
||||
|
||||
#[doc(hidden)]
|
||||
pub struct FetchOr<T>(T);
|
||||
|
||||
/// Query transformer that retrieves components of type `T` that have been mutated since the start of the frame.
|
||||
/// Added components do not count as mutated.
|
||||
pub struct Mutated<'a, T> {
|
||||
value: &'a T,
|
||||
}
|
||||
|
||||
impl<'a, T: Component> Deref for Mutated<'a, T> {
|
||||
type Target = T;
|
||||
|
||||
#[inline]
|
||||
fn deref(&self) -> &T {
|
||||
self.value
|
||||
}
|
||||
}
|
||||
|
||||
impl<'a, T: Component> Query for Mutated<'a, T> {
|
||||
type Fetch = FetchMutated<T>;
|
||||
}
|
||||
|
||||
#[doc(hidden)]
|
||||
pub struct FetchMutated<T>(NonNull<T>, NonNull<bool>);
|
||||
unsafe impl<T> ReadOnlyFetch for FetchMutated<T> {}
|
||||
|
||||
impl<'a, T: Component> Fetch<'a> for FetchMutated<T> {
|
||||
type Item = Mutated<'a, T>;
|
||||
|
||||
const DANGLING: Self = Self(NonNull::dangling(), NonNull::dangling());
|
||||
|
||||
#[inline]
|
||||
fn access() -> QueryAccess {
|
||||
QueryAccess::read::<T>()
|
||||
}
|
||||
|
||||
unsafe fn get(archetype: &'a Archetype, offset: usize) -> Option<Self> {
|
||||
archetype
|
||||
.get_with_type_state::<T>()
|
||||
.map(|(components, type_state)| {
|
||||
Self(
|
||||
NonNull::new_unchecked(components.as_ptr().add(offset)),
|
||||
NonNull::new_unchecked(type_state.mutated().as_ptr().add(offset)),
|
||||
)
|
||||
})
|
||||
}
|
||||
|
||||
unsafe fn should_skip(&self, n: usize) -> bool {
|
||||
// skip if the current item wasn't mutated
|
||||
!*self.1.as_ptr().add(n)
|
||||
}
|
||||
|
||||
#[inline]
|
||||
unsafe fn fetch(&self, n: usize) -> Self::Item {
|
||||
Mutated {
|
||||
value: &*self.0.as_ptr().add(n),
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
/// Query transformer that retrieves components of type `T` that have been added since the start of the frame.
|
||||
pub struct Added<'a, T> {
|
||||
value: &'a T,
|
||||
}
|
||||
|
||||
impl<'a, T: Component> Deref for Added<'a, T> {
|
||||
type Target = T;
|
||||
|
||||
#[inline]
|
||||
fn deref(&self) -> &T {
|
||||
self.value
|
||||
}
|
||||
}
|
||||
|
||||
impl<'a, T: Component> Query for Added<'a, T> {
|
||||
type Fetch = FetchAdded<T>;
|
||||
}
|
||||
|
||||
#[doc(hidden)]
|
||||
pub struct FetchAdded<T>(NonNull<T>, NonNull<bool>);
|
||||
unsafe impl<T> ReadOnlyFetch for FetchAdded<T> {}
|
||||
|
||||
impl<'a, T: Component> Fetch<'a> for FetchAdded<T> {
|
||||
type Item = Added<'a, T>;
|
||||
|
||||
const DANGLING: Self = Self(NonNull::dangling(), NonNull::dangling());
|
||||
|
||||
#[inline]
|
||||
fn access() -> QueryAccess {
|
||||
QueryAccess::read::<T>()
|
||||
}
|
||||
|
||||
unsafe fn get(archetype: &'a Archetype, offset: usize) -> Option<Self> {
|
||||
archetype
|
||||
.get_with_type_state::<T>()
|
||||
.map(|(components, type_state)| {
|
||||
Self(
|
||||
NonNull::new_unchecked(components.as_ptr().add(offset)),
|
||||
NonNull::new_unchecked(type_state.added().as_ptr().add(offset)),
|
||||
)
|
||||
})
|
||||
}
|
||||
|
||||
unsafe fn should_skip(&self, n: usize) -> bool {
|
||||
// skip if the current item wasn't added
|
||||
!*self.1.as_ptr().add(n)
|
||||
}
|
||||
|
||||
#[inline]
|
||||
unsafe fn fetch(&self, n: usize) -> Self::Item {
|
||||
Added {
|
||||
value: &*self.0.as_ptr().add(n),
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
/// Query transformer that retrieves components of type `T` that have either been mutated or added since the start of the frame.
|
||||
pub struct Changed<'a, T> {
|
||||
value: &'a T,
|
||||
}
|
||||
|
||||
impl<'a, T: Component> Deref for Changed<'a, T> {
|
||||
type Target = T;
|
||||
|
||||
#[inline]
|
||||
fn deref(&self) -> &T {
|
||||
self.value
|
||||
}
|
||||
}
|
||||
|
||||
impl<'a, T: Component> Query for Changed<'a, T> {
|
||||
type Fetch = FetchChanged<T>;
|
||||
}
|
||||
|
||||
#[doc(hidden)]
|
||||
pub struct FetchChanged<T>(NonNull<T>, NonNull<bool>, NonNull<bool>);
|
||||
unsafe impl<T> ReadOnlyFetch for FetchChanged<T> {}
|
||||
|
||||
impl<'a, T: Component> Fetch<'a> for FetchChanged<T> {
|
||||
type Item = Changed<'a, T>;
|
||||
|
||||
const DANGLING: Self = Self(
|
||||
NonNull::dangling(),
|
||||
NonNull::dangling(),
|
||||
NonNull::dangling(),
|
||||
);
|
||||
|
||||
#[inline]
|
||||
fn access() -> QueryAccess {
|
||||
QueryAccess::read::<T>()
|
||||
}
|
||||
|
||||
unsafe fn get(archetype: &'a Archetype, offset: usize) -> Option<Self> {
|
||||
archetype
|
||||
.get_with_type_state::<T>()
|
||||
.map(|(components, type_state)| {
|
||||
Self(
|
||||
NonNull::new_unchecked(components.as_ptr().add(offset)),
|
||||
NonNull::new_unchecked(type_state.added().as_ptr().add(offset)),
|
||||
NonNull::new_unchecked(type_state.mutated().as_ptr().add(offset)),
|
||||
)
|
||||
})
|
||||
}
|
||||
|
||||
unsafe fn should_skip(&self, n: usize) -> bool {
|
||||
// skip if the current item wasn't added or mutated
|
||||
!*self.1.as_ptr().add(n) && !*self.2.as_ptr().add(n)
|
||||
}
|
||||
|
||||
#[inline]
|
||||
unsafe fn fetch(&self, n: usize) -> Self::Item {
|
||||
Changed {
|
||||
value: &*self.0.as_ptr().add(n),
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
#[doc(hidden)]
|
||||
pub struct TryFetch<T>(Option<T>);
|
||||
unsafe impl<T> ReadOnlyFetch for TryFetch<T> where T: ReadOnlyFetch {}
|
||||
impl<T> UnfilteredFetch for TryFetch<T> where T: UnfilteredFetch {}
|
||||
|
||||
impl<'a, T: Fetch<'a>> Fetch<'a> for TryFetch<T> {
|
||||
type Item = Option<T::Item>;
|
||||
|
@ -498,142 +237,27 @@ impl<'a, T: Fetch<'a>> Fetch<'a> for TryFetch<T> {
|
|||
unsafe fn fetch(&self, n: usize) -> Option<T::Item> {
|
||||
Some(self.0.as_ref()?.fetch(n))
|
||||
}
|
||||
|
||||
unsafe fn should_skip(&self, n: usize) -> bool {
|
||||
self.0.as_ref().map_or(false, |fetch| fetch.should_skip(n))
|
||||
}
|
||||
}
|
||||
|
||||
/// Query transformer skipping entities that have a `T` component
|
||||
///
|
||||
/// See also `QueryBorrow::without`.
|
||||
///
|
||||
/// # Example
|
||||
/// ```
|
||||
/// # use bevy_hecs::*;
|
||||
/// let mut world = World::new();
|
||||
/// let a = world.spawn((123, true, "abc"));
|
||||
/// let b = world.spawn((456, false));
|
||||
/// let c = world.spawn((42, "def"));
|
||||
/// let entities = world.query::<Without<bool, (Entity, &i32)>>()
|
||||
/// .map(|(e, &i)| (e, i))
|
||||
/// .collect::<Vec<_>>();
|
||||
/// assert_eq!(entities, &[(c, 42)]);
|
||||
/// ```
|
||||
pub struct Without<T, Q>(PhantomData<(Q, fn(T))>);
|
||||
|
||||
impl<T: Component, Q: Query> Query for Without<T, Q> {
|
||||
type Fetch = FetchWithout<T, Q::Fetch>;
|
||||
}
|
||||
|
||||
#[doc(hidden)]
|
||||
pub struct FetchWithout<T, F>(F, PhantomData<fn(T)>);
|
||||
unsafe impl<'a, T: Component, F: Fetch<'a>> ReadOnlyFetch for FetchWithout<T, F> where
|
||||
F: ReadOnlyFetch
|
||||
{
|
||||
}
|
||||
impl<T, F> UnfilteredFetch for FetchWithout<T, F> where F: UnfilteredFetch {}
|
||||
|
||||
impl<'a, T: Component, F: Fetch<'a>> Fetch<'a> for FetchWithout<T, F> {
|
||||
type Item = F::Item;
|
||||
|
||||
const DANGLING: Self = Self(F::DANGLING, PhantomData);
|
||||
|
||||
#[inline]
|
||||
fn access() -> QueryAccess {
|
||||
QueryAccess::without::<T>(F::access())
|
||||
}
|
||||
|
||||
unsafe fn get(archetype: &'a Archetype, offset: usize) -> Option<Self> {
|
||||
if archetype.has::<T>() {
|
||||
return None;
|
||||
}
|
||||
Some(Self(F::get(archetype, offset)?, PhantomData))
|
||||
}
|
||||
|
||||
unsafe fn fetch(&self, n: usize) -> F::Item {
|
||||
self.0.fetch(n)
|
||||
}
|
||||
|
||||
unsafe fn should_skip(&self, n: usize) -> bool {
|
||||
self.0.should_skip(n)
|
||||
}
|
||||
}
|
||||
|
||||
/// Query transformer skipping entities that do not have a `T` component
|
||||
///
|
||||
/// See also `QueryBorrow::with`.
|
||||
///
|
||||
/// # Example
|
||||
/// ```
|
||||
/// # use bevy_hecs::*;
|
||||
/// let mut world = World::new();
|
||||
/// let a = world.spawn((123, true, "abc"));
|
||||
/// let b = world.spawn((456, false));
|
||||
/// let c = world.spawn((42, "def"));
|
||||
/// let entities = world.query::<With<bool, (Entity, &i32)>>()
|
||||
/// .map(|(e, &i)| (e, i))
|
||||
/// .collect::<Vec<_>>();
|
||||
/// assert_eq!(entities.len(), 2);
|
||||
/// assert!(entities.contains(&(a, 123)));
|
||||
/// assert!(entities.contains(&(b, 456)));
|
||||
/// ```
|
||||
pub struct With<T, Q>(PhantomData<(Q, fn(T))>);
|
||||
|
||||
impl<T: Component, Q: Query> Query for With<T, Q> {
|
||||
type Fetch = FetchWith<T, Q::Fetch>;
|
||||
}
|
||||
|
||||
#[doc(hidden)]
|
||||
pub struct FetchWith<T, F>(F, PhantomData<fn(T)>);
|
||||
unsafe impl<'a, T: Component, F: Fetch<'a>> ReadOnlyFetch for FetchWith<T, F> where F: ReadOnlyFetch {}
|
||||
impl<T, F> UnfilteredFetch for FetchWith<T, F> where F: UnfilteredFetch {}
|
||||
|
||||
impl<'a, T: Component, F: Fetch<'a>> Fetch<'a> for FetchWith<T, F> {
|
||||
type Item = F::Item;
|
||||
|
||||
const DANGLING: Self = Self(F::DANGLING, PhantomData);
|
||||
|
||||
#[inline]
|
||||
fn access() -> QueryAccess {
|
||||
QueryAccess::with::<T>(F::access())
|
||||
}
|
||||
|
||||
unsafe fn get(archetype: &'a Archetype, offset: usize) -> Option<Self> {
|
||||
if !archetype.has::<T>() {
|
||||
return None;
|
||||
}
|
||||
Some(Self(F::get(archetype, offset)?, PhantomData))
|
||||
}
|
||||
|
||||
unsafe fn fetch(&self, n: usize) -> F::Item {
|
||||
self.0.fetch(n)
|
||||
}
|
||||
|
||||
unsafe fn should_skip(&self, n: usize) -> bool {
|
||||
self.0.should_skip(n)
|
||||
}
|
||||
}
|
||||
|
||||
struct ChunkInfo<Q: Query> {
|
||||
struct ChunkInfo<Q: Query, F: QueryFilter> {
|
||||
fetch: Q::Fetch,
|
||||
filter: F::EntityFilter,
|
||||
len: usize,
|
||||
}
|
||||
|
||||
/// Iterator over the set of entities with the components in `Q`
|
||||
pub struct QueryIter<'w, Q: Query> {
|
||||
pub struct QueryIter<'w, Q: Query, F: QueryFilter> {
|
||||
archetypes: &'w [Archetype],
|
||||
archetype_index: usize,
|
||||
chunk_info: ChunkInfo<Q>,
|
||||
chunk_info: ChunkInfo<Q, F>,
|
||||
chunk_position: usize,
|
||||
}
|
||||
|
||||
impl<'w, Q: Query> QueryIter<'w, Q> {
|
||||
// #[allow(clippy::declare_interior_mutable_const)] // no trait bounds on const fns
|
||||
// const EMPTY: Q::Fetch = Q::Fetch::DANGLING;
|
||||
const EMPTY: ChunkInfo<Q> = ChunkInfo {
|
||||
impl<'w, Q: Query, F: QueryFilter> QueryIter<'w, Q, F> {
|
||||
const EMPTY: ChunkInfo<Q, F> = ChunkInfo {
|
||||
fetch: Q::Fetch::DANGLING,
|
||||
len: 0,
|
||||
filter: F::EntityFilter::DANGLING,
|
||||
};
|
||||
|
||||
/// Creates a new QueryIter
|
||||
|
@ -648,7 +272,7 @@ impl<'w, Q: Query> QueryIter<'w, Q> {
|
|||
}
|
||||
}
|
||||
|
||||
impl<'w, Q: Query> Iterator for QueryIter<'w, Q> {
|
||||
impl<'w, Q: Query, F: QueryFilter> Iterator for QueryIter<'w, Q, F> {
|
||||
type Item = <Q::Fetch as Fetch<'w>>::Item;
|
||||
|
||||
#[inline]
|
||||
|
@ -660,18 +284,21 @@ impl<'w, Q: Query> Iterator for QueryIter<'w, Q> {
|
|||
self.archetype_index += 1;
|
||||
self.chunk_position = 0;
|
||||
self.chunk_info = Q::Fetch::get(archetype, 0)
|
||||
.map(|fetch| ChunkInfo {
|
||||
fetch,
|
||||
len: archetype.len(),
|
||||
.and_then(|fetch| {
|
||||
Some(ChunkInfo {
|
||||
fetch,
|
||||
len: archetype.len(),
|
||||
filter: F::get_entity_filter(archetype)?,
|
||||
})
|
||||
})
|
||||
.unwrap_or(Self::EMPTY);
|
||||
continue;
|
||||
}
|
||||
|
||||
if self
|
||||
if !self
|
||||
.chunk_info
|
||||
.fetch
|
||||
.should_skip(self.chunk_position as usize)
|
||||
.filter
|
||||
.matches_entity(self.chunk_position as usize)
|
||||
{
|
||||
self.chunk_position += 1;
|
||||
continue;
|
||||
|
@ -687,10 +314,7 @@ impl<'w, Q: Query> Iterator for QueryIter<'w, Q> {
|
|||
|
||||
// if the Fetch is an UnfilteredFetch, then we can cheaply compute the length of the query by getting
|
||||
// the length of each matching archetype
|
||||
impl<'w, Q: Query> ExactSizeIterator for QueryIter<'w, Q>
|
||||
where
|
||||
Q::Fetch: UnfilteredFetch,
|
||||
{
|
||||
impl<'w, Q: Query> ExactSizeIterator for QueryIter<'w, Q, ()> {
|
||||
fn len(&self) -> usize {
|
||||
self.archetypes
|
||||
.iter()
|
||||
|
@ -700,20 +324,21 @@ where
|
|||
}
|
||||
}
|
||||
|
||||
struct ChunkIter<Q: Query> {
|
||||
struct ChunkIter<Q: Query, F: QueryFilter> {
|
||||
fetch: Q::Fetch,
|
||||
filter: F::EntityFilter,
|
||||
position: usize,
|
||||
len: usize,
|
||||
}
|
||||
|
||||
impl<Q: Query> ChunkIter<Q> {
|
||||
impl<Q: Query, F: QueryFilter> ChunkIter<Q, F> {
|
||||
unsafe fn next<'a>(&mut self) -> Option<<Q::Fetch as Fetch<'a>>::Item> {
|
||||
loop {
|
||||
if self.position == self.len {
|
||||
return None;
|
||||
}
|
||||
|
||||
if self.fetch.should_skip(self.position as usize) {
|
||||
if !self.filter.matches_entity(self.position as usize) {
|
||||
self.position += 1;
|
||||
continue;
|
||||
}
|
||||
|
@ -726,15 +351,15 @@ impl<Q: Query> ChunkIter<Q> {
|
|||
}
|
||||
|
||||
/// Batched version of `QueryIter`
|
||||
pub struct BatchedIter<'w, Q: Query> {
|
||||
pub struct BatchedIter<'w, Q: Query, F: QueryFilter> {
|
||||
archetypes: &'w [Archetype],
|
||||
archetype_index: usize,
|
||||
batch_size: usize,
|
||||
batch: usize,
|
||||
_marker: PhantomData<Q>,
|
||||
_marker: PhantomData<(Q, F)>,
|
||||
}
|
||||
|
||||
impl<'w, Q: Query> BatchedIter<'w, Q> {
|
||||
impl<'w, Q: Query, F: QueryFilter> BatchedIter<'w, Q, F> {
|
||||
pub(crate) fn new(archetypes: &'w [Archetype], batch_size: usize) -> Self {
|
||||
Self {
|
||||
archetypes,
|
||||
|
@ -746,11 +371,11 @@ impl<'w, Q: Query> BatchedIter<'w, Q> {
|
|||
}
|
||||
}
|
||||
|
||||
unsafe impl<'w, Q: Query> Send for BatchedIter<'w, Q> {}
|
||||
unsafe impl<'w, Q: Query> Sync for BatchedIter<'w, Q> {}
|
||||
unsafe impl<'w, Q: Query, F: QueryFilter> Send for BatchedIter<'w, Q, F> {}
|
||||
unsafe impl<'w, Q: Query, F: QueryFilter> Sync for BatchedIter<'w, Q, F> {}
|
||||
|
||||
impl<'w, Q: Query> Iterator for BatchedIter<'w, Q> {
|
||||
type Item = Batch<'w, Q>;
|
||||
impl<'w, Q: Query, F: QueryFilter> Iterator for BatchedIter<'w, Q, F> {
|
||||
type Item = Batch<'w, Q, F>;
|
||||
|
||||
fn next(&mut self) -> Option<Self::Item> {
|
||||
loop {
|
||||
|
@ -761,7 +386,10 @@ impl<'w, Q: Query> Iterator for BatchedIter<'w, Q> {
|
|||
self.batch = 0;
|
||||
continue;
|
||||
}
|
||||
if let Some(fetch) = unsafe { Q::Fetch::get(archetype, offset) } {
|
||||
if let (Some(fetch), Some(filter)) = (
|
||||
unsafe { Q::Fetch::get(archetype, offset) },
|
||||
F::get_entity_filter(archetype),
|
||||
) {
|
||||
self.batch += 1;
|
||||
return Some(Batch {
|
||||
_marker: PhantomData,
|
||||
|
@ -769,6 +397,7 @@ impl<'w, Q: Query> Iterator for BatchedIter<'w, Q> {
|
|||
fetch,
|
||||
position: 0,
|
||||
len: self.batch_size.min(archetype.len() - offset),
|
||||
filter,
|
||||
},
|
||||
});
|
||||
} else {
|
||||
|
@ -784,12 +413,12 @@ impl<'w, Q: Query> Iterator for BatchedIter<'w, Q> {
|
|||
}
|
||||
|
||||
/// A sequence of entities yielded by `BatchedIter`
|
||||
pub struct Batch<'q, Q: Query> {
|
||||
pub struct Batch<'q, Q: Query, F: QueryFilter> {
|
||||
_marker: PhantomData<&'q ()>,
|
||||
state: ChunkIter<Q>,
|
||||
state: ChunkIter<Q, F>,
|
||||
}
|
||||
|
||||
impl<'q, 'w, Q: Query> Iterator for Batch<'q, Q> {
|
||||
impl<'q, 'w, Q: Query, F: QueryFilter> Iterator for Batch<'q, Q, F> {
|
||||
type Item = <Q::Fetch as Fetch<'q>>::Item;
|
||||
|
||||
fn next(&mut self) -> Option<Self::Item> {
|
||||
|
@ -798,8 +427,8 @@ impl<'q, 'w, Q: Query> Iterator for Batch<'q, Q> {
|
|||
}
|
||||
}
|
||||
|
||||
unsafe impl<'q, Q: Query> Send for Batch<'q, Q> {}
|
||||
unsafe impl<'q, Q: Query> Sync for Batch<'q, Q> {}
|
||||
unsafe impl<'q, Q: Query, F: QueryFilter> Send for Batch<'q, Q, F> {}
|
||||
unsafe impl<'q, Q: Query, F: QueryFilter> Sync for Batch<'q, Q, F> {}
|
||||
|
||||
macro_rules! tuple_impl {
|
||||
($($name: ident),*) => {
|
||||
|
@ -825,13 +454,6 @@ macro_rules! tuple_impl {
|
|||
let ($($name,)*) = self;
|
||||
($($name.fetch(n),)*)
|
||||
}
|
||||
|
||||
#[allow(unused_variables)]
|
||||
unsafe fn should_skip(&self, n: usize) -> bool {
|
||||
#[allow(non_snake_case)]
|
||||
let ($($name,)*) = self;
|
||||
$($name.should_skip(n)||)* false
|
||||
}
|
||||
}
|
||||
|
||||
impl<$($name: Query),*> Query for ($($name,)*) {
|
||||
|
@ -839,7 +461,6 @@ macro_rules! tuple_impl {
|
|||
}
|
||||
|
||||
unsafe impl<$($name: ReadOnlyFetch),*> ReadOnlyFetch for ($($name,)*) {}
|
||||
impl<$($name: UnfilteredFetch),*> UnfilteredFetch for ($($name,)*) {}
|
||||
};
|
||||
}
|
||||
|
||||
|
@ -847,7 +468,7 @@ smaller_tuples_too!(tuple_impl, O, N, M, L, K, J, I, H, G, F, E, D, C, B, A);
|
|||
|
||||
#[cfg(test)]
|
||||
mod tests {
|
||||
use crate::{Entity, Mut, Mutated, World};
|
||||
use crate::{Added, Changed, Entity, Mut, Mutated, Or, World};
|
||||
use std::{vec, vec::Vec};
|
||||
|
||||
use super::*;
|
||||
|
@ -863,8 +484,7 @@ mod tests {
|
|||
|
||||
fn get_added<Com: Component>(world: &World) -> Vec<Entity> {
|
||||
world
|
||||
.query::<(Added<Com>, Entity)>()
|
||||
.map(|(_added, e)| e)
|
||||
.query_filtered::<Entity, Added<Com>>()
|
||||
.collect::<Vec<Entity>>()
|
||||
};
|
||||
|
||||
|
@ -880,8 +500,7 @@ mod tests {
|
|||
assert_eq!(get_added::<B>(&world), vec![e2]);
|
||||
|
||||
let added = world
|
||||
.query::<(Entity, Added<A>, Added<B>)>()
|
||||
.map(|a| a.0)
|
||||
.query_filtered::<Entity, (Added<A>, Added<B>)>()
|
||||
.collect::<Vec<Entity>>();
|
||||
assert_eq!(added, vec![e2]);
|
||||
}
|
||||
|
@ -900,24 +519,21 @@ mod tests {
|
|||
}
|
||||
}
|
||||
|
||||
fn get_mutated_a(world: &mut World) -> Vec<Entity> {
|
||||
world
|
||||
.query_mut::<(Mutated<A>, Entity)>()
|
||||
.map(|(_a, e)| e)
|
||||
.collect::<Vec<Entity>>()
|
||||
fn get_filtered<F: QueryFilter>(world: &mut World) -> Vec<Entity> {
|
||||
world.query_filtered::<Entity, F>().collect::<Vec<Entity>>()
|
||||
};
|
||||
|
||||
assert_eq!(get_mutated_a(&mut world), vec![e1, e3]);
|
||||
assert_eq!(get_filtered::<Mutated<A>>(&mut world), vec![e1, e3]);
|
||||
|
||||
// ensure changing an entity's archetypes also moves its mutated state
|
||||
world.insert(e1, (C,)).unwrap();
|
||||
|
||||
assert_eq!(get_mutated_a(&mut world), vec![e3, e1], "changed entities list should not change (although the order will due to archetype moves)");
|
||||
assert_eq!(get_filtered::<Mutated<A>>(&mut world), vec![e3, e1], "changed entities list should not change (although the order will due to archetype moves)");
|
||||
|
||||
// spawning a new A entity should not change existing mutated state
|
||||
world.insert(e1, (A(0), B)).unwrap();
|
||||
assert_eq!(
|
||||
get_mutated_a(&mut world),
|
||||
get_filtered::<Mutated<A>>(&mut world),
|
||||
vec![e3, e1],
|
||||
"changed entities list should not change"
|
||||
);
|
||||
|
@ -925,7 +541,7 @@ mod tests {
|
|||
// removing an unchanged entity should not change mutated state
|
||||
world.despawn(e2).unwrap();
|
||||
assert_eq!(
|
||||
get_mutated_a(&mut world),
|
||||
get_filtered::<Mutated<A>>(&mut world),
|
||||
vec![e3, e1],
|
||||
"changed entities list should not change"
|
||||
);
|
||||
|
@ -933,26 +549,23 @@ mod tests {
|
|||
// removing a changed entity should remove it from enumeration
|
||||
world.despawn(e1).unwrap();
|
||||
assert_eq!(
|
||||
get_mutated_a(&mut world),
|
||||
get_filtered::<Mutated<A>>(&mut world),
|
||||
vec![e3],
|
||||
"e1 should no longer be returned"
|
||||
);
|
||||
|
||||
world.clear_trackers();
|
||||
|
||||
assert!(world
|
||||
.query_mut::<(Mutated<A>, Entity)>()
|
||||
.map(|(_a, e)| e)
|
||||
.next()
|
||||
.is_none());
|
||||
assert!(get_filtered::<Mutated<A>>(&mut world).is_empty());
|
||||
|
||||
let e4 = world.spawn(());
|
||||
|
||||
world.insert_one(e4, A(0)).unwrap();
|
||||
assert!(get_mutated_a(&mut world).is_empty());
|
||||
assert!(get_filtered::<Mutated<A>>(&mut world).is_empty());
|
||||
assert_eq!(get_filtered::<Added<A>>(&mut world), vec![e4]);
|
||||
|
||||
world.insert_one(e4, A(1)).unwrap();
|
||||
assert_eq!(get_mutated_a(&mut world), vec![e4]);
|
||||
assert_eq!(get_filtered::<Mutated<A>>(&mut world), vec![e4]);
|
||||
|
||||
world.clear_trackers();
|
||||
|
||||
|
@ -961,19 +574,10 @@ mod tests {
|
|||
// non existing components even when changing archetype.
|
||||
world.insert(e4, (A(0), B(0))).unwrap();
|
||||
|
||||
let added_a = world.query::<(Added<A>, Entity)>().map(|(_, e)| e).next();
|
||||
assert!(added_a.is_none());
|
||||
|
||||
assert_eq!(get_mutated_a(&mut world), vec![e4]);
|
||||
|
||||
let added_b = world.query::<(Added<B>, Entity)>().map(|(_, e)| e).next();
|
||||
assert!(added_b.is_some());
|
||||
|
||||
let mutated_b = world
|
||||
.query_mut::<(Mutated<B>, Entity)>()
|
||||
.map(|(_, e)| e)
|
||||
.next();
|
||||
assert!(mutated_b.is_none());
|
||||
assert!(get_filtered::<Added<A>>(&mut world).is_empty());
|
||||
assert_eq!(get_filtered::<Mutated<A>>(&mut world), vec![e4]);
|
||||
assert_eq!(get_filtered::<Added<B>>(&mut world), vec![e4]);
|
||||
assert!(get_filtered::<Mutated<B>>(&mut world).is_empty());
|
||||
}
|
||||
|
||||
#[test]
|
||||
|
@ -992,8 +596,7 @@ mod tests {
|
|||
}
|
||||
|
||||
let a_b_mutated = world
|
||||
.query_mut::<(Mutated<A>, Mutated<B>, Entity)>()
|
||||
.map(|(_a, _b, e)| e)
|
||||
.query_filtered_mut::<Entity, (Mutated<A>, Mutated<B>)>()
|
||||
.collect::<Vec<Entity>>();
|
||||
assert_eq!(a_b_mutated, vec![e2]);
|
||||
}
|
||||
|
@ -1016,8 +619,7 @@ mod tests {
|
|||
}
|
||||
|
||||
let a_b_mutated = world
|
||||
.query_mut::<(Or<(Mutated<A>, Mutated<B>)>, Entity)>()
|
||||
.map(|((_a, _b), e)| e)
|
||||
.query_filtered_mut::<Entity, Or<(Mutated<A>, Mutated<B>)>>()
|
||||
.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_mutated, vec![e1, e2, e3]);
|
||||
|
@ -1030,8 +632,7 @@ mod tests {
|
|||
|
||||
fn get_changed(world: &World) -> Vec<Entity> {
|
||||
world
|
||||
.query::<(Changed<A>, Entity)>()
|
||||
.map(|(_a, e)| e)
|
||||
.query_filtered::<Entity, Changed<A>>()
|
||||
.collect::<Vec<Entity>>()
|
||||
};
|
||||
assert_eq!(get_changed(&world), vec![e1]);
|
||||
|
|
|
@ -15,8 +15,8 @@
|
|||
// modified by Bevy contributors
|
||||
|
||||
use crate::{
|
||||
alloc::vec::Vec, borrow::EntityRef, query::ReadOnlyFetch, BatchedIter, EntityReserver, Fetch,
|
||||
Mut, QueryIter, RefMut,
|
||||
alloc::vec::Vec, borrow::EntityRef, filter::EntityFilter, query::ReadOnlyFetch, BatchedIter,
|
||||
EntityReserver, Fetch, Mut, QueryFilter, QueryIter, RefMut,
|
||||
};
|
||||
use bevy_utils::{HashMap, HashSet};
|
||||
use core::{any::TypeId, fmt, mem, ptr};
|
||||
|
@ -250,7 +250,16 @@ impl World {
|
|||
/// assert!(entities.contains(&(b, 456, false)));
|
||||
/// ```
|
||||
#[inline]
|
||||
pub fn query<Q: Query>(&self) -> QueryIter<'_, Q>
|
||||
pub fn query<Q: Query>(&self) -> QueryIter<'_, Q, ()>
|
||||
where
|
||||
Q::Fetch: ReadOnlyFetch,
|
||||
{
|
||||
// SAFE: read-only access to world and read only query prevents mutable access
|
||||
unsafe { self.query_unchecked() }
|
||||
}
|
||||
|
||||
#[inline]
|
||||
pub fn query_filtered<Q: Query, F: QueryFilter>(&self) -> QueryIter<'_, Q, F>
|
||||
where
|
||||
Q::Fetch: ReadOnlyFetch,
|
||||
{
|
||||
|
@ -283,7 +292,13 @@ impl World {
|
|||
/// assert!(entities.contains(&(b, 456, false)));
|
||||
/// ```
|
||||
#[inline]
|
||||
pub fn query_mut<Q: Query>(&mut self) -> QueryIter<'_, Q> {
|
||||
pub fn query_mut<Q: Query>(&mut self) -> QueryIter<'_, Q, ()> {
|
||||
// SAFE: unique mutable access
|
||||
unsafe { self.query_unchecked() }
|
||||
}
|
||||
|
||||
#[inline]
|
||||
pub fn query_filtered_mut<Q: Query, F: QueryFilter>(&mut self) -> QueryIter<'_, Q, F> {
|
||||
// SAFE: unique mutable access
|
||||
unsafe { self.query_unchecked() }
|
||||
}
|
||||
|
@ -291,7 +306,19 @@ impl World {
|
|||
/// Like `query`, but instead of returning a single iterator it returns a "batched iterator",
|
||||
/// where each batch is `batch_size`. This is generally used for parallel iteration.
|
||||
#[inline]
|
||||
pub fn query_batched<Q: Query>(&self, batch_size: usize) -> BatchedIter<'_, Q>
|
||||
pub fn query_batched<Q: Query>(&self, batch_size: usize) -> BatchedIter<'_, Q, ()>
|
||||
where
|
||||
Q::Fetch: ReadOnlyFetch,
|
||||
{
|
||||
// SAFE: read-only access to world and read only query prevents mutable access
|
||||
unsafe { self.query_batched_unchecked(batch_size) }
|
||||
}
|
||||
|
||||
#[inline]
|
||||
pub fn query_batched_filtered<Q: Query, F: QueryFilter>(
|
||||
&self,
|
||||
batch_size: usize,
|
||||
) -> BatchedIter<'_, Q, F>
|
||||
where
|
||||
Q::Fetch: ReadOnlyFetch,
|
||||
{
|
||||
|
@ -302,7 +329,16 @@ impl World {
|
|||
/// Like `query`, but instead of returning a single iterator it returns a "batched iterator",
|
||||
/// where each batch is `batch_size`. This is generally used for parallel iteration.
|
||||
#[inline]
|
||||
pub fn query_batched_mut<Q: Query>(&mut self, batch_size: usize) -> BatchedIter<'_, Q> {
|
||||
pub fn query_batched_mut<Q: Query>(&mut self, batch_size: usize) -> BatchedIter<'_, Q, ()> {
|
||||
// SAFE: unique mutable access
|
||||
unsafe { self.query_batched_unchecked(batch_size) }
|
||||
}
|
||||
|
||||
#[inline]
|
||||
pub fn query_batched_filtered_mut<Q: Query, F: QueryFilter>(
|
||||
&mut self,
|
||||
batch_size: usize,
|
||||
) -> BatchedIter<'_, Q, F> {
|
||||
// SAFE: unique mutable access
|
||||
unsafe { self.query_batched_unchecked(batch_size) }
|
||||
}
|
||||
|
@ -321,7 +357,7 @@ impl World {
|
|||
/// This does not check for mutable query correctness. To be safe, make sure mutable queries
|
||||
/// have unique access to the components they query.
|
||||
#[inline]
|
||||
pub unsafe fn query_unchecked<Q: Query>(&self) -> QueryIter<'_, Q> {
|
||||
pub unsafe fn query_unchecked<Q: Query, F: QueryFilter>(&self) -> QueryIter<'_, Q, F> {
|
||||
QueryIter::new(&self.archetypes)
|
||||
}
|
||||
|
||||
|
@ -332,10 +368,10 @@ impl World {
|
|||
/// This does not check for mutable query correctness. To be safe, make sure mutable queries
|
||||
/// have unique access to the components they query.
|
||||
#[inline]
|
||||
pub unsafe fn query_batched_unchecked<Q: Query>(
|
||||
pub unsafe fn query_batched_unchecked<Q: Query, F: QueryFilter>(
|
||||
&self,
|
||||
batch_size: usize,
|
||||
) -> BatchedIter<'_, Q> {
|
||||
) -> BatchedIter<'_, Q, F> {
|
||||
BatchedIter::new(&self.archetypes, batch_size)
|
||||
}
|
||||
|
||||
|
@ -361,7 +397,19 @@ impl World {
|
|||
Q::Fetch: ReadOnlyFetch,
|
||||
{
|
||||
// SAFE: read-only access to world and read only query prevents mutable access
|
||||
unsafe { self.query_one_unchecked::<Q>(entity) }
|
||||
unsafe { self.query_one_unchecked::<Q, ()>(entity) }
|
||||
}
|
||||
|
||||
#[inline]
|
||||
pub fn query_one_filtered<Q: Query, F: QueryFilter>(
|
||||
&self,
|
||||
entity: Entity,
|
||||
) -> Result<<Q::Fetch as Fetch>::Item, NoSuchEntity>
|
||||
where
|
||||
Q::Fetch: ReadOnlyFetch,
|
||||
{
|
||||
// SAFE: read-only access to world and read only query prevents mutable access
|
||||
unsafe { self.query_one_unchecked::<Q, F>(entity) }
|
||||
}
|
||||
|
||||
/// Prepare a query against a single entity
|
||||
|
@ -384,7 +432,16 @@ impl World {
|
|||
entity: Entity,
|
||||
) -> Result<<Q::Fetch as Fetch>::Item, NoSuchEntity> {
|
||||
// SAFE: unique mutable access to world
|
||||
unsafe { self.query_one_unchecked::<Q>(entity) }
|
||||
unsafe { self.query_one_unchecked::<Q, ()>(entity) }
|
||||
}
|
||||
|
||||
#[inline]
|
||||
pub fn query_one_filtered_mut<Q: Query, F: QueryFilter>(
|
||||
&mut self,
|
||||
entity: Entity,
|
||||
) -> Result<<Q::Fetch as Fetch>::Item, NoSuchEntity> {
|
||||
// SAFE: unique mutable access to world
|
||||
unsafe { self.query_one_unchecked::<Q, F>(entity) }
|
||||
}
|
||||
|
||||
/// Prepare a query against a single entity, without checking the safety of mutable queries
|
||||
|
@ -395,15 +452,22 @@ impl World {
|
|||
/// This does not check for mutable query correctness. To be safe, make sure mutable queries
|
||||
/// have unique access to the components they query.
|
||||
#[inline]
|
||||
pub unsafe fn query_one_unchecked<Q: Query>(
|
||||
pub unsafe fn query_one_unchecked<Q: Query, F: QueryFilter>(
|
||||
&self,
|
||||
entity: Entity,
|
||||
) -> Result<<Q::Fetch as Fetch>::Item, NoSuchEntity> {
|
||||
let loc = self.entities.get(entity)?;
|
||||
<Q::Fetch as Fetch>::get(&self.archetypes[loc.archetype as usize], 0)
|
||||
.filter(|fetch| !fetch.should_skip(loc.index))
|
||||
.map(|fetch| fetch.fetch(loc.index))
|
||||
.ok_or(NoSuchEntity)
|
||||
let archetype = &self.archetypes[loc.archetype as usize];
|
||||
let matches_filter = F::get_entity_filter(archetype)
|
||||
.map(|entity_filter| entity_filter.matches_entity(loc.index))
|
||||
.unwrap_or(false);
|
||||
if matches_filter {
|
||||
<Q::Fetch as Fetch>::get(archetype, 0)
|
||||
.map(|fetch| fetch.fetch(loc.index))
|
||||
.ok_or(NoSuchEntity)
|
||||
} else {
|
||||
Err(NoSuchEntity)
|
||||
}
|
||||
}
|
||||
|
||||
/// Borrow the `T` component of `entity`
|
||||
|
|
|
@ -251,7 +251,6 @@ fn query_batched() {
|
|||
.flat_map(|x| x)
|
||||
.map(|e| e)
|
||||
.collect::<Vec<_>>();
|
||||
dbg!(&entities);
|
||||
assert_eq!(entities.len(), 3);
|
||||
assert!(entities.contains(&a));
|
||||
assert!(entities.contains(&b));
|
||||
|
@ -355,22 +354,22 @@ fn added_tracking() {
|
|||
let a = world.spawn((123,));
|
||||
|
||||
assert_eq!(world.query::<&i32>().count(), 1);
|
||||
assert_eq!(world.query::<Added<i32>>().count(), 1);
|
||||
assert_eq!(world.query_filtered::<(), Added<i32>>().count(), 1);
|
||||
assert_eq!(world.query_mut::<&i32>().count(), 1);
|
||||
assert_eq!(world.query_mut::<Added<i32>>().count(), 1);
|
||||
assert_eq!(world.query_filtered_mut::<(), Added<i32>>().count(), 1);
|
||||
assert!(world.query_one::<&i32>(a).is_ok());
|
||||
assert!(world.query_one::<Added<i32>>(a).is_ok());
|
||||
assert!(world.query_one_filtered::<(), Added<i32>>(a).is_ok());
|
||||
assert!(world.query_one_mut::<&i32>(a).is_ok());
|
||||
assert!(world.query_one_mut::<Added<i32>>(a).is_ok());
|
||||
assert!(world.query_one_filtered_mut::<(), Added<i32>>(a).is_ok());
|
||||
|
||||
world.clear_trackers();
|
||||
|
||||
assert_eq!(world.query::<&i32>().count(), 1);
|
||||
assert_eq!(world.query::<Added<i32>>().count(), 0);
|
||||
assert_eq!(world.query_filtered::<(), Added<i32>>().count(), 0);
|
||||
assert_eq!(world.query_mut::<&i32>().count(), 1);
|
||||
assert_eq!(world.query_mut::<Added<i32>>().count(), 0);
|
||||
assert_eq!(world.query_filtered_mut::<(), Added<i32>>().count(), 0);
|
||||
assert!(world.query_one_mut::<&i32>(a).is_ok());
|
||||
assert!(world.query_one_mut::<Added<i32>>(a).is_err());
|
||||
assert!(world.query_one_filtered_mut::<(), Added<i32>>(a).is_err());
|
||||
}
|
||||
|
||||
#[test]
|
||||
|
|
|
@ -220,7 +220,7 @@ mod tests {
|
|||
fn query_system_gets() {
|
||||
fn query_system(
|
||||
mut ran: ResMut<bool>,
|
||||
entity_query: Query<With<A, Entity>>,
|
||||
entity_query: Query<Entity, With<A>>,
|
||||
b_query: Query<&B>,
|
||||
a_c_query: Query<(&A, &C)>,
|
||||
d_query: Query<&D>,
|
||||
|
@ -281,9 +281,9 @@ mod tests {
|
|||
use crate::{Added, Changed, Mutated, Or};
|
||||
let query_system = move |mut ran: ResMut<bool>,
|
||||
set: QuerySet<(
|
||||
Query<Or<(Changed<A>, Changed<B>)>>,
|
||||
Query<Or<(Added<A>, Added<B>)>>,
|
||||
Query<Or<(Mutated<A>, Mutated<B>)>>,
|
||||
Query<(), Or<(Changed<A>, Changed<B>)>>,
|
||||
Query<(), Or<(Added<A>, Added<B>)>>,
|
||||
Query<(), Or<(Mutated<A>, Mutated<B>)>>,
|
||||
)>| {
|
||||
let changed = set.q0().iter().count();
|
||||
let added = set.q1().iter().count();
|
||||
|
|
|
@ -4,17 +4,17 @@ pub use query_set::*;
|
|||
|
||||
use bevy_hecs::{
|
||||
ArchetypeComponent, Batch, BatchedIter, Component, ComponentError, Entity, Fetch, Mut,
|
||||
Query as HecsQuery, QueryIter, ReadOnlyFetch, TypeAccess, World,
|
||||
Query as HecsQuery, QueryFilter, QueryIter, ReadOnlyFetch, TypeAccess, World,
|
||||
};
|
||||
use bevy_tasks::ParallelIterator;
|
||||
use std::marker::PhantomData;
|
||||
|
||||
/// Provides scoped access to a World according to a given [HecsQuery]
|
||||
#[derive(Debug)]
|
||||
pub struct Query<'a, Q: HecsQuery> {
|
||||
pub struct Query<'a, Q: HecsQuery, F: QueryFilter = ()> {
|
||||
pub(crate) world: &'a World,
|
||||
pub(crate) component_access: &'a TypeAccess<ArchetypeComponent>,
|
||||
_marker: PhantomData<Q>,
|
||||
_marker: PhantomData<(Q, F)>,
|
||||
}
|
||||
|
||||
/// An error that occurs when using a [Query]
|
||||
|
@ -26,7 +26,7 @@ pub enum QueryError {
|
|||
NoSuchEntity,
|
||||
}
|
||||
|
||||
impl<'a, Q: HecsQuery> Query<'a, Q> {
|
||||
impl<'a, Q: HecsQuery, F: QueryFilter> Query<'a, Q, F> {
|
||||
/// # Safety
|
||||
/// This will create a Query that could violate memory safety rules. Make sure that this is only called in
|
||||
/// ways that ensure the Queries have unique mutable access.
|
||||
|
@ -44,7 +44,7 @@ impl<'a, Q: HecsQuery> Query<'a, Q> {
|
|||
|
||||
/// Iterates over the query results. This can only be called for read-only queries
|
||||
#[inline]
|
||||
pub fn iter(&self) -> QueryIter<'_, Q>
|
||||
pub fn iter(&self) -> QueryIter<'_, Q, F>
|
||||
where
|
||||
Q::Fetch: ReadOnlyFetch,
|
||||
{
|
||||
|
@ -54,7 +54,7 @@ impl<'a, Q: HecsQuery> Query<'a, Q> {
|
|||
|
||||
/// Iterates over the query results
|
||||
#[inline]
|
||||
pub fn iter_mut(&mut self) -> QueryIter<'_, Q> {
|
||||
pub fn iter_mut(&mut self) -> QueryIter<'_, Q, F> {
|
||||
// SAFE: system runs without conflicts with other systems. same-system queries have runtime borrow checks when they conflict
|
||||
unsafe { self.world.query_unchecked() }
|
||||
}
|
||||
|
@ -63,13 +63,13 @@ impl<'a, Q: HecsQuery> Query<'a, Q> {
|
|||
/// # Safety
|
||||
/// This allows aliased mutability. You must make sure this call does not result in multiple mutable references to the same component
|
||||
#[inline]
|
||||
pub unsafe fn iter_unsafe(&self) -> QueryIter<'_, Q> {
|
||||
pub unsafe fn iter_unsafe(&self) -> QueryIter<'_, Q, F> {
|
||||
// SAFE: system runs without conflicts with other systems. same-system queries have runtime borrow checks when they conflict
|
||||
self.world.query_unchecked()
|
||||
}
|
||||
|
||||
#[inline]
|
||||
pub fn par_iter(&self, batch_size: usize) -> ParIter<'_, Q>
|
||||
pub fn par_iter(&self, batch_size: usize) -> ParIter<'_, Q, F>
|
||||
where
|
||||
Q::Fetch: ReadOnlyFetch,
|
||||
{
|
||||
|
@ -78,7 +78,7 @@ impl<'a, Q: HecsQuery> Query<'a, Q> {
|
|||
}
|
||||
|
||||
#[inline]
|
||||
pub fn par_iter_mut(&mut self, batch_size: usize) -> ParIter<'_, Q> {
|
||||
pub fn par_iter_mut(&mut self, batch_size: usize) -> ParIter<'_, Q, F> {
|
||||
// SAFE: system runs without conflicts with other systems. same-system queries have runtime borrow checks when they conflict
|
||||
unsafe { ParIter::new(self.world.query_batched_unchecked(batch_size)) }
|
||||
}
|
||||
|
@ -92,7 +92,7 @@ impl<'a, Q: HecsQuery> Query<'a, Q> {
|
|||
// SAFE: system runs without conflicts with other systems. same-system queries have runtime borrow checks when they conflict
|
||||
unsafe {
|
||||
self.world
|
||||
.query_one_unchecked::<Q>(entity)
|
||||
.query_one_unchecked::<Q, F>(entity)
|
||||
.map_err(|_err| QueryError::NoSuchEntity)
|
||||
}
|
||||
}
|
||||
|
@ -103,7 +103,7 @@ impl<'a, Q: HecsQuery> Query<'a, Q> {
|
|||
// SAFE: system runs without conflicts with other systems. same-system queries have runtime borrow checks when they conflict
|
||||
unsafe {
|
||||
self.world
|
||||
.query_one_unchecked::<Q>(entity)
|
||||
.query_one_unchecked::<Q, F>(entity)
|
||||
.map_err(|_err| QueryError::NoSuchEntity)
|
||||
}
|
||||
}
|
||||
|
@ -117,7 +117,7 @@ impl<'a, Q: HecsQuery> Query<'a, Q> {
|
|||
entity: Entity,
|
||||
) -> Result<<Q::Fetch as Fetch>::Item, QueryError> {
|
||||
self.world
|
||||
.query_one_unchecked::<Q>(entity)
|
||||
.query_one_unchecked::<Q, F>(entity)
|
||||
.map_err(|_err| QueryError::NoSuchEntity)
|
||||
}
|
||||
|
||||
|
@ -196,23 +196,23 @@ impl<'a, Q: HecsQuery> Query<'a, Q> {
|
|||
}
|
||||
|
||||
/// Parallel version of QueryIter
|
||||
pub struct ParIter<'w, Q: HecsQuery> {
|
||||
batched_iter: BatchedIter<'w, Q>,
|
||||
pub struct ParIter<'w, Q: HecsQuery, F: QueryFilter> {
|
||||
batched_iter: BatchedIter<'w, Q, F>,
|
||||
}
|
||||
|
||||
impl<'w, Q: HecsQuery> ParIter<'w, Q> {
|
||||
pub fn new(batched_iter: BatchedIter<'w, Q>) -> Self {
|
||||
impl<'w, Q: HecsQuery, F: QueryFilter> ParIter<'w, Q, F> {
|
||||
pub fn new(batched_iter: BatchedIter<'w, Q, F>) -> Self {
|
||||
Self { batched_iter }
|
||||
}
|
||||
}
|
||||
|
||||
unsafe impl<'w, Q: HecsQuery> Send for ParIter<'w, Q> {}
|
||||
unsafe impl<'w, Q: HecsQuery, F: QueryFilter> Send for ParIter<'w, Q, F> {}
|
||||
|
||||
impl<'w, Q: HecsQuery> ParallelIterator<Batch<'w, Q>> for ParIter<'w, Q> {
|
||||
impl<'w, Q: HecsQuery, F: QueryFilter> ParallelIterator<Batch<'w, Q, F>> for ParIter<'w, Q, F> {
|
||||
type Item = <Q::Fetch as Fetch<'w>>::Item;
|
||||
|
||||
#[inline]
|
||||
fn next_batch(&mut self) -> Option<Batch<'w, Q>> {
|
||||
fn next_batch(&mut self) -> Option<Batch<'w, Q, F>> {
|
||||
self.batched_iter.next()
|
||||
}
|
||||
}
|
||||
|
|
|
@ -1,6 +1,7 @@
|
|||
use crate::Query;
|
||||
use bevy_hecs::{
|
||||
impl_query_set, ArchetypeComponent, Fetch, Query as HecsQuery, QueryAccess, TypeAccess, World,
|
||||
impl_query_set, ArchetypeComponent, Fetch, Query as HecsQuery, QueryAccess, QueryFilter,
|
||||
TypeAccess, World,
|
||||
};
|
||||
|
||||
pub struct QuerySet<T: QueryTuple> {
|
||||
|
|
|
@ -1,10 +1,11 @@
|
|||
pub use bevy_hecs::SystemParam;
|
||||
|
||||
use crate::{
|
||||
ChangedRes, Commands, FromResources, Local, Query, QuerySet, QueryTuple, Res, ResMut, Resource,
|
||||
ResourceIndex, Resources, SystemState,
|
||||
ChangedRes, Commands, FromResources, Local, Query, QueryAccess, QuerySet, QueryTuple, Res,
|
||||
ResMut, Resource, ResourceIndex, Resources, SystemState,
|
||||
};
|
||||
pub use bevy_hecs::SystemParam;
|
||||
use bevy_hecs::{
|
||||
ArchetypeComponent, Fetch, Or, Query as HecsQuery, QueryFilter, TypeAccess, World,
|
||||
};
|
||||
use bevy_hecs::{ArchetypeComponent, Fetch, Or, Query as HecsQuery, TypeAccess, World};
|
||||
use parking_lot::Mutex;
|
||||
use std::{any::TypeId, sync::Arc};
|
||||
|
||||
|
@ -20,7 +21,7 @@ pub trait SystemParam: Sized {
|
|||
) -> Option<Self>;
|
||||
}
|
||||
|
||||
impl<'a, Q: HecsQuery> SystemParam for Query<'a, Q> {
|
||||
impl<'a, Q: HecsQuery, F: QueryFilter> SystemParam for Query<'a, Q, F> {
|
||||
#[inline]
|
||||
unsafe fn get_param(
|
||||
system_state: &mut SystemState,
|
||||
|
@ -39,9 +40,8 @@ impl<'a, Q: HecsQuery> SystemParam for Query<'a, Q> {
|
|||
system_state
|
||||
.query_archetype_component_accesses
|
||||
.push(TypeAccess::default());
|
||||
system_state
|
||||
.query_accesses
|
||||
.push(vec![<Q::Fetch as Fetch>::access()]);
|
||||
let access = QueryAccess::union(vec![Q::Fetch::access(), F::access()]);
|
||||
system_state.query_accesses.push(vec![access]);
|
||||
system_state
|
||||
.query_type_names
|
||||
.push(std::any::type_name::<Q>());
|
||||
|
|
|
@ -40,7 +40,7 @@ pub fn camera_system<T: CameraProjection + Component>(
|
|||
windows: Res<Windows>,
|
||||
mut queries: QuerySet<(
|
||||
Query<(Entity, &mut Camera, &mut T)>,
|
||||
Query<(Entity, Added<Camera>)>,
|
||||
Query<Entity, Added<Camera>>,
|
||||
)>,
|
||||
) {
|
||||
let mut changed_window_ids = Vec::new();
|
||||
|
@ -71,7 +71,7 @@ pub fn camera_system<T: CameraProjection + Component>(
|
|||
}
|
||||
|
||||
let mut added_cameras = vec![];
|
||||
for (entity, _camera) in &mut queries.q1().iter() {
|
||||
for entity in &mut queries.q1().iter() {
|
||||
added_cameras.push(entity);
|
||||
}
|
||||
for (entity, mut camera, mut camera_projection) in queries.q0_mut().iter_mut() {
|
||||
|
|
|
@ -26,7 +26,7 @@ impl VisibleEntities {
|
|||
pub fn visible_entities_system(
|
||||
mut camera_query: Query<(&Camera, &GlobalTransform, &mut VisibleEntities)>,
|
||||
draw_query: Query<(Entity, &Draw)>,
|
||||
draw_transform_query: Query<With<Draw, &GlobalTransform>>,
|
||||
draw_transform_query: Query<&GlobalTransform, With<Draw>>,
|
||||
) {
|
||||
for (camera, camera_global_transform, mut visible_entities) in camera_query.iter_mut() {
|
||||
visible_entities.value.clear();
|
||||
|
|
|
@ -5,7 +5,7 @@ use smallvec::SmallVec;
|
|||
|
||||
pub fn parent_update_system(
|
||||
commands: &mut Commands,
|
||||
removed_parent_query: Query<Without<Parent, (Entity, &PreviousParent)>>,
|
||||
removed_parent_query: Query<(Entity, &PreviousParent), Without<Parent>>,
|
||||
// TODO: ideally this only runs when the Parent component has changed
|
||||
mut changed_parent_query: Query<(Entity, &Parent, Option<&mut PreviousParent>)>,
|
||||
mut children_query: Query<&mut Children>,
|
||||
|
|
|
@ -3,13 +3,11 @@ use bevy_ecs::prelude::*;
|
|||
|
||||
pub fn transform_propagate_system(
|
||||
mut root_query: Query<
|
||||
Without<
|
||||
Parent,
|
||||
With<GlobalTransform, (Option<&Children>, &Transform, &mut GlobalTransform)>,
|
||||
>,
|
||||
(Option<&Children>, &Transform, &mut GlobalTransform),
|
||||
(Without<Parent>, With<GlobalTransform>),
|
||||
>,
|
||||
mut transform_query: Query<With<Parent, (&Transform, &mut GlobalTransform)>>,
|
||||
children_query: Query<With<Parent, With<GlobalTransform, Option<&Children>>>>,
|
||||
mut transform_query: Query<(&Transform, &mut GlobalTransform), With<Parent>>,
|
||||
children_query: Query<Option<&Children>, (With<Parent>, With<GlobalTransform>)>,
|
||||
) {
|
||||
for (children, transform, mut global_transform) in root_query.iter_mut() {
|
||||
*global_transform = GlobalTransform::from(*transform);
|
||||
|
@ -29,8 +27,8 @@ pub fn transform_propagate_system(
|
|||
|
||||
fn propagate_recursive(
|
||||
parent: &GlobalTransform,
|
||||
transform_query: &mut Query<With<Parent, (&Transform, &mut GlobalTransform)>>,
|
||||
children_query: &Query<With<Parent, With<GlobalTransform, Option<&Children>>>>,
|
||||
transform_query: &mut Query<(&Transform, &mut GlobalTransform), With<Parent>>,
|
||||
children_query: &Query<Option<&Children>, (With<Parent>, With<GlobalTransform>)>,
|
||||
entity: Entity,
|
||||
) {
|
||||
log::trace!("Updating Transform for {:?}", entity);
|
||||
|
|
|
@ -160,10 +160,13 @@ unsafe impl Sync for FlexSurface {}
|
|||
pub fn flex_node_system(
|
||||
windows: Res<Windows>,
|
||||
mut flex_surface: ResMut<FlexSurface>,
|
||||
root_node_query: Query<With<Node, Without<Parent, Entity>>>,
|
||||
node_query: Query<With<Node, (Entity, Changed<Style>, Option<&CalculatedSize>)>>,
|
||||
changed_size_query: Query<With<Node, (Entity, &Style, Changed<CalculatedSize>)>>,
|
||||
children_query: Query<With<Node, (Entity, Changed<Children>)>>,
|
||||
root_node_query: Query<Entity, (With<Node>, Without<Parent>)>,
|
||||
node_query: Query<(Entity, &Style, Option<&CalculatedSize>), (With<Node>, Changed<Style>)>,
|
||||
changed_size_query: Query<
|
||||
(Entity, &Style, &CalculatedSize),
|
||||
(With<Node>, Changed<CalculatedSize>),
|
||||
>,
|
||||
children_query: Query<(Entity, &Children), (With<Node>, Changed<Children>)>,
|
||||
mut node_transform_query: Query<(Entity, &mut Node, &mut Transform, Option<&Parent>)>,
|
||||
) {
|
||||
// update window root nodes
|
||||
|
|
|
@ -8,7 +8,7 @@ use bevy_transform::{
|
|||
pub const UI_Z_STEP: f32 = 0.001;
|
||||
|
||||
pub fn ui_z_system(
|
||||
root_node_query: Query<With<Node, Without<Parent, Entity>>>,
|
||||
root_node_query: Query<Entity, (With<Node>, Without<Parent>)>,
|
||||
mut node_query: Query<(Entity, &Node, &mut Transform)>,
|
||||
children_query: Query<&Children>,
|
||||
) {
|
||||
|
|
|
@ -32,7 +32,7 @@ pub fn text_system(
|
|||
mut font_atlas_sets: ResMut<Assets<FontAtlasSet>>,
|
||||
mut texture_atlases: ResMut<Assets<TextureAtlas>>,
|
||||
mut queries: QuerySet<(
|
||||
Query<(Entity, Changed<Text>, &mut CalculatedSize)>,
|
||||
Query<(Entity, &Text, &mut CalculatedSize), Changed<Text>>,
|
||||
Query<(&Text, &mut CalculatedSize)>,
|
||||
)>,
|
||||
) {
|
||||
|
|
|
@ -92,7 +92,7 @@ fn rotate(
|
|||
commands: &mut Commands,
|
||||
time: Res<Time>,
|
||||
mut parents_query: Query<(Entity, &mut Children, &Sprite)>,
|
||||
mut transform_query: Query<With<Sprite, &mut Transform>>,
|
||||
mut transform_query: Query<&mut Transform, With<Sprite>>,
|
||||
) {
|
||||
let angle = std::f32::consts::PI / 2.0;
|
||||
for (parent, mut children, _) in parents_query.iter_mut() {
|
||||
|
|
|
@ -65,7 +65,7 @@ fn load_scene_system(asset_server: Res<AssetServer>, mut scene_spawner: ResMut<S
|
|||
|
||||
// This system prints all ComponentA components in our world. Try making a change to a ComponentA in load_scene_example.scn.
|
||||
// You should immediately see the changes appear in the console.
|
||||
fn print_system(query: Query<(Entity, Changed<ComponentA>)>) {
|
||||
fn print_system(query: Query<(Entity, &ComponentA), Changed<ComponentA>>) {
|
||||
for (entity, component_a) in query.iter() {
|
||||
println!(" Entity({})", entity.id());
|
||||
println!(
|
||||
|
|
|
@ -29,12 +29,10 @@ impl FromResources for ButtonMaterials {
|
|||
|
||||
fn button_system(
|
||||
button_materials: Res<ButtonMaterials>,
|
||||
mut interaction_query: Query<(
|
||||
&Button,
|
||||
mut interaction_query: Query<
|
||||
(&Button, &Interaction, &mut Handle<ColorMaterial>, &Children),
|
||||
Mutated<Interaction>,
|
||||
&mut Handle<ColorMaterial>,
|
||||
&Children,
|
||||
)>,
|
||||
>,
|
||||
mut text_query: Query<&mut Text>,
|
||||
) {
|
||||
for (_button, interaction, mut material, children) in interaction_query.iter_mut() {
|
||||
|
|
Loading…
Reference in a new issue