父子层级结构

可参考的官方例子: hierarchy, parenting.


从技术上讲,实体/组件 本身不能形成一个层级结构(ECS 是一个一维平面数据结构)。然而,逻辑层级结构是游戏中的一种常见模式。

Bevy 支持在实体之间建立这样的逻辑联系:以形成一个虚拟的 "层级",只需在各自的实体上添加 ParentChildren 组件即可。

当使用 Commands 来生成实体时,Commands 有向实体添加子对象的方法,这个方法同时可以为子对象自动添加正确的组件。

// spawn the parent and get its Entity id
    let parent = commands.spawn_bundle(MyParentBundle::default())
        .id();

// do the same for the child
    let child = commands.spawn_bundle(MyChildBundle::default())
        .id();

// add the child to the parent
    commands.entity(parent).push_children(&[child]);

// you can also use `with_children`:
    commands.spawn_bundle(MyParentBundle::default())
        .with_children(|parent| {
            parent.spawn_bundle(MyChildBundle::default());
        });

你可以通过一条 command 命令将某个层级包含的所有实体全部销毁:

fn close_menu(
    mut commands: Commands,
    query: Query<Entity, With<MainMenuUI>>,
) {
    for entity in query.iter() {
        // despawn the entity and its children
        commands.entity(entity).despawn_recursive();
    }
}

访问父对象或子对象

为了开发一个能与层级结构一起工作的 system 函数,你通常需要两个Query查询参数:

  • 一个用来访问包含你想要组件的子实体
  • 一个用来访问包含你想要组件的父实体

在这两个查询参数中,一个参数应该包含适当的组件(Component),获得实体的 ID,以便与另一个参数查询到的结果一起使用:

  • 如果你想遍历实体并访问它们的父对象,在 child 查询参数类型定义中包含 Parent,或
  • 如果你想遍历实体并访问它们的子对象,在 parent 查询参数类型定义中包含 Children

例如,如果我们想获得有父对象的摄相机(Camera)的 Transform,以及它们父对象的 GlobalTransform

fn camera_with_parent(
    q_child: Query<(&Parent, &Transform), With<Camera>>,
    q_parent: Query<&GlobalTransform>,
) {
    for (parent, child_transform) in q_child.iter() {
        // `parent` contains the Entity ID we can use
        // to query components from the parent:
        let parent_global_transform = q_parent.get(parent.0);

        // do something with the components
    }
}

再举个例子,比如我们在开发一个策略游戏,我们有一群作战单元是隶属于同一个小分队的子对象。假设我们需要编写一个对每个小分队起作用的系统,这个系统需要访问关于子对象的一些信息:

fn process_squad_damage(
    q_parent: Query<(&MySquadDamage, &Children)>,
    q_child: Query<&MyUnitHealth>,
) {
    // get the properties of each squad
    for (squad_dmg, children) in q_parent.iter() {
        // `children` is a collection of Entity IDs
        for &child in children.iter() {
            // get the health of each child unit
            let health = q_child.get(child);

            // do something
        }
    }
}

相对变换

如果你的实体代表某种 "游戏世界中的对象",你可能希望子对象能相对于父对象定位,并随其移动。

Bevy 内置 Bundles 创建的对象,都会自动为它们提供了上面提到的父子相对定位行为。

如果你没有使用这样的 Bundle,你需要确保在父对象和子对象中都添加这些组件:GlobalTransformTransform

Transform 表示相对位置,你可以直接修改它。

GlobalTransform 代表绝对位置,它是由 bevy 内部管理的,不要自己修改和维护它。

更多信息,请访问关于变换的专门页面。