状态:0.19 已正式发布(2026-06-18),本文基于实际发布版本撰写。 原前瞻草稿(2026-05-12)已替换为正式发布版解读。
Bevy 0.19 在今天(2026-06-18)正式发布了。三个 RC 版本(rc.1 May 13 → rc.2 May 22 → rc.3 Jun 10)之后,543 个 PR 合并,终于出了稳定版。
这个版本的跨度比 0.18 大得多。有架构级的变更,有新 crate 分裂,也有大量渲染和跨平台修复。这篇文章按主题整理,把每个变化的上下文和实际影响说清楚。
如果你只想快速升级到 0.19,可以直接跳到最后一节看迁移清单。
一、Resources as Components — 0.19 最大的架构变更
这是 0.19 最深度的改动,说它是最重要的也不为过。
之前:Resource 和 Component 是两条不同的 ECS 轨道。Resource 存在独立的资源存储区,Component 存在实体上。
之后:#[derive(Resource)] 自动实现了 Component trait。Resource 存储为单例实体(Singleton Entity)上的组件。底层存储变了,但 Res<T> / ResMut<T> 参数仍然可以正常用——这是刻意保留的兼容层。
// 0.18 写法 —— 仍能编译
#[derive(Resource)]
struct Score(u32);
fn update_score(mut score: ResMut<Score>) {
score.0 += 1;
}
// 0.19 等价于注入了 Component
// 所以现在也能用 Query 查它……
fn cheat_query(q: Query<&Score>) {
// 但你真的不应该这么做
}实际影响
#[derive(Resource)]的字段类型现在也需要满足Component的 trait bounds。如果你的 Resource 里放了不支持Component的类型,编译会报错。- Broad Query(如
Query<&Transform>)现在会返回 Resource 单例实体,需要加Without<Resource>过滤。 #[reflect(Resource)]的 API 有变化。- Non-Send Resource 改名了:
NonSend<T>→NonSendData<T>。 World::register_resource被废弃——不再需要注册 Resource。
对普通项目的影响:如果你的项目只用 Res<T> / ResMut<T> 访问 Resource,几乎感受不到变化。但如果查了一次 Query<&Transform> 发现多了几个奇怪的实体,就知道原因了。
二、BSN(Bevy Scene Notation)正式落地
BSN 是 0.19 声量最大的特性。从去年开始就在画饼,这次终于进了稳定版。
核心思想很简单:之前 spawn 一个复杂实体需要写一堆 commands.spawn((...)).with_children(|p| {...}),BSN 让你用声明式语法表达:
use bevy::scene::bsn;
commands.spawn(bsn! {
Node::default()
Text("Hello".to_string())
Children [
Node::default()
Text("World".to_string())
]
});语法变化(比 RC.1 时改了)
BSN 在 RC 阶段经历了一次语法重写(#24367):
| 旧语法 (rc.1) | 新语法 (rc.2+) | 含义 |
|---|---|---|
@Template | ~Template | 模板引用/合并 |
:SceneComponent | @SceneComponent | 场景组件继承 |
:(单独) | 不变 | 标记可缓存 |
如果你在 rc.1 阶段试过 BSN,需要扫一遍文中所有的 @ 和 ~ 用法。
Scene Components
配合 BSN 一起落地的还有 SceneComponent trait:让一个组件自带「出厂配置」的场景实体。
#[derive(SceneComponent, Default, Clone)]
struct Player {
score: usize,
}
impl Player {
fn scene() -> impl Scene {
bsn! {
Transform::default()
Children [
#RightHand
]
}
}
}
// 用 @Player 继承语法时,自动展开场景
world.spawn_scene(bsn! {
@Player { score: 0 }
});注意:commands.spawn(Player::default()) 不会展开场景,并且会在运行时记录错误。必须用 world.spawn_scene() + @ 继承语法。
三、渲染大修:新 crate、Contact Shadows、Occlusion Culling 稳定
bevy_material — 材质系统独立
bevy_material 从 bevy_pbr 中拆出,成为独立 crate。自定义材质的接口没变(AsBindGroup + TypePath + Asset + Material trait),但如果你想在 bevy_render 级别使用材质,现在需要显式启用 bevy/bevy_material feature。
Contact Shadows
新的接触阴影实现。之前需要 SSR 或 shadow map 才能处理紧密几何体之间的阴影,现在原生支持。
Occlusion Culling 正式稳定
不再标记为 experimental。默认启用,通过 GPU 裁剪看不见的物体来提升场景渲染性能。
Atmosphere + Skybox → bevy_light
光照相关模块做了统一整合:大气渲染和天空盒都搬到了新的 bevy_light crate。
Skybox图像现在可选EnvironmentMapUniform被移除,旋转数据合并进LightProbesUniform- Bloom 亮度计算改为线性空间,结果更准确
- Light gizmo 也从
bevy_gizmos移到了bevy_light
Solari 实时光追
路径追踪器 Solari 在 0.19 也有一波改进:专用镜面 BRDF、主表面替换、更好的路径终止策略。如果你在关注渲染管线的高级方向,JMS55 的系列博客值得读。
其他渲染变更
- 阴影通道拆分为
per_view_shadow_pass+shared_shadow_pass shadow_pass相关的自定义渲染代码需要适配PlaneMeshBuilder支持 X/Z 方向不同细分- 顶点缓冲区压缩(#21926):法线/切线用八面体编码压缩到 Unorm16,UV/权重用 Float16——减少 GPU 上传带宽,对网格密集场景有明显收益
- WebGL2 3D 场景修复:mesh view bind group 改为按需创建
四、UI / 文本系统重构
Feathers UI 正式稳定
experimental_ui_widgets 和 experimental_bevy_feathers 不再标记为 experimental。Feathers UI widget(滚动条、列表视图、颜色选择器等)现在随 DefaultPlugins 默认启用。
文本系统
文本模块做了比较彻底的整理:
| 旧 | 新 | 影响 |
|---|---|---|
TextRoot / TextSpanAccess / TextSpanComponent | 统合进 TextSection | 大改 |
TextLayout::new_with_justify() | TextLayout::justify() | 方法重命名 |
TextLayout::new_with_linebreak() | TextLayout::linebreak() | 方法重命名 |
Font::try_from_bytes() | Font::from_bytes() | 函数重命名 |
PositionedGlyph::span_index | section_index | 字段重命名 |
PositionedGlyph::byte_index / byte_length | 移除 | 有使用需检查 |
聚焦行为改进
FocusGained 事件现在带 FocusCause 枚举(Pressed / Navigated),可以区分「鼠标点击」和「Tab 导航」进来的聚焦。单行输入框在 Navigated 时自动全选文本——跟 HTML 行为对齐。
之前直接 match 或反序列化 FocusGained 的代码需要更新。
五、Audio 与 Feature Flag 清洗
Audio
Rodio 更新到 0.22。几个重要的 feature flag 变化:
audiofeature 不再被3d、2d或ui隐式包含。如果代码中用到了 audio,需要在Cargo.toml里显式加bevy/audio。uifeature 也不再被3d/2d隐式包含。
这意味着:
# 0.18 —— audio 和 ui 被 3d 隐式带着
bevy = { version = "0.19", features = ["3d"] }
# 0.19 —— 如果用 audio 或 ui,要显式写出来
bevy = { version = "0.19", features = ["3d", "audio", "ui"] }其他 Feature 迁移
bevy_window、bevy_input_focus、custom_cursor 从默认 feature 搬到可选 feature 集合。UiWidgetsPlugins 和 InputDispatchPlugin 现在是 DefaultPlugins 的一部分。
六、ECS 与工具链
bevy_scene→bevy_world_serialization:旧 scene crate 重命名。import 路径要更新。- Render Graph as Systems:渲染图从节点图架构迁移到系统驱动。现有
RenderGraphAPI 仍然兼容,但内部架构变了。 PanCamera组件:新增鼠标拖拽平移功能,编辑器原型更方便。- set_executor 取代 ExecutorKind:多线程执行器配置方式变更。
System::type_id()→System::system_type():避免与std::any::type_id冲突。Ref直接实现Clone+Copy:之前需要手动 clone。AssetServerBuilder 模式:高级加载选项现在通过AssetServer::load().with_settings(...)构建。MorphTarget存储在 mesh 中:结构体重新整理。define_atomic_id移至bevy_utils。World::entities_allocator→World::entity_allocator。
七、迁移清单(从 0.18 升级)
# Cargo.toml — 先确认 feature
[dependencies]
bevy = { version = "0.19", features = [
"3d", # 或 "2d"
"audio", # ← 如果用到 audio,之前被隐式包含
"ui", # ← 如果用到 UI,之前被隐式包含
] }需要立即处理的 breaking changes
audio/uifeature 检查 — 如果不显式启用,Audio 和 UI 功能会静默消失FocusGained匹配代码更新 — 加了FocusCause字段TextLayout构造函数改名 —new_with_*→ 去掉前缀Font::try_from_bytes()→Font::from_bytes()— 简单替换System::type_id()→System::system_type()— 如果在自定义系统中用过bevy_scene→bevy_world_serialization— import 路径更新Resource元数据变更 — 如果用了#[reflect(Resource)],检查更新WgpuSettingsPriority::Compatibility→WebGPU— 如果在配置中写过- Broad Query 过滤 — 如果发现多余的实体,加
Without<Resource>过滤
建议尽早处理的
- Feature flag 清洗:扫一遍
Cargo.toml的features列表,确认 0.19 的隐含关系 - shadow_pass 检查:如果有自定义渲染代码用到 shadow pass,了解它已被拆分
- BSN 语法检查:如果在 rc.1 阶段试过 BSN,
@/~/:的语义变了 PbrNeutral→KhronosPbrNeutral:代码中的 tonemapper 引用需要更新Image::pixel_bytes()改为返回Result:错误处理要补上
这次 0.19 的跨度确实比 0.18 大——从架构到拆 crate 再到 UI 重构,改动面很广。好消息是核心 ECS 接口(Res / ResMut)保持了向后兼容,大部分日常代码不需要动。
下一阶段的博客我会开始跟进 0.20 的动态(BSN 资产格式、动态场景加载等),同时继续黄金矿工系列的 Stage 4+。