Skip to content

命名引用与层级

bsn! 提供 #Name 语法来命名实体、建立引用关系,用 Children 构建层级树。

#Name 引用

#Name 做两件事:

  1. 给实体添加 Name("Name") 组件
  2. 在当前 bsn! 作用域内注册该实体,允许其他位置通过 #Name 引用它
rust
fn setup(world: &mut World) {
    world.spawn_scene(bsn! {
        #Player
        Health(100)
        Children [
            #Minion
            Health(30)
            TargetEntity { target: #Player }
        ]
    }).unwrap();

    let mut query = world.query::<(&Name, &Health)>();
    for (name, health) in query.iter(world) {
        println!("  {} HP {}", name, health.0);
    }

    let minion = world
        .query_filtered::<Entity, With<TargetEntity>>()
        .single(world)
        .unwrap();
    let target = world.get::<TargetEntity>(minion).unwrap();
    let target_name = world.get::<Name>(target.target).unwrap();
    println!("  Minion 的目标是:{}", target_name);
}

Listing 32-4:#Name 引用与跨实体关联

运行:

console
cargo run -p ch32-scenes --example listing-32-04
text
  Player HP 100
  Minion HP 30
  Minion 的目标是:Player

TargetEntity { target: #Player } 里的 #Player 会解析为 Entity,由 FromTemplate 自动完成 EntityTemplate → Entity 的转换。子实体可以引用父实体,父实体也可以引用子实体——引用方向不受层级限制。

Children

Children [...] 把列表中的场景作为子实体生成。逗号分隔不同子实体,空格分隔同一子实体的组件:

rust
bsn! {
    #Parent
    Children [
        #Child1 ComponentA,
        #Child2 ComponentB,
    ]
}

用括号可以更清晰地分隔:

rust
bsn! {
    #Parent
    Children [
        (#Child1 ComponentA),
        (#Child2 ComponentB ComponentC),
    ]
}

嵌套层级

Children 可以嵌套,构建任意深度的树:

rust
bsn! {
    #Root
    Children [
        #Branch
        Children [
            #Leaf1,
            #Leaf2,
        ]
    ]
}

bsn_list!

bsn_list! 生成多个根实体,它们共享同一个命名作用域。这在需要兄弟实体互相引用时很有用:

rust
fn linked_pair() -> impl SceneList {
    bsn_list![
        (#Left  Link(#Right)),
        (#Right Link(#Left)),
    ]
}

bsn_list! 的每个条目用逗号分隔,每个条目生成一个独立实体。所有条目共享命名作用域——#Left#Right 可以互相引用。

main.rs 的完整示例中,bsn_list! 用于同时生成玩家和多个敌人:

rust
fn setup(world: &mut World) {
    world.spawn(Camera2d);

    // 用 bsn_list! 生成多个根实体,共享命名作用域
    world.spawn_scene_list(bsn_list![
        (
            player()
            Transform::from_xyz(-200.0, 0.0, 0.0)
        ),
        (
            enemy("史莱姆", 30, 5)
            Transform::from_xyz(100.0, 50.0, 0.0)
        ),
        (
            enemy("骷髅兵", 60, 12)
            Transform::from_xyz(100.0, -50.0, 0.0)
        ),
    ]).unwrap();

    println!("场景已生成");
}

Listing 32-7(节选):bsn_list! 生成多个根实体

作用域规则

  • 每个 bsn! 调用创建独立的命名作用域
  • #Name 对根实体、子实体、孙实体都可见——上下都能引用
  • 组合进来的场景函数有各自的作用域,同名不会冲突
  • bsn_list! 的所有条目共享一个作用域
  • EntityTemplate 只在生成期间有效,不要存进组件字段

动态名称

#Name 只接受标识符。需要运行时动态名称时,先用 #Name 注册引用,再用 Name(...) 覆盖:

rust
let label = format!("Enemy_{}", id);
bsn! {
    #Enemy
    Name({label})
    Health(100)
}

#Enemy 注册引用名,Name({label}) 用动态值覆盖显示名。引用仍然用 #Enemy