Latest commit c8a3aaa

Hierarchical(Parent/Child) Entities

相关的官方文档:[hierarchy]、[parenting]


从技术上讲,实体/组件本身不能形成层次结构(它们是扁平数据结构)。但是,逻辑层次结构是游戏中常见的模式,Bevy 支持在实体之间建立有层次的逻辑联系,形成虚拟的 “层次结构”,方法是在实体上添加 Parent 组件和 Children 组件。

可以用 Commands 自带的方法给实体添加子实体,这些方法会自动添加正确的组件:

#![allow(unused)]
fn main() {
// 生成父实体并获取 ID
let parent = commands.spawn_bundle(MyParentBundle::default()).id();

// 生成子实体并获取 ID
let child = commands.spawn_bundle(MyChildBundle::default()).id();

// 把子实体添加到父实体,形成层级关系
commands.entity(parent).push_children(&[child]);

// 也可以用 `with_children`
commands.spawn_bundle(MyParentBundle::default())
    .with_children(|child_builder| {
        cchild_builder.spawn_bundle(MyChildBundle::default());
    });
}

可以用一个命令来销毁实体层级:

#![allow(unused)]
fn main() {
fn close_menu(
    mut commands: Commands,
    query: Query<Entity, With<MainMenuUI>>,
) {
    for entity in query.iter() {
        //  销毁实体和它的所有子级实体
        commands.entity(entity).despawn_recursive();
    }
}
}

访问父实体或子实体

要让系统处理具有层次结构的实体,要用到两个 Query:

  • 一个查询子实体的组件
  • 另一个查询父实体的组件

其中的一个查询应该包含合适的组件,才能获取所需的实体 ID:

  • 如果在查询实体时要获取父实体的数据,就要用 Parent 组件来获取父实体的 ID
  • 如果在查询实体时要获取子实体的数据,就要用 Children 组件来获取子实体的 ID

例如,某个相机有一个父实体,需要获取相机的 Transform 和父实体的 GlobalTransform

#![allow(unused)]
fn main() {
fn camera_with_parent(
    q_child: Query<(&Parent, &Transform), With<Camera>>,
    q_parent: Query<&GlobalTransform>,
) {
    for (parent, child_transform) in q_child.iter() {
        // `parent` 变量包含 Entity 的 ID,可以用这个 ID 来查询父实体的组件
        let parent_global_transform = q_parent.get(parent.0);

        // 做一些事
    }
}
}

再举一个例子,假设我们正在做一个战略游戏,游戏中的某个小分队(父实体)有一些成员(子实体)。为了使某个系统在每个小分队(父实体)上都有效,这个系统需要这些成员(子实体)的信息:

#![allow(unused)]
fn main() {
fn process_squad_damage(
    q_parent: Query<(&MySquadDamage, &Children)>,
    q_child: Query<&MyUnitHealth>,
) {
    // 获取每个小队的属性
    for (squad_dmg, children) in q_parent.iter() {
        // `children` 是实体 ID 的集合
        for &child in children.iter() {
            // 获取每个子实体单位的 “Health”
            let health = q_child.get(child);

            // 做一些事
        }
    }
}
}

相对变换

如果实体代表“游戏世界中的对象”,那么子实体应该相对于父实体定位,并随父实体移动。

所有 Bevy 内置的 Bundle 自动提供“相对变换”功能。如果只要变换,不需要其他东西,可以使用 TransformBundle

更多信息请查看[Transforms and Coordinates]。

变换和可见性传播

如果实体代表“游戏世界中的对象”,那么子实体应该受到父实体的影响:

  • Transform:子实体相对于父实体定位,并随父实体移动
  • Visibility:父实体的可见性会影响子实体的可见性

所有 Bevy 内置 Bundle 都是这样。自定义 Bundle 可以用 SpatialBundle 来添加 TransformVisibility