场景组合与 Patch
BSN 的核心设计目标之一是组合:把小场景拼成大场景,同时只覆盖需要改的字段。
场景函数
用普通 Rust 函数返回 impl Scene,就是可复用的场景片段:
fn enemy() -> impl Scene {
bsn! {
Health { current: 100, max: 100 }
Damage(10)
}
}Listing 32-3(节选):enemy() 场景函数
enemy() 返回一个带 Health { current: 100, max: 100 } 和 Damage(10) 的场景。调用 world.spawn_scene(enemy()) 就能生成一个普通敌人。
字段级 Patch
组合场景时,只需写想覆盖的字段。未提及的字段保留原值:
fn boss() -> impl Scene {
bsn! {
enemy()
Health { max: 500, current: 500 }
Damage(50)
}
}Listing 32-3(节选):boss() 组合并覆盖字段
boss() 先包含 enemy() 的全部内容,再用 Health { max: 500, current: 500 } 和 Damage(50) 覆盖。最终 boss 的属性是 Health { current: 500, max: 500 }、Damage(50)。
完整示例:
fn setup(world: &mut World) {
// 普通敌人:Health { current: 100, max: 100 }, Damage(10)
world.spawn_scene(enemy()).unwrap();
// Boss:从 enemy() 组合,覆盖 Health 和 Damage
world.spawn_scene(boss()).unwrap();
let mut query = world.query::<(&Health, &Damage)>();
for (health, damage) in query.iter(world) {
println!(" HP {}/{}, 伤害 {}", health.current, health.max, damage.0);
}
}Listing 32-3(节选):生成普通敌人和 Boss
运行:
cargo run -p ch32-scenes --example listing-32-03 HP 100/100, 伤害 10
HP 500/500, 伤害 50多次 Patch 合并
同一组件可以被多次 patch,效果叠加:
bsn! {
Button // 插入默认值
Button { width: 200 } // 只改 width
Button { height: 300 } // 只改 height
}最终 Button { width: 200, height: 300 }——两次 patch 各自生效,互不干扰。
Patch 的工作原理
bsn! 对每个组件条目生成一个 Template。当同一个组件类型出现多次时,后面的 Template 会合并到前面的 Template 上——只有显式设置的字段会被覆盖,未设置的字段保留原值。这和"整个组件替换"的普通 ECS 行为不同。
合并顺序:从左到右、从上到下。后面的 patch 覆盖前面的。
组合的传递性
场景函数可以嵌套调用:
fn base() -> impl Scene {
bsn! { Health { current: 100, max: 100 } }
}
fn warrior() -> impl Scene {
bsn! {
base()
Damage(20)
}
}
fn elite_warrior() -> impl Scene {
bsn! {
warrior()
Health { max: 300 }
Damage(40)
}
}elite_warrior() 最终有 Health { current: 100, max: 300 }、Damage(40)——base() 的 current 保留,warrior() 的 Damage(20) 被覆盖。