mirror of
https://github.com/bevyengine/bevy
synced 2024-11-10 07:04:33 +00:00
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:
parent
bbae58a1f3
commit
d91117d6e7
6 changed files with 174 additions and 4 deletions
|
@ -198,6 +198,10 @@ path = "examples/diagnostics/custom_diagnostic.rs"
|
||||||
name = "log_diagnostics"
|
name = "log_diagnostics"
|
||||||
path = "examples/diagnostics/log_diagnostics.rs"
|
path = "examples/diagnostics/log_diagnostics.rs"
|
||||||
|
|
||||||
|
[[example]]
|
||||||
|
name = "change_detection"
|
||||||
|
path = "examples/ecs/change_detection.rs"
|
||||||
|
|
||||||
[[example]]
|
[[example]]
|
||||||
name = "event"
|
name = "event"
|
||||||
path = "examples/ecs/event.rs"
|
path = "examples/ecs/event.rs"
|
||||||
|
|
|
@ -51,7 +51,7 @@ pub use entities::{Entity, EntityReserver, Location, NoSuchEntity};
|
||||||
pub use entity_builder::{BuiltEntity, EntityBuilder};
|
pub use entity_builder::{BuiltEntity, EntityBuilder};
|
||||||
pub use entity_map::*;
|
pub use entity_map::*;
|
||||||
pub use filter::{Added, Changed, EntityFilter, Mutated, Or, QueryFilter, With, Without};
|
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::{ArchetypesGeneration, Component, ComponentError, SpawnBatchIter, World};
|
||||||
pub use world_builder::*;
|
pub use world_builder::*;
|
||||||
|
|
||||||
|
|
|
@ -130,6 +130,41 @@ impl<T: WorldQuery> WorldQuery for Option<T> {
|
||||||
type Fetch = TryFetch<T::Fetch>;
|
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
|
/// Unique borrow of an entity's component
|
||||||
pub struct Mut<'a, T: Component> {
|
pub struct Mut<'a, T: Component> {
|
||||||
pub(crate) value: &'a mut T,
|
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> {
|
struct ChunkInfo<Q: WorldQuery, F: QueryFilter> {
|
||||||
fetch: Q::Fetch,
|
fetch: Q::Fetch,
|
||||||
filter: F::EntityFilter,
|
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)]
|
#[cfg(test)]
|
||||||
mod tests {
|
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 std::{vec, vec::Vec};
|
||||||
|
|
||||||
use super::Mut;
|
use super::Mut;
|
||||||
|
@ -640,6 +720,38 @@ mod tests {
|
||||||
assert_eq!(get_changed(&world), vec![e1]);
|
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]
|
#[test]
|
||||||
fn exact_size_query() {
|
fn exact_size_query() {
|
||||||
let mut world = World::default();
|
let mut world = World::default();
|
||||||
|
|
|
@ -16,7 +16,7 @@ pub mod prelude {
|
||||||
resource::{ChangedRes, FromResources, Local, Res, ResMut, Resource, Resources},
|
resource::{ChangedRes, FromResources, Local, Res, ResMut, Resource, Resources},
|
||||||
schedule::{Schedule, State, StateStage, SystemStage},
|
schedule::{Schedule, State, StateStage, SystemStage},
|
||||||
system::{Commands, IntoSystem, Query, System},
|
system::{Commands, IntoSystem, Query, System},
|
||||||
Added, Bundle, Changed, Component, Entity, In, IntoChainSystem, Mut, Mutated, Or, QuerySet,
|
Added, Bundle, Changed, Component, Entity, Flags, In, IntoChainSystem, Mut, Mutated, Or,
|
||||||
Ref, RefMut, With, Without, World,
|
QuerySet, Ref, RefMut, With, Without, World,
|
||||||
};
|
};
|
||||||
}
|
}
|
||||||
|
|
|
@ -114,6 +114,7 @@ Example | File | Description
|
||||||
|
|
||||||
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
|
`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
|
`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
|
`hierarchy` | [`ecs/hierarchy.rs`](./ecs/hierarchy.rs) | Creates a hierarchy of parents and children entities
|
||||||
|
|
53
examples/ecs/change_detection.rs
Normal file
53
examples/ecs/change_detection.rs
Normal 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()
|
||||||
|
);
|
||||||
|
}
|
||||||
|
}
|
Loading…
Reference in a new issue