From 937f6bf95c3d411621b9fc7cb0f6b55d78fc88d8 Mon Sep 17 00:00:00 2001 From: iiyese Date: Sun, 5 Nov 2023 23:45:54 +0000 Subject: [PATCH 1/3] Save work --- Cargo.toml | 4 +-- README.md | 4 +-- src/edges.rs | 35 ++++++++++++++---- src/lib.rs | 4 +-- src/operations/mod.rs | 2 +- src/operations/utils.rs | 32 +++++++++++++++-- src/relation.rs | 77 +++++++++++++++++++++++++++++++++++++--- src/scope.rs | 78 ++++++++++++++++++++++------------------- src/tuple_traits.rs | 28 +++++++++++++-- 9 files changed, 205 insertions(+), 59 deletions(-) diff --git a/Cargo.toml b/Cargo.toml index 686498c..1065e56 100644 --- a/Cargo.toml +++ b/Cargo.toml @@ -1,6 +1,6 @@ [package] name = "aery" -version = "0.4.0" +version = "0.5.0" edition = "2021" authors = ["iiYese iiyese@outlook.com"] repository = "https://github.com/iiYese/aery" @@ -12,7 +12,7 @@ readme = "README.md" # See more keys and their definitions at https://doc.rust-lang.org/cargo/reference/manifest.html [dependencies] -bevy = "0.11" +bevy = "0.12" smallvec = "1.11.0" aery_macros = { path = "macros", version = "0.3.0-dev" } aquamarine = "0.3.2" diff --git a/README.md b/README.md index d5c8fe7..43d1654 100644 --- a/README.md +++ b/README.md @@ -154,8 +154,8 @@ fn tick_devices( ### Version table | Bevy version | Aery verison | |--------------|--------------| -| 0.11 | 0.4 | -| 0.11 | 0.3 | +| 0.12 | 0.5 | +| 0.11 | 0.3 - 0.4 | | 0.10 | 0.1 - 0.2 | ### Credits diff --git a/src/edges.rs b/src/edges.rs index 5d90c2a..1953b9f 100644 --- a/src/edges.rs +++ b/src/edges.rs @@ -9,9 +9,9 @@ use bevy::{ entity::Entity, query::{Or, With, Without, WorldQuery}, system::{Command, CommandQueue, Resource}, - world::{EntityMut, World}, + world::{EntityWorldMut, World}, }, - //hierarchy::{Children, Parent}, + hierarchy::{Children, Parent}, log::warn, prelude::{Deref, DerefMut}, }; @@ -86,6 +86,14 @@ impl Default for Targets { #[allow(missing_docs)] pub type EdgeIter<'a> = std::iter::Copied>; +/// Edges world query for hierarchy compatibility +#[derive(WorldQuery)] +pub struct HierarchyEdges { + pub(crate) hosts: Option<&'static Children>, + pub(crate) target: Option<&'static Parent>, + pub(crate) _filter: Or<(With, With)>, +} + /// World query to get the edge info of a Relation. #[derive(WorldQuery)] pub struct Edges { @@ -102,6 +110,22 @@ pub trait EdgeInfo { fn targets(&self) -> &[Entity]; } +impl EdgeInfo for HierarchyEdgesItem<'_> { + fn hosts(&self) -> &[Entity] { + match self.hosts { + Some(hosts) => hosts, + None => &[], + } + } + + fn targets(&self) -> &[Entity] { + match self.target { + Some(target) => target.as_slice(), + None => &[], + } + } +} + impl EdgeInfo for EdgesItem<'_, R> { fn hosts(&self) -> &[Entity] { match self.hosts { @@ -135,9 +159,6 @@ impl EdgeInfo for Option { } } -// TODO: bevy 0.12 -// impl EdgeInfo for Hierarchy - #[derive(Component, Default, Deref, DerefMut)] pub(crate) struct OnDelete { #[deref] @@ -646,7 +667,7 @@ impl Command for Withdraw { } } -/// An extension API for [`EntityMut`] to sugar using relation commands. +/// An extension API for [`EntityWorldMut`] to sugar using relation commands. pub trait RelationCommands { /// [`Set`] a relationship target. fn set(&mut self, target: Entity) -> &mut Self; @@ -662,7 +683,7 @@ pub trait RelationCommands { #[rustfmt::skip] #[allow(clippy::let_unit_value)] -impl RelationCommands for EntityMut<'_> { +impl RelationCommands for EntityWorldMut<'_> { fn set(&mut self, target: Entity) -> &mut Self { let _ = R::ZST_OR_PANIC; diff --git a/src/lib.rs b/src/lib.rs index e52c171..294cefa 100644 --- a/src/lib.rs +++ b/src/lib.rs @@ -241,8 +241,8 @@ pub mod prelude { utils::{EdgeSide, Relations, Up}, FoldBreadth, Join, Track, TrackSelf, Traverse, }, - relation::{CleanupPolicy, Relation, ZstOrPanic}, - scope::{EntityMutExt, Scope}, + relation::{CleanupPolicy, Hierarchy, Relation, ZstOrPanic}, + scope::{AeryEntityWorldMutExt, Scope}, tuple_traits::{Joinable, RelationSet}, }; #[doc(no_inline)] diff --git a/src/operations/mod.rs b/src/operations/mod.rs index 59e82d2..9f0ffb7 100644 --- a/src/operations/mod.rs +++ b/src/operations/mod.rs @@ -167,7 +167,7 @@ impl TrackSelf for TraverseAnd { } } -/// Track the last seen from a query when traversing an edge. This is useful in scenarios +/// Track the last seen item from a query when traversing an edge. This is useful in scenarios /// where you mightn't have a component on every entity in a hierarchy. For instance it might not /// make sense for a component to be on every entity or even an immediate ancestor meaning spam /// propogation isn't viable. Scroll areas in UI are one example of this. diff --git a/src/operations/utils.rs b/src/operations/utils.rs index 5f681ac..5fd3c66 100644 --- a/src/operations/utils.rs +++ b/src/operations/utils.rs @@ -1,12 +1,12 @@ use crate::{ edges::EdgeIter, - relation::{Relation, RelationId}, + relation::{Hierarchy, Relation, RelationId}, tuple_traits::*, }; use bevy::ecs::{entity::Entity, query::WorldQuery}; -use std::marker::PhantomData; +use std::{any::TypeId, marker::PhantomData}; /// Struct to track metadat for a join operation. pub struct JoinWith { @@ -95,6 +95,34 @@ pub trait EdgeSide { RelationsItem<'i, RS>: RelationEntries; } +impl EdgeSide for Hierarchy { + fn entities<'i, 'r, RS>(relations: &'r RelationsItem<'i, RS>) -> EdgeIter<'r> + where + 'i: 'r, + RS: RelationSet, + RelationsItem<'i, RS>: RelationEntries, + { + relations + .hosts(RelationId(TypeId::of::())) + .iter() + .copied() + } +} + +impl EdgeSide for Up { + fn entities<'i, 'r, RS>(relations: &'r RelationsItem<'i, RS>) -> EdgeIter<'r> + where + 'i: 'r, + RS: RelationSet, + RelationsItem<'i, RS>: RelationEntries, + { + relations + .targets(RelationId(TypeId::of::())) + .iter() + .copied() + } +} + impl EdgeSide for R { fn entities<'i, 'r, RS>(relations: &'r RelationsItem<'i, RS>) -> EdgeIter<'r> where diff --git a/src/relation.rs b/src/relation.rs index ee0cfd5..554d196 100644 --- a/src/relation.rs +++ b/src/relation.rs @@ -1,8 +1,6 @@ use crate::Var; use core::any::TypeId; -// TODO 0.12 impl for Hierarchy - /// Type ID of a relation. #[derive(Clone, Copy, Debug, PartialEq, Eq, Hash)] pub struct RelationId(pub(crate) TypeId); @@ -21,6 +19,12 @@ impl From for Var { } } +impl From for Var { + fn from(_: Hierarchy) -> Self { + Self::Val(RelationId(TypeId::of::())) + } +} + /// Hack to ensure relation types are indeed ZSTs pub trait ZstOrPanic: Sized { #[allow(missing_docs)] @@ -153,6 +157,37 @@ pub enum CleanupPolicy { /// for more information. This isn't necessarily good or bad. Archetype fragmentation is a more /// advanced topic but to keep it short and simple the archetype fragmentation is comparable to /// `bevy_hierarchy` if it supported multiple hierarchy types. +/// ## Derive examples +/// ``` +/// use aery::prelude::*; +/// +/// // Simple derive with defaults: +/// // - Orphaning +/// // - Exclusive +/// // - Asymmetric +/// #[derive(Relation)] +/// struct R; +/// +/// // Override edge exclusivity +/// #[derive(Relation)] +/// #[aery(Poly)] +/// struct Poly; +/// +/// // Override edge symmetry +/// #[derive(Relation)] +/// #[aery(Symmetric)] +/// struct Symmetric; +/// +/// // Override cleanup policy +/// #[derive(Relation)] +/// #[aery(Recursive)] // Available: Counted, Recursive, Total +/// struct Recursive; +/// +/// // Override multiple properties +/// #[derive(Relation)] +/// #[aery(Poly, Symmetric, Counted)] +/// struct Multi; +/// ``` pub trait Relation: 'static + Sized + Send + Sync { /// How to clean up entities and relations when an entity with a relation is despawned /// or when a relation is unset. @@ -172,6 +207,38 @@ pub trait Relation: 'static + Sized + Send + Sync { const SYMMETRIC: bool = false; } -// TODO: Enable for 0.12 -// For hierarchy compatibility -//pub struct Hierarchy; +/// For compatibility with bevy_hierarchy. +/// **WARNING:** +/// - Hierarchy cleanup does not clean aery relations. +/// - Aery cleanup policies do not clean up hierarchy edges. +/// ## Query example +/// ``` +/// use bevy::prelude::*; +/// use aery::prelude::*; +/// +/// #[derive(Component)] +/// struct A { +/// // .. +/// } +/// +/// #[derive(Component)] +/// struct B { +/// // .. +/// } +/// +/// #[derive(Relation)] +/// struct R; +/// +/// fn sys( +/// a_query: Query<&A>, +/// b_query: Query<(&B, Relations<(Hierarchy, R)>)>, // Can use alone or along side relations +/// roots: Query, Without)> +/// ) { +/// b_query.traverse::(roots.iter()).for_each(|b, edges| { +/// edges.join::(&a_query).for_each(|a| { +/// // .. +/// }); +/// }) +/// } +/// ``` +pub struct Hierarchy; diff --git a/src/scope.rs b/src/scope.rs index 42aa53e..bcbf24e 100644 --- a/src/scope.rs +++ b/src/scope.rs @@ -1,58 +1,52 @@ use crate::{ edges::Set, - relation::{ - Relation, - ZstOrPanic, - // TODO: bevy 0.12 - // Hierarchy - }, + relation::{Relation, ZstOrPanic}, }; use bevy::ecs::{ bundle::Bundle, entity::Entity, - system::Command, - world::{EntityMut, World}, + system::{Command, Commands, EntityCommands}, + world::{EntityWorldMut, World}, }; use std::marker::PhantomData; -/// TODO: Example + mermaid illustration /// Builder API to construct hierarchies of relations. -pub struct Scope<'w, T> { +pub struct Scope { top: Entity, last: Entity, - world: &'w mut World, - _phantom: PhantomData, + api: API, + _phantom: PhantomData, } -impl Scope<'_, R> { +impl Scope<&'_ mut World, R> { /// Spawn an entity from a bundle and have it target the currently scoped entity via. pub fn add(&mut self, bundle: impl Bundle) -> &mut Self { - let id = self.world.spawn(bundle).id(); - Command::apply(Set::::new(id, self.top), self.world); + let id = self.api.spawn(bundle).id(); + Command::apply(Set::::new(id, self.top), self.api); self.last = id; self } /// Spawn an entity from a bundle and set it as a target of the currently scoped entity. pub fn add_target(&mut self, bundle: impl Bundle) -> &mut Self { - let id = self.world.spawn(bundle).id(); - Command::apply(Set::::new(self.top, id), self.world); + let id = self.api.spawn(bundle).id(); + Command::apply(Set::::new(self.top, id), self.api); self.last = id; self } /// Spawn an entity and have it target the currently scoped entity via. /// This function takes a closure to provide entity mut access. - pub fn add_and(&mut self, mut func: impl for<'e> FnMut(&mut EntityMut<'e>)) -> &mut Self { + pub fn add_and(&mut self, mut func: impl for<'e> FnMut(&mut EntityWorldMut<'e>)) -> &mut Self { let id = { - let mut spawned = self.world.spawn(()); + let mut spawned = self.api.spawn(()); func(&mut spawned); spawned.id() }; - Command::apply(Set::::new(id, self.top), self.world); + Command::apply(Set::::new(id, self.top), self.api); self.last = id; self } @@ -61,33 +55,33 @@ impl Scope<'_, R> { /// This function takes a closure to provide entity mut access. pub fn add_target_and( &mut self, - mut func: impl for<'e> FnMut(&mut EntityMut<'e>), + mut func: impl for<'e> FnMut(&mut EntityWorldMut<'e>), ) -> &mut Self { let id = { - let mut spawned = self.world.spawn(()); + let mut spawned = self.api.spawn(()); func(&mut spawned); spawned.id() }; - Command::apply(Set::::new(self.top, id), self.world); + Command::apply(Set::::new(self.top, id), self.api); self.last = id; self } } -impl<'a, T: Relation> Scope<'a, T> { +impl Scope<&'_ mut World, T> { /// Scope the last spawned entity via `R`. Any targets or hosts that are added in the scope /// implicitly use `R` as the edge. pub fn scope( &mut self, - mut func: impl for<'i> FnMut(&mut Scope<'i, R>), + mut func: impl for<'i> FnMut(&mut Scope<&'_ mut World, R>), ) -> &mut Self { let _ = R::ZST_OR_PANIC; - let mut inner = Scope:: { + let mut inner = Scope::<&'_ mut World, R> { top: self.last, last: self.last, - world: self.world, + api: self.api, _phantom: PhantomData, }; @@ -97,17 +91,20 @@ impl<'a, T: Relation> Scope<'a, T> { } } -// TODO: bevy 0.12 -// impl<'a> Scope<'a, Hierarchy> {} - -/// Ext trait to produce a [`Scope`] from an [`EntityMut`]. -pub trait EntityMutExt<'a> { +/// Ext trait to produce a [`Scope`] from an [`EntityWorldMut`]. +pub trait AeryEntityWorldMutExt<'a> { #[allow(missing_docs)] - fn scope(&mut self, func: impl for<'i> FnMut(&mut Scope<'i, R>)) -> &mut Self; + fn scope( + &mut self, + func: impl for<'i> FnMut(&mut Scope<&'_ mut World, R>), + ) -> &mut Self; } -impl<'a> EntityMutExt<'a> for EntityMut<'a> { - fn scope(&mut self, mut func: impl for<'i> FnMut(&mut Scope<'i, R>)) -> &mut Self { +impl<'a> AeryEntityWorldMutExt<'a> for EntityWorldMut<'a> { + fn scope( + &mut self, + mut func: impl for<'i> FnMut(&mut Scope<&'_ mut World, R>), + ) -> &mut Self { let _ = R::ZST_OR_PANIC; let id = self.id(); @@ -116,7 +113,7 @@ impl<'a> EntityMutExt<'a> for EntityMut<'a> { let mut scope = Scope { top: id, last: id, - world, + api: world, _phantom: PhantomData, }; @@ -126,3 +123,12 @@ impl<'a> EntityMutExt<'a> for EntityMut<'a> { self } } + +/// Ext trait to produce a [`Scope`] from an [`EntityCommands`]. +pub trait AeryEntityCommandsExt<'a> { + #[allow(missing_docs)] + fn scope( + &mut self, + func: impl for<'i> FnMut(&mut Scope<&'_ mut Commands, R>), + ) -> &mut Self; +} diff --git a/src/tuple_traits.rs b/src/tuple_traits.rs index f56b3e0..e3e6da6 100644 --- a/src/tuple_traits.rs +++ b/src/tuple_traits.rs @@ -1,7 +1,7 @@ use crate::{ - edges::{EdgeInfo, Edges}, + edges::{EdgeInfo, Edges, HierarchyEdges}, operations::utils::{EdgeProduct, EdgeSide, RelationsItem}, - relation::{Relation, RelationId}, + relation::{Hierarchy, Relation, RelationId}, }; use core::any::TypeId; @@ -18,6 +18,7 @@ mod sealed { use super::*; pub trait Sealed {} + impl Sealed for Hierarchy {} impl Sealed for R {} impl Sealed for Option {} @@ -83,6 +84,11 @@ pub trait RelationSet: Sized + Sealed { type Types: 'static; } +impl RelationSet for Hierarchy { + type Edges = HierarchyEdges; + type Types = Hierarchy; +} + impl RelationSet for R { type Edges = Edges; type Types = R; @@ -112,6 +118,24 @@ pub trait RelationEntries { fn targets(&self, id: impl Into) -> &[Entity]; } +impl RelationEntries for RelationsItem<'_, Hierarchy> { + fn hosts(&self, id: impl Into) -> &[Entity] { + if TypeId::of::() == id.into().0 { + self.edges.hosts() + } else { + &[] + } + } + + fn targets(&self, id: impl Into) -> &[Entity] { + if TypeId::of::() == id.into().0 { + self.edges.targets() + } else { + &[] + } + } +} + impl RelationEntries for RelationsItem<'_, R> { fn hosts(&self, id: impl Into) -> &[Entity] { if TypeId::of::() == id.into().0 { From 64796c30f89173a0ae292c0bac8d943bc6876579 Mon Sep 17 00:00:00 2001 From: iiyese Date: Wed, 8 Nov 2023 13:46:44 +0000 Subject: [PATCH 2/3] save work --- src/scope.rs | 177 ++++++++++++++++++++++++++++++++++++++++----------- 1 file changed, 140 insertions(+), 37 deletions(-) diff --git a/src/scope.rs b/src/scope.rs index bcbf24e..84b3f01 100644 --- a/src/scope.rs +++ b/src/scope.rs @@ -13,6 +13,19 @@ use bevy::ecs::{ use std::marker::PhantomData; /// Builder API to construct hierarchies of relations. +/// ``` +/// world.spawn(bundle).scope::(|scope| { +/// // x, y, z are implicitly `ChildOf` the last spawned entity +/// scope.add(x) +/// .add(y) +/// .add(z) +/// .scope::(|scope| { +/// // a, b are implicitly `ChildOf` the last spawned entity (z) +/// scope.add(a) +/// .add(b); +/// }); +/// }); +/// ``` pub struct Scope { top: Entity, last: Entity, @@ -20,8 +33,60 @@ pub struct Scope { _phantom: PhantomData, } +/// Ext trait to produce a [`Scope`] from an [`EntityWorldMut`]. +pub trait AeryEntityWorldMutExt<'a> { + #[allow(missing_docs)] + fn scope(&mut self, func: impl FnMut(&mut Scope<&'_ mut World, R>)) -> &mut Self; +} + +impl<'a> AeryEntityWorldMutExt<'a> for EntityWorldMut<'a> { + fn scope( + &mut self, + mut func: impl FnMut(&mut Scope<&'_ mut World, R>), + ) -> &mut Self { + let _ = R::ZST_OR_PANIC; + + let id = self.id(); + + self.world_scope(|world| { + let mut scope = Scope { + top: id, + last: id, + api: world, + _phantom: PhantomData, + }; + + func(&mut scope); + }); + + self + } +} + +impl Scope<&'_ mut World, T> { + /// Scope the last spawned entity via `R`. Any targets or hosts that are added in the scope + /// implicitly use `R` as the edge. + pub fn scope( + &mut self, + mut func: impl FnMut(&mut Scope<&'_ mut World, R>), + ) -> &mut Self { + let _ = R::ZST_OR_PANIC; + + let mut inner = Scope::<&'_ mut World, R> { + top: self.last, + last: self.last, + api: self.api, + _phantom: PhantomData, + }; + + func(&mut inner); + + self + } +} + impl Scope<&'_ mut World, R> { - /// Spawn an entity from a bundle and have it target the currently scoped entity via. + /// Spawn an entity from a bundle and have it target the currently scoped entity via `R`. pub fn add(&mut self, bundle: impl Bundle) -> &mut Self { let id = self.api.spawn(bundle).id(); Command::apply(Set::::new(id, self.top), self.api); @@ -69,16 +134,47 @@ impl Scope<&'_ mut World, R> { } } -impl Scope<&'_ mut World, T> { +/// Ext trait to produce a [`Scope`] from an [`EntityCommands`]. +pub trait AeryEntityCommandsExt<'w, 's, 'a> { + #[allow(missing_docs)] + fn scope( + &mut self, + func: impl FnMut(&mut Scope<&'_ mut Commands, R>), + ) -> &mut Self; +} + +impl<'w, 's, 'a> AeryEntityCommandsExt<'w, 's, 'a> for EntityCommands<'w, 's, 'a> { + fn scope( + &mut self, + mut func: impl FnMut(&mut Scope<&'_ mut Commands, R>), + ) -> &mut Self { + let _ = R::ZST_OR_PANIC; + + let id = self.id(); + + let mut scope = Scope { + top: id, + last: id, + api: self.commands(), + _phantom: PhantomData, + }; + + func(&mut scope); + + self + } +} + +impl Scope<&'_ mut Commands<'_, '_>, T> { /// Scope the last spawned entity via `R`. Any targets or hosts that are added in the scope /// implicitly use `R` as the edge. pub fn scope( &mut self, - mut func: impl for<'i> FnMut(&mut Scope<&'_ mut World, R>), + mut func: impl FnMut(&mut Scope<&'_ mut Commands<'_, '_>, R>), ) -> &mut Self { let _ = R::ZST_OR_PANIC; - let mut inner = Scope::<&'_ mut World, R> { + let mut inner = Scope::<&'_ mut Commands<'_, '_>, R> { top: self.last, last: self.last, api: self.api, @@ -91,44 +187,51 @@ impl Scope<&'_ mut World, T> { } } -/// Ext trait to produce a [`Scope`] from an [`EntityWorldMut`]. -pub trait AeryEntityWorldMutExt<'a> { - #[allow(missing_docs)] - fn scope( - &mut self, - func: impl for<'i> FnMut(&mut Scope<&'_ mut World, R>), - ) -> &mut Self; -} - -impl<'a> AeryEntityWorldMutExt<'a> for EntityWorldMut<'a> { - fn scope( - &mut self, - mut func: impl for<'i> FnMut(&mut Scope<&'_ mut World, R>), - ) -> &mut Self { - let _ = R::ZST_OR_PANIC; - - let id = self.id(); +impl Scope<&'_ mut Commands<'_, '_>, R> { + /// Spawn an entity from a bundle and have it target the currently scoped entity via `R`. + pub fn add(&mut self, bundle: impl Bundle) -> &mut Self { + let id = self.api.spawn(bundle).id(); + self.api.add(Set::::new(id, self.top)); + self.last = id; + self + } - self.world_scope(|world| { - let mut scope = Scope { - top: id, - last: id, - api: world, - _phantom: PhantomData, - }; + /// Spawn an entity from a bundle and set it as a target of the currently scoped entity. + pub fn add_target(&mut self, bundle: impl Bundle) -> &mut Self { + let id = self.api.spawn(bundle).id(); + Command::apply(Set::::new(self.top, id), self.api); + self.last = id; + self + } - func(&mut scope); - }); + /// Spawn an entity and have it target the currently scoped entity via. + /// This function takes a closure to provide entity mut access. + pub fn add_and(&mut self, mut func: impl for<'e> FnMut(&mut EntityWorldMut<'e>)) -> &mut Self { + let id = { + let mut spawned = self.api.spawn(()); + func(&mut spawned); + spawned.id() + }; + Command::apply(Set::::new(id, self.top), self.api); + self.last = id; self } -} -/// Ext trait to produce a [`Scope`] from an [`EntityCommands`]. -pub trait AeryEntityCommandsExt<'a> { - #[allow(missing_docs)] - fn scope( + /// Spawn an entity and set it as a target of the currently scoped entity. + /// This function takes a closure to provide entity mut access. + pub fn add_target_and( &mut self, - func: impl for<'i> FnMut(&mut Scope<&'_ mut Commands, R>), - ) -> &mut Self; + mut func: impl for<'e> FnMut(&mut EntityWorldMut<'e>), + ) -> &mut Self { + let id = { + let mut spawned = self.api.spawn(()); + func(&mut spawned); + spawned.id() + }; + + Command::apply(Set::::new(self.top, id), self.api); + self.last = id; + self + } } From 00de73f284be7473d7be508ec602007ceefb081e Mon Sep 17 00:00:00 2001 From: iiyese Date: Wed, 8 Nov 2023 16:56:06 +0000 Subject: [PATCH 3/3] Complete scope api for commands + add missing docs. --- src/lib.rs | 2 +- src/scope.rs | 44 ++++++++++++++++++++++++++++---------------- 2 files changed, 29 insertions(+), 17 deletions(-) diff --git a/src/lib.rs b/src/lib.rs index 294cefa..a0fe074 100644 --- a/src/lib.rs +++ b/src/lib.rs @@ -242,7 +242,7 @@ pub mod prelude { FoldBreadth, Join, Track, TrackSelf, Traverse, }, relation::{CleanupPolicy, Hierarchy, Relation, ZstOrPanic}, - scope::{AeryEntityWorldMutExt, Scope}, + scope::{AeryEntityWorldMutExt, Scope, AeryEntityCommandsExt}, tuple_traits::{Joinable, RelationSet}, }; #[doc(no_inline)] diff --git a/src/scope.rs b/src/scope.rs index 84b3f01..995788c 100644 --- a/src/scope.rs +++ b/src/scope.rs @@ -14,17 +14,29 @@ use std::marker::PhantomData; /// Builder API to construct hierarchies of relations. /// ``` -/// world.spawn(bundle).scope::(|scope| { -/// // x, y, z are implicitly `ChildOf` the last spawned entity -/// scope.add(x) -/// .add(y) -/// .add(z) -/// .scope::(|scope| { -/// // a, b are implicitly `ChildOf` the last spawned entity (z) -/// scope.add(a) -/// .add(b); -/// }); -/// }); +/// use bevy::prelude::*; +/// use aery::prelude::*; +/// +/// #[derive(Relation)] +/// struct ChildOf; +/// +/// #[derive(Component)] +/// struct C; +/// +/// fn sys(mut cmds: Commands) { +/// cmds.spawn(C::<0>) +/// .scope::(|scope| { +/// // 1, 2, 3 are implicitly `ChildOf` the last spawned entity (0) +/// scope.add(C::<1>) +/// .add(C::<2>) +/// .add(C::<3>) +/// .scope::(|scope| { +/// // 4, 5 are implicitly `ChildOf` the last spawned entity (3) +/// scope.add(C::<4>) +/// .add(C::<5>); +/// }); +/// }); +/// } /// ``` pub struct Scope { top: Entity, @@ -199,21 +211,21 @@ impl Scope<&'_ mut Commands<'_, '_>, R> { /// Spawn an entity from a bundle and set it as a target of the currently scoped entity. pub fn add_target(&mut self, bundle: impl Bundle) -> &mut Self { let id = self.api.spawn(bundle).id(); - Command::apply(Set::::new(self.top, id), self.api); + self.api.add(Set::::new(self.top, id)); self.last = id; self } /// Spawn an entity and have it target the currently scoped entity via. /// This function takes a closure to provide entity mut access. - pub fn add_and(&mut self, mut func: impl for<'e> FnMut(&mut EntityWorldMut<'e>)) -> &mut Self { + pub fn add_and(&mut self, mut func: impl for<'w, 's, 'e> FnMut(&mut EntityCommands<'w, 's, 'e>)) -> &mut Self { let id = { let mut spawned = self.api.spawn(()); func(&mut spawned); spawned.id() }; - Command::apply(Set::::new(id, self.top), self.api); + self.api.add(Set::::new(id, self.top)); self.last = id; self } @@ -222,7 +234,7 @@ impl Scope<&'_ mut Commands<'_, '_>, R> { /// This function takes a closure to provide entity mut access. pub fn add_target_and( &mut self, - mut func: impl for<'e> FnMut(&mut EntityWorldMut<'e>), + mut func: impl for<'w, 's, 'e> FnMut(&mut EntityCommands<'w, 's, 'e>), ) -> &mut Self { let id = { let mut spawned = self.api.spawn(()); @@ -230,7 +242,7 @@ impl Scope<&'_ mut Commands<'_, '_>, R> { spawned.id() }; - Command::apply(Set::::new(self.top, id), self.api); + self.api.add(Set::::new(self.top, id)); self.last = id; self }