Skip to content

Template 与 FromTemplate

Template 是 BSN 的底层机制——一个"带 ECS 上下文的构造器"。FromTemplate 把组件和它的 Template 关联起来。

Default + Clone vs FromTemplate

两种方式让组件在 bsn! 里可用:

方式适用场景示例
Default + Clone所有字段都有合理默认值Health(i32)Score(usize)
FromTemplate有字段需要运行时解析Handle<T>Entity 引用

规则:不能同时 derive FromTemplateDefault——两者都会提供 FromTemplate 实现,会冲突。

FromTemplate derive

当组件有 Handle<T> 字段时,必须用 FromTemplate

rust
fn setup(world: &mut World) {
    world.spawn_scene(bsn! {
        #Icon
        SpriteIcon {
            image: "textures/icon.png",
            tint: Color::WHITE,
        }
    }).unwrap();

    println!("已生成带 Handle 的实体");
}

Listing 32-5:FromTemplate 与 Handle 字段

运行:

console
cargo run -p ch32-scenes --example listing-32-05
text
已生成带 Handle 的实体

"textures/icon.png" 是一个字符串字面量,bsn! 宏检测到 image 字段是 Handle<Image> 类型,自动将其转换为 HandleTemplate<Image>。在 Scene::resolve 阶段,HandleTemplate 调用 AssetServer::load 完成实际加载。

HandleTemplate

HandleTemplateHandle<T> 的 Template 实现。它接受字符串路径,在解析时调用 AssetServer::load

rust
// 在 bsn! 里直接写字符串路径
Sprite { image: "player.png" }

// 等价于手写
let handle: HandleTemplate<Image> = "player.png".into();

如果资产已加载,AssetServer::load 返回已有 Handle,不会重复加载。

EntityTemplate

#Name 引用在组件字段中产生 EntityTemplate。组件用 Entity 字段时,FromTemplate 自动把 EntityTemplate 解析为实际 Entity

rust
#[derive(Component, FromTemplate)]
struct TargetEntity {
    target: Entity,  // #Name 引用自动解析为 Entity
}

bsn! {
    #Player
    Children [
        #Minion
        TargetEntity { target: #Player }
    ]
}

FontSourceTemplate

字体资产也有专门的 Template:

rust
bsn! {
    Text("Hello")
    TextFont {
        font: FontSourceTemplate::Handle("fonts/FiraSans-Bold.ttf"),
        font_size: px(24.0),
    }
}

FontSourceTemplate 处理字体路径到 FontSource 的转换。

自定义 Template

对于需要 World 访问的复杂构造,用 template(|ctx| { ... }) 语法:

rust
bsn! {
    #Foo
    template(|ctx| {
        Foo(ctx.resource::<MyAssetCollection>().get("generated_asset_name"))
    })
}

ctxTemplateContext,提供 EntityWorldMut 和命名实体引用。

Template 值注入

已有组件实例可以用 template_value(...) 注入:

rust
let transform = Transform::from_translation(translation);
bsn! {
    #Foo
    template_value(transform)
}

这绕过了 Template 系统,直接插入值。适合已经有构造好的组件实例的场景。