状态: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_materialbevy_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_widgetsexperimental_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_indexsection_index字段重命名
PositionedGlyph::byte_index / byte_length移除有使用需检查

聚焦行为改进

FocusGained 事件现在带 FocusCause 枚举(Pressed / Navigated),可以区分「鼠标点击」和「Tab 导航」进来的聚焦。单行输入框在 Navigated 时自动全选文本——跟 HTML 行为对齐。

之前直接 match 或反序列化 FocusGained 的代码需要更新。

五、Audio 与 Feature Flag 清洗

Audio

Rodio 更新到 0.22。几个重要的 feature flag 变化:

  • audio feature 不再被 3d2dui 隐式包含。如果代码中用到了 audio,需要在 Cargo.toml 里显式加 bevy/audio
  • ui feature 也不再被 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_windowbevy_input_focuscustom_cursor 从默认 feature 搬到可选 feature 集合。UiWidgetsPluginsInputDispatchPlugin 现在是 DefaultPlugins 的一部分。

六、ECS 与工具链

  • bevy_scenebevy_world_serialization:旧 scene crate 重命名。import 路径要更新。
  • Render Graph as Systems:渲染图从节点图架构迁移到系统驱动。现有 RenderGraph API 仍然兼容,但内部架构变了。
  • PanCamera 组件:新增鼠标拖拽平移功能,编辑器原型更方便。
  • set_executor 取代 ExecutorKind:多线程执行器配置方式变更。
  • System::type_id()System::system_type():避免与 std::any::type_id 冲突。
  • Ref 直接实现 Clone + Copy:之前需要手动 clone。
  • AssetServer Builder 模式:高级加载选项现在通过 AssetServer::load().with_settings(...) 构建。
  • MorphTarget 存储在 mesh 中:结构体重新整理。
  • define_atomic_id 移至 bevy_utils
  • World::entities_allocatorWorld::entity_allocator

七、迁移清单(从 0.18 升级)

# Cargo.toml — 先确认 feature
[dependencies]
bevy = { version = "0.19", features = [
    "3d",       # 或 "2d"
    "audio",    # ← 如果用到 audio,之前被隐式包含
    "ui",       # ← 如果用到 UI,之前被隐式包含
] }

需要立即处理的 breaking changes

  1. audio / ui feature 检查 — 如果不显式启用,Audio 和 UI 功能会静默消失
  2. FocusGained 匹配代码更新 — 加了 FocusCause 字段
  3. TextLayout 构造函数改名new_with_* → 去掉前缀
  4. Font::try_from_bytes()Font::from_bytes() — 简单替换
  5. System::type_id()System::system_type() — 如果在自定义系统中用过
  6. bevy_scenebevy_world_serialization — import 路径更新
  7. Resource 元数据变更 — 如果用了 #[reflect(Resource)],检查更新
  8. WgpuSettingsPriority::CompatibilityWebGPU — 如果在配置中写过
  9. Broad Query 过滤 — 如果发现多余的实体,加 Without<Resource> 过滤

建议尽早处理的

  • Feature flag 清洗:扫一遍 Cargo.tomlfeatures 列表,确认 0.19 的隐含关系
  • shadow_pass 检查:如果有自定义渲染代码用到 shadow pass,了解它已被拆分
  • BSN 语法检查:如果在 rc.1 阶段试过 BSN,@ / ~ / : 的语义变了
  • PbrNeutralKhronosPbrNeutral:代码中的 tonemapper 引用需要更新
  • Image::pixel_bytes() 改为返回 Result:错误处理要补上

这次 0.19 的跨度确实比 0.18 大——从架构到拆 crate 再到 UI 重构,改动面很广。好消息是核心 ECS 接口(Res / ResMut)保持了向后兼容,大部分日常代码不需要动。

下一阶段的博客我会开始跟进 0.20 的动态(BSN 资产格式、动态场景加载等),同时继续黄金矿工系列的 Stage 4+。

相关链接