命名引用与层级
bsn! 提供 #Name 语法来命名实体、建立引用关系,用 Children 构建层级树。
#Name 引用
#Name 做两件事:
- 给实体添加
Name("Name")组件 - 在当前
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-04text
Player HP 100
Minion HP 30
Minion 的目标是:PlayerTargetEntity { 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。