add Flags<T> as a query to get flags of component (#1172)

add `Flags` as a query to get flags of component
This commit is contained in:
François 2020-12-31 23:29:08 +01:00 committed by GitHub
parent bbae58a1f3
commit d91117d6e7
No known key found for this signature in database
GPG key ID: 4AEE18F83AFDEB23
6 changed files with 174 additions and 4 deletions

View file

@ -198,6 +198,10 @@ path = "examples/diagnostics/custom_diagnostic.rs"
name = "log_diagnostics"
path = "examples/diagnostics/log_diagnostics.rs"
[[example]]
name = "change_detection"
path = "examples/ecs/change_detection.rs"
[[example]]
name = "event"
path = "examples/ecs/event.rs"

View file

@ -51,7 +51,7 @@ pub use entities::{Entity, EntityReserver, Location, NoSuchEntity};
pub use entity_builder::{BuiltEntity, EntityBuilder};
pub use entity_map::*;
pub use filter::{Added, Changed, EntityFilter, Mutated, Or, QueryFilter, With, Without};
pub use query::{Batch, BatchedIter, Mut, QueryIter, ReadOnlyFetch, WorldQuery};
pub use query::{Batch, BatchedIter, Flags, Mut, QueryIter, ReadOnlyFetch, WorldQuery};
pub use world::{ArchetypesGeneration, Component, ComponentError, SpawnBatchIter, World};
pub use world_builder::*;

View file

@ -130,6 +130,41 @@ impl<T: WorldQuery> WorldQuery for Option<T> {
type Fetch = TryFetch<T::Fetch>;
}
/// Flags on component `T` that happened since the start of the frame.
#[derive(Debug, Clone)]
pub struct Flags<T: Component> {
_marker: std::marker::PhantomData<T>,
with: bool,
added: bool,
mutated: bool,
}
impl<T: Component> Flags<T> {
/// Does the entity have this component
pub fn with(&self) -> bool {
self.with
}
/// Has this component been added since the start of the frame.
pub fn added(&self) -> bool {
self.added
}
/// Has this component been mutated since the start of the frame.
pub fn mutated(&self) -> bool {
self.mutated
}
/// Has this component been either mutated or added since the start of the frame.
pub fn changed(&self) -> bool {
self.added || self.mutated
}
}
impl<T: Component> WorldQuery for Flags<T> {
type Fetch = FlagsFetch<T>;
}
/// Unique borrow of an entity's component
pub struct Mut<'a, T: Component> {
pub(crate) value: &'a mut T,
@ -237,6 +272,51 @@ impl<'a, T: Fetch<'a>> Fetch<'a> for TryFetch<T> {
}
}
#[doc(hidden)]
pub struct FlagsFetch<T>(Option<NonNull<ComponentFlags>>, PhantomData<T>);
unsafe impl<T> ReadOnlyFetch for FlagsFetch<T> {}
impl<'a, T: Component> Fetch<'a> for FlagsFetch<T> {
type Item = Flags<T>;
const DANGLING: Self = Self(None, PhantomData::<T>);
#[inline]
fn access() -> QueryAccess {
QueryAccess::read::<T>()
}
unsafe fn get(archetype: &'a Archetype, offset: usize) -> Option<Self> {
Some(Self(
archetype
.get_type_state(std::any::TypeId::of::<T>())
.map(|type_state| {
NonNull::new_unchecked(type_state.component_flags().as_ptr().add(offset))
}),
PhantomData::<T>,
))
}
unsafe fn fetch(&self, n: usize) -> Self::Item {
if let Some(flags) = self.0.as_ref() {
let flags = *flags.as_ptr().add(n);
Self::Item {
_marker: PhantomData::<T>,
with: true,
added: flags.contains(ComponentFlags::ADDED),
mutated: flags.contains(ComponentFlags::MUTATED),
}
} else {
Self::Item {
_marker: PhantomData::<T>,
with: false,
added: false,
mutated: false,
}
}
}
}
struct ChunkInfo<Q: WorldQuery, F: QueryFilter> {
fetch: Q::Fetch,
filter: F::EntityFilter,
@ -466,7 +546,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::core::{Added, Changed, Component, Entity, Mutated, Or, QueryFilter, World};
use crate::core::{Added, Changed, Component, Entity, Flags, Mutated, Or, QueryFilter, World};
use std::{vec, vec::Vec};
use super::Mut;
@ -640,6 +720,38 @@ mod tests {
assert_eq!(get_changed(&world), vec![e1]);
}
#[test]
fn flags_query() {
let mut world = World::default();
let e1 = world.spawn((A(0), B(0)));
world.spawn((B(0),));
fn get_flags(world: &World) -> Vec<Flags<A>> {
world.query::<Flags<A>>().collect::<Vec<Flags<A>>>()
}
let flags = get_flags(&world);
assert!(flags[0].with());
assert!(flags[0].added());
assert!(!flags[0].mutated());
assert!(flags[0].changed());
assert!(!flags[1].with());
assert!(!flags[1].added());
assert!(!flags[1].mutated());
assert!(!flags[1].changed());
world.clear_trackers();
let flags = get_flags(&world);
assert!(flags[0].with());
assert!(!flags[0].added());
assert!(!flags[0].mutated());
assert!(!flags[0].changed());
*world.get_mut(e1).unwrap() = A(1);
let flags = get_flags(&world);
assert!(flags[0].with());
assert!(!flags[0].added());
assert!(flags[0].mutated());
assert!(flags[0].changed());
}
#[test]
fn exact_size_query() {
let mut world = World::default();

View file

@ -16,7 +16,7 @@ pub mod prelude {
resource::{ChangedRes, FromResources, Local, Res, ResMut, Resource, Resources},
schedule::{Schedule, State, StateStage, SystemStage},
system::{Commands, IntoSystem, Query, System},
Added, Bundle, Changed, Component, Entity, In, IntoChainSystem, Mut, Mutated, Or, QuerySet,
Ref, RefMut, With, Without, World,
Added, Bundle, Changed, Component, Entity, Flags, In, IntoChainSystem, Mut, Mutated, Or,
QuerySet, Ref, RefMut, With, Without, World,
};
}

View file

@ -114,6 +114,7 @@ Example | File | Description
Example | File | Description
--- | --- | ---
`change_detection` | [`ecs/change_detection.rs`](./ecs/change_detection.rs) | Change detection on components
`ecs_guide` | [`ecs/ecs_guide.rs`](./ecs/ecs_guide.rs) | Full guide to Bevy's ECS
`event` | [`ecs/event.rs`](./ecs/event.rs) | Illustrates event creation, activation, and reception
`hierarchy` | [`ecs/hierarchy.rs`](./ecs/hierarchy.rs) | Creates a hierarchy of parents and children entities

View file

@ -0,0 +1,53 @@
use bevy::prelude::*;
use rand::Rng;
// This example illustrates how to react to component change
fn main() {
App::build()
.add_plugins(DefaultPlugins)
.add_startup_system(setup.system())
.add_system(change_component.system())
.add_system(change_detection.system())
.add_system(flags_monitoring.system())
.run();
}
#[derive(Debug)]
struct MyComponent(f64);
fn setup(commands: &mut Commands) {
commands.spawn((MyComponent(0.),));
commands.spawn((Transform::default(),));
}
fn change_component(time: Res<Time>, mut query: Query<(Entity, &mut MyComponent)>) {
for (entity, mut component) in query.iter_mut() {
if rand::thread_rng().gen_bool(0.1) {
info!("changing component {:?}", entity);
component.0 = time.seconds_since_startup();
}
}
}
// There are query filters for `Changed<T>`, `Added<T>` and `Mutated<T>`
// Only entities matching the filters will be in the query
fn change_detection(query: Query<(Entity, &MyComponent), Changed<MyComponent>>) {
for (entity, component) in query.iter() {
info!("{:?} changed: {:?}", entity, component,);
}
}
// By looking at flags, the query is not filtered but the information is available
fn flags_monitoring(query: Query<(Entity, Option<&MyComponent>, Flags<MyComponent>)>) {
for (entity, component, flags) in query.iter() {
info!(
"{:?}: {:?} -> with: {:?} - added: {:?} - mutated: {:?} - changed: {:?}",
entity,
component,
flags.with(),
flags.added(),
flags.mutated(),
flags.changed()
);
}
}