From 5b0f232bf7b19401aa7167a8d7be4e10f9c02cdb Mon Sep 17 00:00:00 2001 From: Victoron <59878206+Victoronz@users.noreply.github.com> Date: Sat, 20 Jul 2024 20:47:04 +0200 Subject: [PATCH 1/4] implement randomize for QueryIter --- crates/bevy_ecs/src/query/iter.rs | 34 +++++++++++++++++++++++++++++++ 1 file changed, 34 insertions(+) diff --git a/crates/bevy_ecs/src/query/iter.rs b/crates/bevy_ecs/src/query/iter.rs index 05955d5b37854..a0b58677276d8 100644 --- a/crates/bevy_ecs/src/query/iter.rs +++ b/crates/bevy_ecs/src/query/iter.rs @@ -1,3 +1,5 @@ +use bevy_utils::RandomState; + use crate::{ archetype::{Archetype, ArchetypeEntity, Archetypes}, component::Tick, @@ -10,6 +12,7 @@ use std::{ borrow::Borrow, cmp::Ordering, fmt::{self, Debug, Formatter}, + hash::{BuildHasher, Hash, Hasher}, iter::FusedIterator, mem::MaybeUninit, ops::Range, @@ -167,6 +170,32 @@ impl<'w, 's, D: QueryData, F: QueryFilter> QueryIter<'w, 's, D, F> { accum } + /// Randomizes the order of all query items within a new iterator. + /// + /// This method produces a new order with each call. + /// + /// # Panics + /// + /// This will panic if `next` has been called on `QueryIter` before, unless the underlying `Query` is empty. + /// + pub fn randomize( + self, + ) -> QueryRandomIter< + 'w, + 's, + D, + F, + impl ExactSizeIterator + DoubleEndedIterator + FusedIterator + 'w, + > { + // FIXME: This uses AHash, it could be made faster with `EntityHash`. + // However, `EntityHasher` currently does not have a `RandomState` equivalent. + let mut hasher = RandomState::new().build_hasher(); + self.sort_by_cached_key::(move |e| { + e.hash(&mut hasher); + hasher.finish() + }) + } + /// Sorts all query items into a new iterator, using the query lens as a key. /// /// This sort is stable (i.e., does not reorder equal elements). @@ -1071,6 +1100,11 @@ impl<'w, 's, D: QueryData, F: QueryFilter, I: Iterator> Debug } } +/// An [`Iterator`] over randomized query results of a [`Query`](crate::system::Query). +/// +/// This type is created by the [`QueryIter::randomize`] method. +type QueryRandomIter<'w, 's, D, F, I> = QuerySortedIter<'w, 's, D, F, I>; + /// An [`Iterator`] over the query items generated from an iterator of [`Entity`]s. /// /// Items are returned in the order of the provided iterator. From 4b67193ff3704d1ee7cbedfd7c5910eb9038385c Mon Sep 17 00:00:00 2001 From: Victoron <59878206+Victoronz@users.noreply.github.com> Date: Sat, 20 Jul 2024 21:08:23 +0200 Subject: [PATCH 2/4] add an example --- crates/bevy_ecs/src/query/iter.rs | 14 ++++++++++++++ 1 file changed, 14 insertions(+) diff --git a/crates/bevy_ecs/src/query/iter.rs b/crates/bevy_ecs/src/query/iter.rs index a0b58677276d8..cfd0ad582448f 100644 --- a/crates/bevy_ecs/src/query/iter.rs +++ b/crates/bevy_ecs/src/query/iter.rs @@ -178,6 +178,20 @@ impl<'w, 's, D: QueryData, F: QueryFilter> QueryIter<'w, 's, D, F> { /// /// This will panic if `next` has been called on `QueryIter` before, unless the underlying `Query` is empty. /// + /// # Example + /// + /// ``` + /// # use bevy_ecs::prelude::*; + /// # let mut world = World::new(); + /// # fn system(query: Query<()>) { + /// // Fetch random query item! + /// let random_item = query.iter().randomize().take(1); + /// # } + /// # + /// # let mut schedule = Schedule::default(); + /// # schedule.add_systems(system); + /// # schedule.run(&mut world); + /// ``` pub fn randomize( self, ) -> QueryRandomIter< From 7c28f40b3d950bbce044a9b315194098776d4065 Mon Sep 17 00:00:00 2001 From: Victoron <59878206+Victoronz@users.noreply.github.com> Date: Sat, 20 Jul 2024 21:25:26 +0200 Subject: [PATCH 3/4] improve example --- crates/bevy_ecs/src/query/iter.rs | 13 ++++++++++--- 1 file changed, 10 insertions(+), 3 deletions(-) diff --git a/crates/bevy_ecs/src/query/iter.rs b/crates/bevy_ecs/src/query/iter.rs index cfd0ad582448f..58a4133ccaf92 100644 --- a/crates/bevy_ecs/src/query/iter.rs +++ b/crates/bevy_ecs/src/query/iter.rs @@ -179,13 +179,20 @@ impl<'w, 's, D: QueryData, F: QueryFilter> QueryIter<'w, 's, D, F> { /// This will panic if `next` has been called on `QueryIter` before, unless the underlying `Query` is empty. /// /// # Example - /// + /// /// ``` /// # use bevy_ecs::prelude::*; + /// # + /// # #[derive(Component)] + /// # pub struct Nothing; + /// # /// # let mut world = World::new(); + /// # world.spawn_batch((0..100).map(|_| Nothing)); + /// # /// # fn system(query: Query<()>) { - /// // Fetch random query item! - /// let random_item = query.iter().randomize().take(1); + /// # let n = 7; + /// // Fetch n random query items! + /// let random_items = query.iter().randomize().take(n); /// # } /// # /// # let mut schedule = Schedule::default(); From e1fc773a372d0046e5191df26b7d471d422db8a9 Mon Sep 17 00:00:00 2001 From: Victoron <59878206+Victoronz@users.noreply.github.com> Date: Sat, 20 Jul 2024 21:35:18 +0200 Subject: [PATCH 4/4] set QueryRandomIter as pub --- crates/bevy_ecs/src/query/iter.rs | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/crates/bevy_ecs/src/query/iter.rs b/crates/bevy_ecs/src/query/iter.rs index 58a4133ccaf92..4c384f3616fc9 100644 --- a/crates/bevy_ecs/src/query/iter.rs +++ b/crates/bevy_ecs/src/query/iter.rs @@ -1124,7 +1124,7 @@ impl<'w, 's, D: QueryData, F: QueryFilter, I: Iterator> Debug /// An [`Iterator`] over randomized query results of a [`Query`](crate::system::Query). /// /// This type is created by the [`QueryIter::randomize`] method. -type QueryRandomIter<'w, 's, D, F, I> = QuerySortedIter<'w, 's, D, F, I>; +pub type QueryRandomIter<'w, 's, D, F, I> = QuerySortedIter<'w, 's, D, F, I>; /// An [`Iterator`] over the query items generated from an iterator of [`Entity`]s. ///