ARTICLE DETAIL

资讯详情

深耕网站视觉设计与运营推广的一线实战洞察。

Bevy BSN 语法改进迁移指南:统一 `@` 场景引用前缀,全面告别 `template_value`

Bevy BSN 语法改进迁移指南:统一 `@` 场景引用前缀,全面告别 `template_value` Bevy BSN 语法改进迁移指南统一场景引用前缀全面告别template_value【免费下载链接】bevyA refreshingly simple>项目地址: https://gitcode.com/GitHub_Trending/be/bevyBSN 是 Bevy 场景系统中一套用于在 Rust 代码里声明式组合场景Scene的 DSL由bsn!与bsn_list!宏驱动本仓库中真实用法可参见 examples/scene/bsn.rs。本指南围绕迁移文档 _release-content/migration-guides/bsn_syntax_improvements.md对应 PR #25318讲解本周期对 BSN 语法的人机工效ergonomics与可读性改进所有场景引用统一改用前缀、组件值不再需要template_value包装、枚举不再依赖VariantDefaults/FromTemplate。读完本文你将能够把既有 BSN 声明平滑迁移到新语法并理解这些语法收紧背后的设计取舍。背景为什么 BSN 要“改语法”BSN 在落地之初带有一批在实践中造成摩擦的“怪癖”idiosyncrasies。最典型的问题有两个场景引用scene reference与组件/模板值在写法上缺乏显式区分导致解析与阅读都需要额外上下文template_value包装器无处不在编写“组件值是某个计算表达式/函数调用结果”的场景时冗长且啰嗦。本次改动就是要在不影响表达能力的前提下把这两类负担消除掉。语法层面的总方向是用统一的前缀把“引用了另一个场景”这件事写得明明白白从而释放出空间让组件值以裸 Rust 表达式的形式直接书写。从实现侧看bsn!宏目前位于 crates/bevy_scene/macros/src/bsn/mod.rs其中bsn()/bsn_list()分别解析BsnRoot/BsnListRoot语法树并调用 codegen 输出spawn代码而“语法规则长什么样”由 crates/bevy_scene/macros/src/bsn/parse.rs 解析器定义约 parse.rs 第 100-155 行 是BsnEntry的条目分发逻辑。改动一所有场景引用统一使用前缀语法对比新规则非常简单只要某个条目引用的是“场景”而不是组件或资源值就必须用开头无论它是场景变量、场景函数调用还是任意表达式// Before bsn! { scene_variable scene_function() {scene_expression} } // After bsn! { scene_variable scene_function() {scene_expression} }三条对应关系分别为引用形式旧写法新写法场景变量scene_variablescene_variable场景函数调用scene_function()scene_function()任意场景表达式{scene_expression}{scene_expression}源码印证与真实用例在 parse.rs 中可以看到当前解析器对以Attoken开头的条目会解析为一个独立的场景引用类型同时缓存场景仍使用:前缀且解析器只允许其为资源场景——否则会直接报错提示Caching is currently only supported for asset scenes. Consider replacing : with 这恰好说明前缀的作用就是把“场景引用”从其他 BSN 条目中干净地区分出来避免与后面的组件裸写法产生歧义。仓库内 examples/scene/bsn.rs 已经全面采用新语法可作为迁移后的参考样板fn scene() - impl SceneList { bsn_list![Camera2d, ui()] // 场景函数调用ui() } fn ui() - impl Scene { bsn! { Node { width: percent(100), height: percent(100), ... } Children [ button(Ok) // 带参数的场景函数调用button(Ok) on(|_event: OnPointerPress| println!(Ok pressed!)), ... ] } } fn button(label: str) - impl Scene { bsn! { Button Node { width: px(150), ... } ... Children [( Text(label) TextFont { font: FontSourceTemplate::Handle(fonts/FiraSans-Bold.ttf), font_size: px(33.0), } ... )] } }注意其中Camera2d、Button、Node、BackgroundColor(...)等裸类型/构造函数都是组件而非场景引用因此不需要只有ui()、button(Ok)这类“把另一个场景函数/变量挂进来”的位置需要。迁移操作通读每个bsn!/bsn_list!声明块找出引用其它场景的条目——特征是旧代码中它们原本可能混在组件中间、或写在Children [...]/SceneList列表里为每条场景引用补上前缀表达式形式的再加一层花括号{...}。改动二组件值直接裸写不再需要template_value包装语法对比正因为已经承担了“场景引用”的全部职责普通裸标识符就只剩下“组件”一种含义于是组件值的写法可以大幅简化// Before bsn! { template_value(component_variable) template_value(component_function()) } // After bsn! { component_variable component_function() }此前你需要手动把“作为值的表达式”包进template_value(...)而现在bsn!内部会把裸表达式统一解析为组件值。从代码库看template_value相关的模板机制仍集中在 crates/bevy_ecs/src/template.rs 及其宏crates/bevy_ecs/macros/src/template.rs中但BSN 声明层已不再要求你书写这层包装。迁移操作把 BSN 声明里所有template_value(...)的括号剥掉、保留括号内的裸表达式即可。下面几类典型场景逐一说明。改动三枚举组件不再要求VariantDefaults与FromTemplate枚举定义简化此前在 BSN 中直接写枚举变体需要让枚举实现额外的VariantDefaults、FromTemplate。现在只要枚举本身实现Default与Clone作为组件通常还带#[derive(Component)]即可// Before #[derive(Component, Default, Clone, VariantDefaults)] enum Foo { A { x: u32, y: u32 }, #[default] B, } bsn! { Foo::B } // After #[derive(Component, Default, Clone)] enum Foo { A { x: u32, y: u32 }, #[default] B, } bsn! { Foo::B }因此如果之前是为了在 BSN 里使用某个不支持VariantDefaults的枚举才给它包上template_value现在可以直接去掉这层包装// Before bsn! { template_value(Foo::A) } // After bsn! { Foo::A }为什么不再支持“变体默认值”模式迁移文档明确指出所谓 “variant defaults” 模式其实现方式是为每个枚举变体单独生成一个“default 构造器”例如VariantDefaults与FromTemplate会为上面的Foo生成Foo::a_default()、Foo::b_default()从而支持“只指定部分字段、其余字段自动取默认值”individual enum field value patching。这一模式最终被移除原因是其“怪异程度”以及与 Rust 生态兼容性带来的成本都太高——普通 Rust 本身并不支持“按枚举变体设置默认值”的语法。迁移操作必须写全每个字段迁移后的规则回归普通 Rust枚举变体的每个字段都要显式给出不带..Default::default()之类的简写// Beforey 字段会被自动初始化为默认值 0 bsn! { Foo::A { x: 1 } } // Aftery 字段必须手动补齐 bsn! { Foo::A { x: 1, y: 0 } }在升级代码时需要把此前依赖VariantDefaults隐式补全的字段全部显式写出值取该字段类型的默认值即可。若代码量较大可借助编译器报错逐条补齐缺失字段。改动四Builder 链与任意表达式都直接裸写Builder 模式不再需要包装对“构造组件后再调用一系列方法”的 builder 写法同样可以剥掉template_value// Before bsn! { template_value(Transform::from_xyz(-2.5, 4.5, 9.0).looking_at(Vec3::ZERO, Vec3::Y)) } // After bsn! { Transform::from_xyz(-2.5, 4.5, 9.0).looking_at(Vec3::ZERO, Vec3::Y) }任意 Rust 表达式同样适用只要表达式的结果是组件就可以直接作为组件值例如对某个node调用.clone()// Before bsn! { template_value(node.clone()) } // After bsn! { node.clone() }再进一步任何需要把运行期算出来的值塞进场景的位置都适用此规则。综合 examples/scene/bsn.rs 中的写法可以看到on(|_event: OnPointerPress| ...)这类闭包/处理器也可以作为裸值出现与组件字段并列书写。迁移总览一条可以机械执行的规则把以上改动归纳成一张“操作对照表”迁移时按表执行即可改动点BeforeAfter场景引用scene_variable/scene_function()/{scene_expression}scene_variable/scene_function()/{scene_expression}组件值template_value(component_variable)component_variable组件函数值template_value(component_function())component_function()枚举变体#[derive(..., VariantDefaults)]Foo::B#[derive(...)]只需DefaultCloneFoo::B不支持 VariantDefaults 的枚举template_value(Foo::A)Foo::A枚举变体字段Foo::A { x: 1 }缺省字段自动补默认值Foo::A { x: 1, y: 0 }字段必须写全Builder 表达式template_value(Transform::from_xyz(...).looking_at(...))Transform::from_xyz(...).looking_at(...)任意表达式template_value(node.clone())node.clone()迁移文档给出的总结同样适用你现在应该可以从所有 BSN 声明中移除全部template_value实例了。这是本次改动的最大收益——BSN 声明中的“值”与“场景”各归其位、一眼可辨场景树写起来与普通 Rust 表达式的习惯高度一致。验证迁移结果迁移完成后建议从以下两条路径验证代码行为正确、且充分利用了新语法阅读并对照官方示例examples/scene/bsn.rs 同时演示了bsn_list!返回impl SceneList与bsn!返回impl Scene的新写法并通过scene.spawn()把场景挂进Startup系统场景被实例化后再叠加组件与子场景的写法如button(Ok)之后紧跟on(...)也是合法的 BSN 组合方式。借助仓库内基准与测试确认运行路径场景模块的 spawn 相关基准位于 benches/benches/bevy_scene/spawn.rsBSN 宏的解析与代码生成实现在 crates/bevy_scene/macros/src/bsnparse.rs负责语法、codegen.rs负责展开、types.rs定义中间类型排查语法问题时可定位到这些文件。最后提醒一点边界本次改动属于 BSN 语法层面的破坏性变更如果依赖旧语法无的场景引用、template_value包装、VariantDefaults部分字段补全迁移时编译器会直接报解析/宏展开错误。因此建议一次性、系统性完成上述替换而不是新旧混写——混合写法在阅读上反而容易让“组件”与“场景引用”重新变得难以区分这也正是本次语法收紧想要根除的摩擦来源。【免费下载链接】bevyA refreshingly simple>项目地址: https://gitcode.com/GitHub_Trending/be/bevy创作声明:本文部分内容由AI辅助生成(AIGC),仅供参考
返回列表