父实体通过宏声明子实体

环境: v0.16.

fn spawn_splash_screen(mut commands: Commands, asset_server: Res<AssetServer>) {
    commands.ui_root().insert((
        Name::new("Splash screen"),
        BackgroundColor(SPLASH_BACKGROUND_COLOR),
        StateScoped(Screen::Splash),
        children![(
            Name::new("Splash image"),
            Node {
                margin: UiRect::all(Val::Auto),
                width: Val::Percent(70.0),
                ..default()
            },
            ImageNode::new(asset_server.load_with_settings(
                // This should be an embedded asset for instant loading, but that is
                // currently [broken on Windows Wasm builds](https://github.com/bevyengine/bevy/issues/14246).
                "images/splash.png",
                |settings: &mut ImageLoaderSettings| {
                    // Make an exception for the splash image in case
                    // `ImagePlugin::default_nearest()` is used for pixel art.
                    settings.sampler = ImageSampler::linear();
                },
            )),
            ImageNodeFadeInOut {
                total_duration: SPLASH_DURATION_SECS,
                fade_duration: SPLASH_FADE_DURATION_SECS,
                t: 0.0,
            },
        )],
    ));
}
/// An extension trait for spawning UI containers.
pub trait Containers {
    /// Spawns a root node that covers the full screen
    /// and centers its content horizontally and vertically.
    fn ui_root(&mut self) -> EntityCommands;
}

impl Containers for Commands<'_, '_> {
    fn ui_root(&mut self) -> EntityCommands {
        self.spawn((
            Name::new("UI Root"),
            Node {
                width: Percent(100.0),
                height: Percent(100.0),
                justify_content: JustifyContent::Center,
                align_items: AlignItems::Center,
                flex_direction: FlexDirection::Column,
                row_gap: Px(10.0),
                position_type: PositionType::Absolute,
                ..default()
            },
        ))
    }
}
#[macro_export]
macro_rules! children {
    [$($child:expr),*$(,)?] => {
       $crate::hierarchy::Children::spawn(($($crate::spawn::Spawn($child)),*))
    };
}

Children的定义如下,是组件Component,也是关系目标relationship_target:子实体.

#[derive(Component, Default, Debug, PartialEq, Eq)]
#[relationship_target(relationship = ChildOf, linked_spawn)]
#[cfg_attr(feature = "bevy_reflect", derive(bevy_reflect::Reflect))]
#[cfg_attr(feature = "bevy_reflect", reflect(Component, FromWorld, Default))]
#[doc(alias = "IsParent")]
pub struct Children(Vec<Entity>);

Children的方法spawn来自SpawnRelated特型. 翻译一下就是:我有子实体,我就能spawn子实体.

pub trait SpawnRelated: RelationshipTarget {...}

impl<T: RelationshipTarget> SpawnRelated for T {
    fn spawn<L: SpawnableList<Self::Relationship>>(
        list: L,
    ) -> SpawnRelatedBundle<Self::Relationship, L> {
        SpawnRelatedBundle {
            list,
            marker: PhantomData,
        }
    }
}

至于子实体spawn的时机,就在于apply这里,入参是world和id,此id是父实体的Entity,后面会用到:

pub struct SpawnRelatedBundle<R: Relationship, L: SpawnableList<R>> {
    list: L,
    marker: PhantomData<R>,
}

impl<R: Relationship, L: SpawnableList<R>> BundleEffect for SpawnRelatedBundle<R, L> {
    fn apply(self, entity: &mut EntityWorldMut) {
        let id = entity.id();
        entity.world_scope(|world: &mut World| {
            self.list.spawn(world, id);
        });
    }
}

SpawnableList特型的其中一个实现如下,不继续往下跟踪了,接下来对比一下with_children:

pub struct SpawnWith<F>(pub F);

impl<R: Relationship, F: FnOnce(&mut RelatedSpawner<R>) + Send + Sync + 'static> SpawnableList<R>
    for SpawnWith<F>
{
    fn spawn(self, world: &mut World, entity: Entity) {
        world.entity_mut(entity).with_related_entities(self.0);
    }

    fn size_hint(&self) -> usize {
        1
    }
}

常用的with_children如下,和上面的SpawnWith.spawn对比一下, 不难发现两者都是创建子实体(ChildOf,子实体).

impl<'a> EntityCommands<'a> {
    /// Spawns children of this entity (with a [`ChildOf`] relationship) by taking a function that operates on a [`ChildSpawner`].
    pub fn with_children(
        &mut self,
        func: impl FnOnce(&mut RelatedSpawnerCommands<ChildOf>),
    ) -> &mut Self {
        self.with_related_entities(func);
        self
    }

    pub fn with_related_entities<R: Relationship>(
        &mut self,
        func: impl FnOnce(&mut RelatedSpawnerCommands<R>),
    ) -> &mut Self {
        let id = self.id();
        func(&mut RelatedSpawnerCommands::new(self.commands(), id));
        self
    }
}

至于父实体的Children组件的填充,肯定是在子实体创建之后通过Hook再进行填充的.