From b2046ca77902643fe88de46aedf46c6ea3f00aca Mon Sep 17 00:00:00 2001 From: eckz <567737+eckz@users.noreply.github.com> Date: Tue, 21 Apr 2026 17:19:43 +0200 Subject: [PATCH 1/2] Allowing using ChildOf directly inside bsn! macro --- crates/bevy_ecs/src/hierarchy.rs | 3 ++- crates/bevy_ecs/src/template.rs | 12 +++++++++++- crates/bevy_scene/src/lib.rs | 29 +++++++++++++++++++++++++++++ 3 files changed, 42 insertions(+), 2 deletions(-) diff --git a/crates/bevy_ecs/src/hierarchy.rs b/crates/bevy_ecs/src/hierarchy.rs index 9472a74730a8c..ad272a2f0480c 100644 --- a/crates/bevy_ecs/src/hierarchy.rs +++ b/crates/bevy_ecs/src/hierarchy.rs @@ -14,6 +14,7 @@ use crate::{ entity::Entity, relationship::{RelatedSpawner, RelatedSpawnerCommands}, system::EntityCommands, + template::FromTemplate, world::{EntityWorldMut, FromWorld, World}, }; use alloc::vec::Vec; @@ -90,7 +91,7 @@ use core::slice; /// ``` /// /// [`Relationship`]: crate::relationship::Relationship -#[derive(Component, Clone, PartialEq, Eq, Debug)] +#[derive(Component, FromTemplate, Clone, PartialEq, Eq, Debug)] #[cfg_attr(feature = "bevy_reflect", derive(bevy_reflect::Reflect))] #[cfg_attr( feature = "bevy_reflect", diff --git a/crates/bevy_ecs/src/template.rs b/crates/bevy_ecs/src/template.rs index 85c9e3787fb24..445d2d78b4e99 100644 --- a/crates/bevy_ecs/src/template.rs +++ b/crates/bevy_ecs/src/template.rs @@ -403,6 +403,8 @@ pub trait SpecializeFromTemplate: Sized {} /// A [`Template`] reference to an [`Entity`]. pub enum EntityReference { + /// A reference to a specific [`Entity`] + SpecificEntity(Entity), /// A reference to an entity via a [`ScopedEntityIndex`] ScopedEntityIndex(ScopedEntityIndex), } @@ -427,12 +429,19 @@ impl Default for EntityReference { } } +impl From for EntityReference { + fn from(entity: Entity) -> Self { + Self::SpecificEntity(entity) + } +} + impl Template for EntityReference { type Output = Entity; fn build_template(&self, context: &mut TemplateContext) -> Result { Ok(match self { - EntityReference::ScopedEntityIndex(scoped_entity_index) => { + Self::SpecificEntity(entity) => *entity, + Self::ScopedEntityIndex(scoped_entity_index) => { context.get_scoped_entity(*scoped_entity_index) } }) @@ -440,6 +449,7 @@ impl Template for EntityReference { fn clone_template(&self) -> Self { match self { + Self::SpecificEntity(entity) => Self::SpecificEntity(*entity), Self::ScopedEntityIndex(scoped_entity_index) => { Self::ScopedEntityIndex(*scoped_entity_index) } diff --git a/crates/bevy_scene/src/lib.rs b/crates/bevy_scene/src/lib.rs index 86ed3cc40240b..98a2a76cbe49a 100644 --- a/crates/bevy_scene/src/lib.rs +++ b/crates/bevy_scene/src/lib.rs @@ -569,6 +569,7 @@ mod tests { use bevy_asset::{Asset, AssetApp, AssetLoader, AssetPlugin, AssetServer, Assets, Handle}; use bevy_ecs::lifecycle::HookContext; use bevy_ecs::prelude::*; + use bevy_ecs::relationship::Relationship; use bevy_ecs::world::DeferredWorld; use bevy_reflect::TypePath; use std::path::Path; @@ -1154,6 +1155,34 @@ mod tests { assert_eq!(sprite.0, handle); } + #[test] + fn child_of_template() { + let mut app = test_app(); + + let world = app.world_mut(); + + fn scene(root: Entity) -> impl SceneList { + bsn_list! { + ( #Child1 ChildOf(root) ), + ( #Child2 ChildOf(#Child1) ), + } + } + + let root = world.spawn_empty().id(); + + let ids = world.spawn_scene_list(scene(root)).unwrap(); + assert_eq!(ids.len(), 2); + + let [a, b] = world.entity(&*ids)[..] else { + unreachable!() + }; + assert_eq!(a.get::().unwrap().as_str(), "Child1"); + assert_eq!(a.get::().unwrap().get(), root); + + assert_eq!(b.get::().unwrap().as_str(), "Child2"); + assert_eq!(b.get::().unwrap().get(), a.id()); + } + #[test] fn scene_list_children() { let mut app = test_app(); From fd212f9d528b1f970e8dac05d0436322a117d87a Mon Sep 17 00:00:00 2001 From: Carter Anderson Date: Tue, 21 Apr 2026 13:19:29 -0700 Subject: [PATCH 2/2] SpecificEntity -> Entity --- crates/bevy_ecs/src/template.rs | 8 ++++---- 1 file changed, 4 insertions(+), 4 deletions(-) diff --git a/crates/bevy_ecs/src/template.rs b/crates/bevy_ecs/src/template.rs index 445d2d78b4e99..0760961d0bbfe 100644 --- a/crates/bevy_ecs/src/template.rs +++ b/crates/bevy_ecs/src/template.rs @@ -404,7 +404,7 @@ pub trait SpecializeFromTemplate: Sized {} /// A [`Template`] reference to an [`Entity`]. pub enum EntityReference { /// A reference to a specific [`Entity`] - SpecificEntity(Entity), + Entity(Entity), /// A reference to an entity via a [`ScopedEntityIndex`] ScopedEntityIndex(ScopedEntityIndex), } @@ -431,7 +431,7 @@ impl Default for EntityReference { impl From for EntityReference { fn from(entity: Entity) -> Self { - Self::SpecificEntity(entity) + Self::Entity(entity) } } @@ -440,7 +440,7 @@ impl Template for EntityReference { fn build_template(&self, context: &mut TemplateContext) -> Result { Ok(match self { - Self::SpecificEntity(entity) => *entity, + Self::Entity(entity) => *entity, Self::ScopedEntityIndex(scoped_entity_index) => { context.get_scoped_entity(*scoped_entity_index) } @@ -449,7 +449,7 @@ impl Template for EntityReference { fn clone_template(&self) -> Self { match self { - Self::SpecificEntity(entity) => Self::SpecificEntity(*entity), + Self::Entity(entity) => Self::Entity(*entity), Self::ScopedEntityIndex(scoped_entity_index) => { Self::ScopedEntityIndex(*scoped_entity_index) }