mirror of
https://github.com/bevyengine/bevy
synced 2024-11-22 04:33:37 +00:00
[ecs] implement is_empty for queries (#2271)
## Problem - The `Query` struct does not provide an easy way to check if it is empty. - Specifically, users have to use `.iter().peekable()` or `.iter().next().is_none()` which is not very ergonomic. - Fixes: #2270 ## Solution - Implement an `is_empty` function for queries to more easily check if the query is empty.
This commit is contained in:
parent
a20dc36c8c
commit
19db1e402b
4 changed files with 99 additions and 0 deletions
|
@ -69,6 +69,62 @@ where
|
|||
current_index: 0,
|
||||
}
|
||||
}
|
||||
|
||||
/// Consumes `self` and returns true if there were no elements remaining in this iterator.
|
||||
#[inline(always)]
|
||||
pub(crate) fn none_remaining(mut self) -> bool {
|
||||
// NOTE: this mimics the behavior of `QueryIter::next()`, except that it
|
||||
// never gets a `Self::Item`.
|
||||
unsafe {
|
||||
if self.is_dense {
|
||||
loop {
|
||||
if self.current_index == self.current_len {
|
||||
let table_id = match self.table_id_iter.next() {
|
||||
Some(table_id) => table_id,
|
||||
None => return true,
|
||||
};
|
||||
let table = &self.tables[*table_id];
|
||||
self.filter.set_table(&self.query_state.filter_state, table);
|
||||
self.current_len = table.len();
|
||||
self.current_index = 0;
|
||||
continue;
|
||||
}
|
||||
|
||||
if !self.filter.table_filter_fetch(self.current_index) {
|
||||
self.current_index += 1;
|
||||
continue;
|
||||
}
|
||||
|
||||
return false;
|
||||
}
|
||||
} else {
|
||||
loop {
|
||||
if self.current_index == self.current_len {
|
||||
let archetype_id = match self.archetype_id_iter.next() {
|
||||
Some(archetype_id) => archetype_id,
|
||||
None => return true,
|
||||
};
|
||||
let archetype = &self.archetypes[*archetype_id];
|
||||
self.filter.set_archetype(
|
||||
&self.query_state.filter_state,
|
||||
archetype,
|
||||
self.tables,
|
||||
);
|
||||
self.current_len = archetype.len();
|
||||
self.current_index = 0;
|
||||
continue;
|
||||
}
|
||||
|
||||
if !self.filter.archetype_filter_fetch(self.current_index) {
|
||||
self.current_index += 1;
|
||||
continue;
|
||||
}
|
||||
|
||||
return false;
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
impl<'w, 's, Q: WorldQuery, F: WorldQuery> Iterator for QueryIter<'w, 's, Q, F>
|
||||
|
|
|
@ -68,6 +68,16 @@ where
|
|||
state
|
||||
}
|
||||
|
||||
#[inline]
|
||||
pub fn is_empty(&self, world: &World, last_change_tick: u32, change_tick: u32) -> bool {
|
||||
// SAFE: the iterator is instantly consumed via `none_remaining` and the implementation of
|
||||
// `QueryIter::none_remaining` never creates any references to the `<Q::Fetch as Fetch<'w>>::Item`.
|
||||
unsafe {
|
||||
self.iter_unchecked_manual(world, last_change_tick, change_tick)
|
||||
.none_remaining()
|
||||
}
|
||||
}
|
||||
|
||||
pub fn validate_world_and_update_archetypes(&mut self, world: &World) {
|
||||
if world.id() != self.world_id {
|
||||
panic!("Attempted to use {} with a mismatched World. QueryStates can only be used with the World they were created from.",
|
||||
|
|
|
@ -438,6 +438,30 @@ mod tests {
|
|||
assert_eq!(conflicts, vec![b_id, d_id]);
|
||||
}
|
||||
|
||||
#[test]
|
||||
fn query_is_empty() {
|
||||
fn without_filter(not_empty: Query<&A>, empty: Query<&B>) {
|
||||
assert!(!not_empty.is_empty());
|
||||
assert!(empty.is_empty());
|
||||
}
|
||||
|
||||
fn with_filter(not_empty: Query<&A, With<C>>, empty: Query<&A, With<D>>) {
|
||||
assert!(!not_empty.is_empty());
|
||||
assert!(empty.is_empty());
|
||||
}
|
||||
|
||||
let mut world = World::default();
|
||||
world.spawn().insert(A).insert(C);
|
||||
|
||||
let mut without_filter = without_filter.system();
|
||||
without_filter.initialize(&mut world);
|
||||
without_filter.run((), &mut world);
|
||||
|
||||
let mut with_filter = with_filter.system();
|
||||
with_filter.initialize(&mut world);
|
||||
with_filter.run((), &mut world);
|
||||
}
|
||||
|
||||
#[test]
|
||||
#[allow(clippy::too_many_arguments)]
|
||||
fn can_have_16_parameters() {
|
||||
|
|
|
@ -543,6 +543,15 @@ where
|
|||
>())),
|
||||
}
|
||||
}
|
||||
|
||||
/// Returns true if this query contains no elements.
|
||||
#[inline]
|
||||
pub fn is_empty(&self) -> bool {
|
||||
// TODO: This code can be replaced with `self.iter().next().is_none()` if/when
|
||||
// we sort out how to convert "write" queries to "read" queries.
|
||||
self.state
|
||||
.is_empty(self.world, self.last_change_tick, self.change_tick)
|
||||
}
|
||||
}
|
||||
|
||||
/// An error that occurs when retrieving a specific [`Entity`]'s component from a [`Query`]
|
||||
|
|
Loading…
Reference in a new issue