Add VisitEntities
for generic and reflectable Entity iteration (#15425)
# Objective - Provide a generic and _reflectable_ way to iterate over contained entities ## Solution Adds two new traits: * `VisitEntities`: Reflectable iteration, accepts a closure rather than producing an iterator. Implemented by default for `IntoIterator` implementing types. A proc macro is also provided. * A `Mut` variant of the above. Its derive macro uses the same field attribute to avoid repetition. ## Testing Added a test for `VisitEntities` that also transitively tests its derive macro as well as the default `MapEntities` impl.
This commit is contained in:
parent
40c26f80aa
commit
f97eba2082
@ -28,7 +28,10 @@ use bevy_app::{App, Plugin, PostUpdate};
|
||||
use bevy_asset::{Asset, AssetApp, Assets, Handle};
|
||||
use bevy_core::Name;
|
||||
use bevy_ecs::{
|
||||
entity::MapEntities, prelude::*, reflect::ReflectMapEntities, world::EntityMutExcept,
|
||||
entity::{VisitEntities, VisitEntitiesMut},
|
||||
prelude::*,
|
||||
reflect::{ReflectMapEntities, ReflectVisitEntities, ReflectVisitEntitiesMut},
|
||||
world::EntityMutExcept,
|
||||
};
|
||||
use bevy_math::FloatExt;
|
||||
use bevy_reflect::{
|
||||
@ -527,12 +530,13 @@ impl Hash for AnimationTargetId {
|
||||
/// Note that each entity can only be animated by one animation player at a
|
||||
/// time. However, you can change [`AnimationTarget`]'s `player` property at
|
||||
/// runtime to change which player is responsible for animating the entity.
|
||||
#[derive(Clone, Copy, Component, Reflect)]
|
||||
#[reflect(Component, MapEntities)]
|
||||
#[derive(Clone, Copy, Component, Reflect, VisitEntities, VisitEntitiesMut)]
|
||||
#[reflect(Component, MapEntities, VisitEntities, VisitEntitiesMut)]
|
||||
pub struct AnimationTarget {
|
||||
/// The ID of this animation target.
|
||||
///
|
||||
/// Typically, this is derived from the path.
|
||||
#[visit_entities(ignore)]
|
||||
pub id: AnimationTargetId,
|
||||
|
||||
/// The entity containing the [`AnimationPlayer`].
|
||||
@ -1298,12 +1302,6 @@ impl From<&Name> for AnimationTargetId {
|
||||
}
|
||||
}
|
||||
|
||||
impl MapEntities for AnimationTarget {
|
||||
fn map_entities<M: EntityMapper>(&mut self, entity_mapper: &mut M) {
|
||||
self.player = entity_mapper.map_entity(self.player);
|
||||
}
|
||||
}
|
||||
|
||||
impl AnimationGraphEvaluator {
|
||||
// Starts a new depth-first search.
|
||||
fn reset(&mut self, root: AnimationNodeIndex, node_count: usize) {
|
||||
|
@ -14,6 +14,7 @@ use crate::{query_data::derive_query_data_impl, query_filter::derive_query_filte
|
||||
use bevy_macro_utils::{derive_label, ensure_no_collision, get_struct_fields, BevyManifest};
|
||||
use proc_macro::TokenStream;
|
||||
use proc_macro2::Span;
|
||||
use proc_macro2::TokenStream as TokenStream2;
|
||||
use quote::{format_ident, quote};
|
||||
use syn::{
|
||||
parse_macro_input, parse_quote, punctuated::Punctuated, spanned::Spanned, token::Comma,
|
||||
@ -180,6 +181,110 @@ pub fn derive_bundle(input: TokenStream) -> TokenStream {
|
||||
})
|
||||
}
|
||||
|
||||
fn derive_visit_entities_base(
|
||||
input: TokenStream,
|
||||
trait_name: TokenStream2,
|
||||
gen_methods: impl FnOnce(Vec<TokenStream2>) -> TokenStream2,
|
||||
) -> TokenStream {
|
||||
let ast = parse_macro_input!(input as DeriveInput);
|
||||
let ecs_path = bevy_ecs_path();
|
||||
|
||||
let named_fields = match get_struct_fields(&ast.data) {
|
||||
Ok(fields) => fields,
|
||||
Err(e) => return e.into_compile_error().into(),
|
||||
};
|
||||
|
||||
let field = named_fields
|
||||
.iter()
|
||||
.filter_map(|field| {
|
||||
if let Some(attr) = field
|
||||
.attrs
|
||||
.iter()
|
||||
.find(|a| a.path().is_ident("visit_entities"))
|
||||
{
|
||||
let ignore = attr.parse_nested_meta(|meta| {
|
||||
if meta.path.is_ident("ignore") {
|
||||
Ok(())
|
||||
} else {
|
||||
Err(meta.error("Invalid visit_entities attribute. Use `ignore`"))
|
||||
}
|
||||
});
|
||||
return match ignore {
|
||||
Ok(()) => None,
|
||||
Err(e) => Some(Err(e)),
|
||||
};
|
||||
}
|
||||
Some(Ok(field))
|
||||
})
|
||||
.map(|res| res.map(|field| field.ident.as_ref()))
|
||||
.collect::<Result<Vec<_>, _>>();
|
||||
|
||||
let field = match field {
|
||||
Ok(field) => field,
|
||||
Err(e) => return e.into_compile_error().into(),
|
||||
};
|
||||
|
||||
if field.is_empty() {
|
||||
return syn::Error::new(
|
||||
ast.span(),
|
||||
format!("Invalid `{}` type: at least one field", trait_name),
|
||||
)
|
||||
.into_compile_error()
|
||||
.into();
|
||||
}
|
||||
|
||||
let field_access = field
|
||||
.iter()
|
||||
.enumerate()
|
||||
.map(|(n, f)| {
|
||||
if let Some(ident) = f {
|
||||
quote! {
|
||||
self.#ident
|
||||
}
|
||||
} else {
|
||||
let idx = Index::from(n);
|
||||
quote! {
|
||||
self.#idx
|
||||
}
|
||||
}
|
||||
})
|
||||
.collect::<Vec<_>>();
|
||||
|
||||
let methods = gen_methods(field_access);
|
||||
|
||||
let generics = ast.generics;
|
||||
let (impl_generics, ty_generics, _) = generics.split_for_impl();
|
||||
let struct_name = &ast.ident;
|
||||
|
||||
TokenStream::from(quote! {
|
||||
impl #impl_generics #ecs_path::entity:: #trait_name for #struct_name #ty_generics {
|
||||
#methods
|
||||
}
|
||||
})
|
||||
}
|
||||
|
||||
#[proc_macro_derive(VisitEntitiesMut, attributes(visit_entities))]
|
||||
pub fn derive_visit_entities_mut(input: TokenStream) -> TokenStream {
|
||||
derive_visit_entities_base(input, quote! { VisitEntitiesMut }, |field| {
|
||||
quote! {
|
||||
fn visit_entities_mut<F: FnMut(&mut Entity)>(&mut self, mut f: F) {
|
||||
#(#field.visit_entities_mut(&mut f);)*
|
||||
}
|
||||
}
|
||||
})
|
||||
}
|
||||
|
||||
#[proc_macro_derive(VisitEntities, attributes(visit_entities))]
|
||||
pub fn derive_visit_entities(input: TokenStream) -> TokenStream {
|
||||
derive_visit_entities_base(input, quote! { VisitEntities }, |field| {
|
||||
quote! {
|
||||
fn visit_entities<F: FnMut(Entity)>(&self, mut f: F) {
|
||||
#(#field.visit_entities(&mut f);)*
|
||||
}
|
||||
}
|
||||
})
|
||||
}
|
||||
|
||||
fn get_idents(fmt_string: fn(usize) -> String, count: usize) -> Vec<Ident> {
|
||||
(0..count)
|
||||
.map(|i| Ident::new(&fmt_string(i), Span::call_site()))
|
||||
|
@ -4,7 +4,7 @@ use crate::{
|
||||
world::World,
|
||||
};
|
||||
|
||||
use super::EntityHashMap;
|
||||
use super::{EntityHashMap, VisitEntitiesMut};
|
||||
|
||||
/// Operation to map all contained [`Entity`] fields in a type to new values.
|
||||
///
|
||||
@ -45,6 +45,14 @@ pub trait MapEntities {
|
||||
fn map_entities<M: EntityMapper>(&mut self, entity_mapper: &mut M);
|
||||
}
|
||||
|
||||
impl<T: VisitEntitiesMut> MapEntities for T {
|
||||
fn map_entities<M: EntityMapper>(&mut self, entity_mapper: &mut M) {
|
||||
self.visit_entities_mut(|entity| {
|
||||
*entity = entity_mapper.map_entity(*entity);
|
||||
});
|
||||
}
|
||||
}
|
||||
|
||||
/// An implementor of this trait knows how to map an [`Entity`] into another [`Entity`].
|
||||
///
|
||||
/// Usually this is done by using an [`EntityHashMap<Entity>`] to map source entities
|
||||
@ -122,6 +130,16 @@ impl<T: EntityMapper> DynEntityMapper for T {
|
||||
}
|
||||
}
|
||||
|
||||
impl<'a> EntityMapper for &'a mut dyn DynEntityMapper {
|
||||
fn map_entity(&mut self, entity: Entity) -> Entity {
|
||||
(*self).dyn_map_entity(entity)
|
||||
}
|
||||
|
||||
fn mappings(&self) -> impl Iterator<Item = (Entity, Entity)> {
|
||||
(*self).dyn_mappings().into_iter()
|
||||
}
|
||||
}
|
||||
|
||||
impl EntityMapper for SceneEntityMapper<'_> {
|
||||
/// Returns the corresponding mapped entity or reserves a new dead entity ID in the current world if it is absent.
|
||||
fn map_entity(&mut self, entity: Entity) -> Entity {
|
||||
@ -152,8 +170,7 @@ impl EntityMapper for SceneEntityMapper<'_> {
|
||||
/// world. These newly allocated references are guaranteed to never point to any living entity in that world.
|
||||
///
|
||||
/// References are allocated by returning increasing generations starting from an internally initialized base
|
||||
/// [`Entity`]. After it is finished being used by [`MapEntities`] implementations, this entity is despawned and the
|
||||
/// requisite number of generations reserved.
|
||||
/// [`Entity`]. After it is finished being used, this entity is despawned and the requisite number of generations reserved.
|
||||
pub struct SceneEntityMapper<'m> {
|
||||
/// A mapping from one set of entities to another.
|
||||
///
|
||||
|
@ -36,11 +36,13 @@
|
||||
//! [`EntityWorldMut::insert`]: crate::world::EntityWorldMut::insert
|
||||
//! [`EntityWorldMut::remove`]: crate::world::EntityWorldMut::remove
|
||||
mod map_entities;
|
||||
mod visit_entities;
|
||||
#[cfg(feature = "bevy_reflect")]
|
||||
use bevy_reflect::Reflect;
|
||||
#[cfg(all(feature = "bevy_reflect", feature = "serialize"))]
|
||||
use bevy_reflect::{ReflectDeserialize, ReflectSerialize};
|
||||
pub use map_entities::*;
|
||||
pub use visit_entities::*;
|
||||
|
||||
mod hash;
|
||||
pub use hash::*;
|
||||
|
150
crates/bevy_ecs/src/entity/visit_entities.rs
Normal file
150
crates/bevy_ecs/src/entity/visit_entities.rs
Normal file
@ -0,0 +1,150 @@
|
||||
pub use bevy_ecs_macros::{VisitEntities, VisitEntitiesMut};
|
||||
|
||||
use crate::entity::Entity;
|
||||
|
||||
/// Apply an operation to all entities in a container.
|
||||
///
|
||||
/// This is implemented by default for types that implement [`IntoIterator`].
|
||||
///
|
||||
/// It may be useful to implement directly for types that can't produce an
|
||||
/// iterator for lifetime reasons, such as those involving internal mutexes.
|
||||
pub trait VisitEntities {
|
||||
/// Apply an operation to all contained entities.
|
||||
fn visit_entities<F: FnMut(Entity)>(&self, f: F);
|
||||
}
|
||||
|
||||
impl<T> VisitEntities for T
|
||||
where
|
||||
for<'a> &'a T: IntoIterator<Item = &'a Entity>,
|
||||
{
|
||||
fn visit_entities<F: FnMut(Entity)>(&self, f: F) {
|
||||
self.into_iter().copied().for_each(f);
|
||||
}
|
||||
}
|
||||
|
||||
impl VisitEntities for Entity {
|
||||
fn visit_entities<F: FnMut(Entity)>(&self, mut f: F) {
|
||||
f(*self);
|
||||
}
|
||||
}
|
||||
|
||||
/// Apply an operation to mutable references to all entities in a container.
|
||||
///
|
||||
/// This is implemented by default for types that implement [`IntoIterator`].
|
||||
///
|
||||
/// It may be useful to implement directly for types that can't produce an
|
||||
/// iterator for lifetime reasons, such as those involving internal mutexes.
|
||||
pub trait VisitEntitiesMut: VisitEntities {
|
||||
/// Apply an operation to mutable references to all contained entities.
|
||||
fn visit_entities_mut<F: FnMut(&mut Entity)>(&mut self, f: F);
|
||||
}
|
||||
|
||||
impl<T: VisitEntities> VisitEntitiesMut for T
|
||||
where
|
||||
for<'a> &'a mut T: IntoIterator<Item = &'a mut Entity>,
|
||||
{
|
||||
fn visit_entities_mut<F: FnMut(&mut Entity)>(&mut self, f: F) {
|
||||
self.into_iter().for_each(f);
|
||||
}
|
||||
}
|
||||
|
||||
impl VisitEntitiesMut for Entity {
|
||||
fn visit_entities_mut<F: FnMut(&mut Entity)>(&mut self, mut f: F) {
|
||||
f(self);
|
||||
}
|
||||
}
|
||||
|
||||
#[cfg(test)]
|
||||
mod tests {
|
||||
use crate::{
|
||||
self as bevy_ecs,
|
||||
entity::{EntityHashMap, MapEntities, SceneEntityMapper},
|
||||
world::World,
|
||||
};
|
||||
use bevy_utils::HashSet;
|
||||
|
||||
use super::*;
|
||||
|
||||
#[derive(VisitEntities, Debug, PartialEq)]
|
||||
struct Foo {
|
||||
ordered: Vec<Entity>,
|
||||
unordered: HashSet<Entity>,
|
||||
single: Entity,
|
||||
#[allow(dead_code)]
|
||||
#[visit_entities(ignore)]
|
||||
not_an_entity: String,
|
||||
}
|
||||
|
||||
// Need a manual impl since VisitEntitiesMut isn't implemented for `HashSet`.
|
||||
// We don't expect users to actually do this - it's only for test purposes
|
||||
// to prove out the automatic `MapEntities` impl we get with `VisitEntitiesMut`.
|
||||
impl VisitEntitiesMut for Foo {
|
||||
fn visit_entities_mut<F: FnMut(&mut Entity)>(&mut self, mut f: F) {
|
||||
self.ordered.visit_entities_mut(&mut f);
|
||||
self.unordered = self
|
||||
.unordered
|
||||
.drain()
|
||||
.map(|mut entity| {
|
||||
f(&mut entity);
|
||||
entity
|
||||
})
|
||||
.collect();
|
||||
f(&mut self.single);
|
||||
}
|
||||
}
|
||||
|
||||
#[test]
|
||||
fn visit_entities() {
|
||||
let mut world = World::new();
|
||||
let entities = world.entities();
|
||||
let mut foo = Foo {
|
||||
ordered: vec![entities.reserve_entity(), entities.reserve_entity()],
|
||||
unordered: [
|
||||
entities.reserve_entity(),
|
||||
entities.reserve_entity(),
|
||||
entities.reserve_entity(),
|
||||
]
|
||||
.into_iter()
|
||||
.collect(),
|
||||
single: entities.reserve_entity(),
|
||||
not_an_entity: "Bar".into(),
|
||||
};
|
||||
|
||||
let mut entity_map = EntityHashMap::<Entity>::default();
|
||||
let mut remapped = Foo {
|
||||
ordered: vec![],
|
||||
unordered: HashSet::new(),
|
||||
single: Entity::PLACEHOLDER,
|
||||
not_an_entity: foo.not_an_entity.clone(),
|
||||
};
|
||||
|
||||
// Note: this assumes that the VisitEntities derive is field-ordered,
|
||||
// which isn't explicitly stated/guaranteed.
|
||||
// If that changes, this test will fail, but that might be OK if
|
||||
// we're intentionally breaking that assumption.
|
||||
let mut i = 0;
|
||||
foo.visit_entities(|entity| {
|
||||
let new_entity = entities.reserve_entity();
|
||||
if i < foo.ordered.len() {
|
||||
assert_eq!(entity, foo.ordered[i]);
|
||||
remapped.ordered.push(new_entity);
|
||||
} else if i < foo.ordered.len() + foo.unordered.len() {
|
||||
assert!(foo.unordered.contains(&entity));
|
||||
remapped.unordered.insert(new_entity);
|
||||
} else {
|
||||
assert_eq!(entity, foo.single);
|
||||
remapped.single = new_entity;
|
||||
}
|
||||
|
||||
entity_map.insert(entity, new_entity);
|
||||
|
||||
i += 1;
|
||||
});
|
||||
|
||||
SceneEntityMapper::world_scope(&mut entity_map, &mut world, |_, mapper| {
|
||||
foo.map_entities(mapper);
|
||||
});
|
||||
|
||||
assert_eq!(foo, remapped);
|
||||
}
|
||||
}
|
@ -93,6 +93,7 @@ mod tests {
|
||||
world::{EntityMut, EntityRef, Mut, World},
|
||||
};
|
||||
use alloc::sync::Arc;
|
||||
use bevy_ecs_macros::{VisitEntities, VisitEntitiesMut};
|
||||
use bevy_tasks::{ComputeTaskPool, TaskPool};
|
||||
use bevy_utils::HashSet;
|
||||
use core::{
|
||||
@ -2051,4 +2052,18 @@ mod tests {
|
||||
field0: Simple,
|
||||
field1: ComponentB,
|
||||
}
|
||||
|
||||
#[allow(dead_code)]
|
||||
#[derive(Component, VisitEntities, VisitEntitiesMut)]
|
||||
struct MyEntities {
|
||||
entities: Vec<Entity>,
|
||||
another_one: Entity,
|
||||
maybe_entity: Option<Entity>,
|
||||
#[visit_entities(ignore)]
|
||||
something_else: String,
|
||||
}
|
||||
|
||||
#[allow(dead_code)]
|
||||
#[derive(Component, VisitEntities, VisitEntitiesMut)]
|
||||
struct MyEntitiesTuple(Vec<Entity>, Entity, #[visit_entities(ignore)] usize);
|
||||
}
|
||||
|
@ -18,6 +18,7 @@ mod entity_commands;
|
||||
mod from_world;
|
||||
mod map_entities;
|
||||
mod resource;
|
||||
mod visit_entities;
|
||||
|
||||
pub use bundle::{ReflectBundle, ReflectBundleFns};
|
||||
pub use component::{ReflectComponent, ReflectComponentFns};
|
||||
@ -25,6 +26,7 @@ pub use entity_commands::ReflectCommandExt;
|
||||
pub use from_world::{ReflectFromWorld, ReflectFromWorldFns};
|
||||
pub use map_entities::{ReflectMapEntities, ReflectMapEntitiesResource};
|
||||
pub use resource::{ReflectResource, ReflectResourceFns};
|
||||
pub use visit_entities::{ReflectVisitEntities, ReflectVisitEntitiesMut};
|
||||
|
||||
/// A [`Resource`] storing [`TypeRegistry`] for
|
||||
/// type registrations relevant to a whole app.
|
||||
|
62
crates/bevy_ecs/src/reflect/visit_entities.rs
Normal file
62
crates/bevy_ecs/src/reflect/visit_entities.rs
Normal file
@ -0,0 +1,62 @@
|
||||
use crate::entity::{Entity, VisitEntities, VisitEntitiesMut};
|
||||
use bevy_reflect::{FromReflect, FromType, PartialReflect};
|
||||
|
||||
/// For a reflected value, apply an operation to all contained entities.
|
||||
///
|
||||
/// See [`VisitEntities`] for more details.
|
||||
#[derive(Clone)]
|
||||
pub struct ReflectVisitEntities {
|
||||
visit_entities: fn(&dyn PartialReflect, &mut dyn FnMut(Entity)),
|
||||
}
|
||||
|
||||
impl ReflectVisitEntities {
|
||||
/// A general method for applying an operation to all entities in a
|
||||
/// reflected component.
|
||||
pub fn visit_entities(&self, component: &dyn PartialReflect, f: &mut dyn FnMut(Entity)) {
|
||||
(self.visit_entities)(component, f);
|
||||
}
|
||||
}
|
||||
|
||||
impl<C: FromReflect + VisitEntities> FromType<C> for ReflectVisitEntities {
|
||||
fn from_type() -> Self {
|
||||
ReflectVisitEntities {
|
||||
visit_entities: |component, f| {
|
||||
let concrete = C::from_reflect(component).unwrap();
|
||||
concrete.visit_entities(f);
|
||||
},
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
/// For a reflected value, apply an operation to mutable references to all
|
||||
/// contained entities.
|
||||
///
|
||||
/// See [`VisitEntitiesMut`] for more details.
|
||||
#[derive(Clone)]
|
||||
pub struct ReflectVisitEntitiesMut {
|
||||
visit_entities_mut: fn(&mut dyn PartialReflect, &mut dyn FnMut(&mut Entity)),
|
||||
}
|
||||
|
||||
impl ReflectVisitEntitiesMut {
|
||||
/// A general method for applying an operation to all entities in a
|
||||
/// reflected component.
|
||||
pub fn visit_entities(
|
||||
&self,
|
||||
component: &mut dyn PartialReflect,
|
||||
f: &mut dyn FnMut(&mut Entity),
|
||||
) {
|
||||
(self.visit_entities_mut)(component, f);
|
||||
}
|
||||
}
|
||||
|
||||
impl<C: FromReflect + VisitEntitiesMut> FromType<C> for ReflectVisitEntitiesMut {
|
||||
fn from_type() -> Self {
|
||||
ReflectVisitEntitiesMut {
|
||||
visit_entities_mut: |component, f| {
|
||||
let mut concrete = C::from_reflect(component).unwrap();
|
||||
concrete.visit_entities_mut(f);
|
||||
component.apply(&concrete);
|
||||
},
|
||||
}
|
||||
}
|
||||
}
|
@ -1,8 +1,11 @@
|
||||
#[cfg(feature = "reflect")]
|
||||
use bevy_ecs::reflect::{ReflectComponent, ReflectFromWorld, ReflectMapEntities};
|
||||
use bevy_ecs::reflect::{
|
||||
ReflectComponent, ReflectFromWorld, ReflectMapEntities, ReflectVisitEntities,
|
||||
ReflectVisitEntitiesMut,
|
||||
};
|
||||
use bevy_ecs::{
|
||||
component::Component,
|
||||
entity::{Entity, EntityMapper, MapEntities},
|
||||
entity::{Entity, VisitEntitiesMut},
|
||||
prelude::FromWorld,
|
||||
world::World,
|
||||
};
|
||||
@ -22,19 +25,21 @@ use smallvec::SmallVec;
|
||||
/// [`Query`]: bevy_ecs::system::Query
|
||||
/// [`Parent`]: crate::components::parent::Parent
|
||||
/// [`BuildChildren::with_children`]: crate::child_builder::BuildChildren::with_children
|
||||
#[derive(Component, Debug)]
|
||||
#[derive(Component, Debug, VisitEntitiesMut)]
|
||||
#[cfg_attr(feature = "reflect", derive(bevy_reflect::Reflect))]
|
||||
#[cfg_attr(feature = "reflect", reflect(Component, MapEntities, Debug, FromWorld))]
|
||||
#[cfg_attr(
|
||||
feature = "reflect",
|
||||
reflect(
|
||||
Component,
|
||||
MapEntities,
|
||||
VisitEntities,
|
||||
VisitEntitiesMut,
|
||||
Debug,
|
||||
FromWorld
|
||||
)
|
||||
)]
|
||||
pub struct Children(pub(crate) SmallVec<[Entity; 8]>);
|
||||
|
||||
impl MapEntities for Children {
|
||||
fn map_entities<M: EntityMapper>(&mut self, entity_mapper: &mut M) {
|
||||
for entity in &mut self.0 {
|
||||
*entity = entity_mapper.map_entity(*entity);
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
// TODO: We need to impl either FromWorld or Default so Children can be registered as Reflect.
|
||||
// This is because Reflect deserialize by creating an instance and apply a patch on top.
|
||||
// However Children should only ever be set with a real user-defined entities. Its worth looking
|
||||
|
@ -1,8 +1,11 @@
|
||||
#[cfg(feature = "reflect")]
|
||||
use bevy_ecs::reflect::{ReflectComponent, ReflectFromWorld, ReflectMapEntities};
|
||||
use bevy_ecs::reflect::{
|
||||
ReflectComponent, ReflectFromWorld, ReflectMapEntities, ReflectVisitEntities,
|
||||
ReflectVisitEntitiesMut,
|
||||
};
|
||||
use bevy_ecs::{
|
||||
component::Component,
|
||||
entity::{Entity, EntityMapper, MapEntities},
|
||||
entity::{Entity, VisitEntities, VisitEntitiesMut},
|
||||
traversal::Traversal,
|
||||
world::{FromWorld, World},
|
||||
};
|
||||
@ -21,11 +24,19 @@ use core::ops::Deref;
|
||||
/// [`Query`]: bevy_ecs::system::Query
|
||||
/// [`Children`]: super::children::Children
|
||||
/// [`BuildChildren::with_children`]: crate::child_builder::BuildChildren::with_children
|
||||
#[derive(Component, Debug, Eq, PartialEq)]
|
||||
#[derive(Component, Debug, Eq, PartialEq, VisitEntities, VisitEntitiesMut)]
|
||||
#[cfg_attr(feature = "reflect", derive(bevy_reflect::Reflect))]
|
||||
#[cfg_attr(
|
||||
feature = "reflect",
|
||||
reflect(Component, MapEntities, PartialEq, Debug, FromWorld)
|
||||
reflect(
|
||||
Component,
|
||||
MapEntities,
|
||||
VisitEntities,
|
||||
VisitEntitiesMut,
|
||||
PartialEq,
|
||||
Debug,
|
||||
FromWorld
|
||||
)
|
||||
)]
|
||||
pub struct Parent(pub(crate) Entity);
|
||||
|
||||
@ -59,12 +70,6 @@ impl FromWorld for Parent {
|
||||
}
|
||||
}
|
||||
|
||||
impl MapEntities for Parent {
|
||||
fn map_entities<M: EntityMapper>(&mut self, entity_mapper: &mut M) {
|
||||
self.0 = entity_mapper.map_entity(self.0);
|
||||
}
|
||||
}
|
||||
|
||||
impl Deref for Parent {
|
||||
type Target = Entity;
|
||||
|
||||
|
@ -1,29 +1,29 @@
|
||||
use bevy_asset::{Asset, Handle};
|
||||
use bevy_ecs::{
|
||||
component::Component,
|
||||
entity::{Entity, EntityMapper, MapEntities},
|
||||
entity::{Entity, VisitEntities, VisitEntitiesMut},
|
||||
prelude::ReflectComponent,
|
||||
reflect::ReflectMapEntities,
|
||||
reflect::{ReflectMapEntities, ReflectVisitEntities, ReflectVisitEntitiesMut},
|
||||
};
|
||||
use bevy_math::Mat4;
|
||||
use bevy_reflect::prelude::*;
|
||||
use core::ops::Deref;
|
||||
|
||||
#[derive(Component, Debug, Default, Clone, Reflect)]
|
||||
#[reflect(Component, MapEntities, Default, Debug)]
|
||||
#[derive(Component, Debug, Default, Clone, Reflect, VisitEntities, VisitEntitiesMut)]
|
||||
#[reflect(
|
||||
Component,
|
||||
MapEntities,
|
||||
VisitEntities,
|
||||
VisitEntitiesMut,
|
||||
Default,
|
||||
Debug
|
||||
)]
|
||||
pub struct SkinnedMesh {
|
||||
#[visit_entities(ignore)]
|
||||
pub inverse_bindposes: Handle<SkinnedMeshInverseBindposes>,
|
||||
pub joints: Vec<Entity>,
|
||||
}
|
||||
|
||||
impl MapEntities for SkinnedMesh {
|
||||
fn map_entities<M: EntityMapper>(&mut self, entity_mapper: &mut M) {
|
||||
for joint in &mut self.joints {
|
||||
*joint = entity_mapper.map_entity(*joint);
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
#[derive(Asset, TypePath, Debug)]
|
||||
pub struct SkinnedMeshInverseBindposes(Box<[Mat4]>);
|
||||
|
||||
|
@ -210,7 +210,7 @@ where
|
||||
mod tests {
|
||||
use bevy_ecs::{
|
||||
component::Component,
|
||||
entity::{Entity, EntityHashMap, EntityMapper, MapEntities},
|
||||
entity::{Entity, EntityHashMap, EntityMapper, MapEntities, VisitEntities},
|
||||
reflect::{
|
||||
AppTypeRegistry, ReflectComponent, ReflectMapEntities, ReflectMapEntitiesResource,
|
||||
ReflectResource,
|
||||
@ -224,7 +224,7 @@ mod tests {
|
||||
use crate::dynamic_scene::DynamicScene;
|
||||
use crate::dynamic_scene_builder::DynamicSceneBuilder;
|
||||
|
||||
#[derive(Resource, Reflect, Debug)]
|
||||
#[derive(Resource, Reflect, Debug, VisitEntities)]
|
||||
#[reflect(Resource, MapEntitiesResource)]
|
||||
struct TestResource {
|
||||
entity_a: Entity,
|
||||
@ -362,7 +362,7 @@ mod tests {
|
||||
#[reflect(Component)]
|
||||
struct A;
|
||||
|
||||
#[derive(Component, Reflect)]
|
||||
#[derive(Component, Reflect, VisitEntities)]
|
||||
#[reflect(Component, MapEntities)]
|
||||
struct B(pub Entity);
|
||||
|
||||
|
@ -515,7 +515,7 @@ mod tests {
|
||||
DynamicScene, DynamicSceneBuilder,
|
||||
};
|
||||
use bevy_ecs::{
|
||||
entity::{Entity, EntityHashMap, EntityMapper, MapEntities},
|
||||
entity::{Entity, EntityHashMap, VisitEntities, VisitEntitiesMut},
|
||||
prelude::{Component, ReflectComponent, ReflectResource, Resource, World},
|
||||
query::{With, Without},
|
||||
reflect::{AppTypeRegistry, ReflectMapEntities},
|
||||
@ -584,16 +584,10 @@ mod tests {
|
||||
foo: i32,
|
||||
}
|
||||
|
||||
#[derive(Clone, Component, Reflect, PartialEq)]
|
||||
#[derive(Clone, Component, Reflect, PartialEq, VisitEntities, VisitEntitiesMut)]
|
||||
#[reflect(Component, MapEntities, PartialEq)]
|
||||
struct MyEntityRef(Entity);
|
||||
|
||||
impl MapEntities for MyEntityRef {
|
||||
fn map_entities<M: EntityMapper>(&mut self, entity_mapper: &mut M) {
|
||||
self.0 = entity_mapper.map_entity(self.0);
|
||||
}
|
||||
}
|
||||
|
||||
impl FromWorld for MyEntityRef {
|
||||
fn from_world(_world: &mut World) -> Self {
|
||||
Self(Entity::PLACEHOLDER)
|
||||
|
@ -1,7 +1,7 @@
|
||||
use core::num::NonZero;
|
||||
|
||||
use bevy_ecs::{
|
||||
entity::{Entity, EntityMapper, MapEntities},
|
||||
entity::{Entity, VisitEntities, VisitEntitiesMut},
|
||||
prelude::{Component, ReflectComponent},
|
||||
};
|
||||
use bevy_math::{DVec2, IVec2, UVec2, Vec2};
|
||||
@ -58,14 +58,21 @@ impl WindowRef {
|
||||
}
|
||||
}
|
||||
|
||||
impl MapEntities for WindowRef {
|
||||
fn map_entities<M: EntityMapper>(&mut self, entity_mapper: &mut M) {
|
||||
impl VisitEntities for WindowRef {
|
||||
fn visit_entities<F: FnMut(Entity)>(&self, mut f: F) {
|
||||
match self {
|
||||
Self::Entity(entity) => {
|
||||
*entity = entity_mapper.map_entity(*entity);
|
||||
}
|
||||
Self::Entity(entity) => f(*entity),
|
||||
Self::Primary => {}
|
||||
};
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
impl VisitEntitiesMut for WindowRef {
|
||||
fn visit_entities_mut<F: FnMut(&mut Entity)>(&mut self, mut f: F) {
|
||||
match self {
|
||||
Self::Entity(entity) => f(entity),
|
||||
Self::Primary => {}
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
|
Loading…
Reference in New Issue
Block a user