Bevy 0.11
发布于 2023 年 7 月 9 日 由 Bevy 贡献者

感谢 166 位贡献者、522 次拉取请求、社区审阅者以及我们 慷慨的赞助商,我们很高兴在 crates.io 上宣布发布 Bevy 0.11!
对于那些不知道的人,Bevy 是一款用 Rust 构建的令人耳目一新的简单数据驱动游戏引擎。您可以查看我们的 快速入门指南 以立即尝试它。它是免费的,并且永远开源!您可以在 GitHub 上获取完整的 源代码。查看 Bevy 资产 以获取社区开发的插件、游戏和学习资源的集合。
要将现有的 Bevy 应用或插件更新到 Bevy 0.11,请查看我们的 0.10 到 0.11 迁移指南.
自从我们几个月前的上一次发布以来,我们添加了 大量 新功能、错误修复和使用体验改进,但以下是其中的一些亮点
- 屏幕空间环境光遮蔽 (SSAO):通过模拟“间接”漫反射光的遮挡来提高场景渲染质量
- 时间抗锯齿 (TAA):一种流行的抗锯齿技术,它使用运动向量将当前帧与过去帧混合在一起以平滑掉伪影
- 变形目标:在预定义状态之间对网格上的顶点位置进行动画处理。非常适合角色定制!
- 鲁棒对比度自适应锐化 (RCAS):智能地锐化渲染,与 TAA 搭配使用效果很好
- WebGPU 支持:Bevy 现在可以使用现代 WebGPU Web API 在网络上更快、更具功能地渲染
- 改进的着色器导入:Bevy 着色器现在支持细粒度的导入和其他新功能
- 视差贴图:材质现在支持可选的深度图,通过对材质纹理进行视差处理,使平面表面具有深度感
- Schedule-First ECS API:更简单、更符合人体工程学的 ECS 系统调度 API
- 立即模式 Gizmo 渲染:轻松高效地渲染 2D 和 3D 形状,用于调试和编辑器场景
- ECS 音频 API:一种更直观、更惯用的音频回放方式
- UI 边框:UI 节点现在可以具有可配置的边框!
- 网格 UI 布局:Bevy UI 现在支持 CSS 风格的网格布局
- UI 性能改进:UI 批处理算法已更改,带来了显著的性能提升
屏幕空间环境光遮蔽 #
拖动此图像进行比较


仅 SSAO
Bevy 现在支持屏幕空间环境光遮蔽 (SSAO)。虽然 Bevy 已经支持来自直接光源的阴影(DirectionalLight
、PointLight
、SpotLight
)通过阴影贴图,Bevy 现在支持来自 间接 漫反射光的阴影,如 AmbientLight
或 EnvironmentMapLight
.
这些阴影通过估计通过屏幕空间深度和法线预处理周围几何体对入射光的阻挡程度,使场景更加“接地”。您可以在新的 SSAO 示例 中试用它。
请注意,将 SSAO 与新添加的时间抗锯齿一起使用会导致质量和降噪的 大幅 提升。
平台支持目前有限 - 目前仅支持 Vulkan、DirectX12 和 Metal。WebGPU 支持将在稍后的日期提供。WebGL 可能不会被支持,因为它没有计算着色器。
特别感谢英特尔及其开源 XeGTAO 项目,该项目在开发此功能方面提供了巨大的帮助。
时间抗锯齿 #
拖动此图像进行比较


除了 MSAA 和 FXAA 之外,Bevy 现在支持时间抗锯齿 (TAA) 作为一种抗锯齿选项。
TAA 通过将新渲染的帧与过去帧混合在一起以平滑图像中的锯齿伪影来工作。TAA 在业界越来越受欢迎,因为它能够掩盖许多渲染伪影:它可以平滑阴影(全局照明和“投射”阴影)、网格边缘、纹理,并减少反射表面上光的镜面锯齿。但是,由于“平滑”效果非常明显,因此有些人更喜欢其他方法。
以下是对 Bevy 支持的每种抗锯齿方法的优点和缺点的快速概览
- 多重采样抗锯齿 (MSAA)
- 在平滑网格边缘(抗几何锯齿)方面做得很好。对镜面锯齿没有帮助。性能成本随三角形数量而变化,在具有许多三角形的场景中表现非常差
- 快速近似抗锯齿 (FXAA)
- 在处理几何和镜面锯齿方面做得相当好。在所有场景中性能成本都很低。结果有点模糊且质量低
- 时间抗锯齿 (TAA)
- 在处理几何和镜面锯齿方面做得非常好。在处理时间锯齿方面做得很好,其中高频细节随着时间的推移或随着您移动相机或事物动画而闪烁。性能成本适中,仅随屏幕分辨率而变化。可能会出现“重影”,其中网格或灯光效果可能会留下随着时间的推移而逐渐消失的轨迹。虽然 TAA 有助于减少时间锯齿,但它也可能会引入额外的时间锯齿,尤其是在远处渲染的薄几何体或纹理细节上。需要 2 视图的额外 GPU 内存,以及启用运动向量和深度预处理。需要准确的运动向量和深度预处理,这会使自定义材质变得复杂
TAA 实现是一系列权衡,并且依赖于易于出错的启发式方法。在 Bevy 0.11 中,TAA 被标记为一项实验性功能,原因如下
- TAA 目前不适用于以下 Bevy 功能:蒙皮、变形目标和视差贴图
- TAA 目前往往会稍微柔化图像,可以通过后期处理锐化来解决
- 我们的 TAA 启发式方法目前不可用户配置(这些启发式方法可能会发生变化和发展)
我们将在未来的版本中继续改进质量、兼容性和性能。如果您遇到任何错误,请报告!
您可以在 Bevy 改进的 抗锯齿示例 中比较我们所有的抗锯齿方法。
鲁棒对比度自适应锐化 #
TAA 和 FXAA 等效果会导致最终渲染变得模糊。锐化后期处理效果可以帮助抵消这种情况。在 Bevy 0.11 中,我们添加了 AMD 的鲁棒对比度自适应锐化 (RCAS) 的端口。
拖动此图像进行比较


请注意,头盔皮革部分的纹理要清晰得多!
变形目标 #
Bevy 自 0.7 版本以来支持 3D 动画。
但它只支持 骨骼 动画。在人行道上留下了一种常见的动画类型,称为 变形目标(又名混合形状、关键形状,以及一大堆其他名称)。这是所有 3D 角色动画的鼻祖!Crash Bandicoot 的跑步循环使用了变形目标。
如今,动画师通常会使用骨骼绑定来进行大幅度移动,并使用变形目标来完善细节动作。
然而,在游戏资产方面,艺术家用于面部和手的复杂骨骼绑定过于沉重。通常,姿势会被“烘焙”到变形姿势中,并且面部表情转换会在引擎中通过变形目标进行处理。
变形目标是一种非常简单的动画方法。获取模型,获得基础顶点位置,移动顶点以创建多个姿势
默认

皱眉

狡黠一笑

将这些姿势存储为基础网格与变体姿势之间的差异,然后在运行时,混合 每个姿势。现在,有了与基础网格的差异,我们可以通过简单地将基础顶点位置添加起来获得变体姿势。
就是这样,变形目标着色器看起来像这样
fn morph_vertex(vertex: Vertex) {
for (var i: u32 = 0u; i < pose_count(); i++) {
let weight = weight_for_pose(i);
vertex.position += weight * get_difference(vertex.index, position_offset, i);
vertex.normal += weight * get_difference(vertex.index, normal_offset, i);
}
}
在 Bevy 中,我们将每个姿势的权重存储在 MorphWeights
组件中。
fn set_weights_system(mut morph_weights: Query<&mut MorphWeights>) {
for mut entity_weights in &mut morph_weights {
let weights = entity_weights.weights_mut();
weights[0] = 0.5;
weights[1] = 0.25;
}
}
现在假设我们有两个变形目标,(1) 皱眉姿势,(2) 狡黠一笑姿势
[0.0, 0.0]
默认姿势

[1.0, 0.0]
仅皱眉

[0.0, 1.0]
仅狡黠一笑

[0.5, 0.0]
半皱眉

[1.0, 1.0]
两者都达到最大值

[0.5, 0.25]
两者都有点

虽然概念很简单,但它需要向 GPU 传输大量数据。成千上万个顶点,每个 288 位,多个模型变体,有时甚至多达一百个。
我们将顶点数据存储为 3D 纹理中的像素。这使得变形目标不仅可以在 WebGPU 上运行,还可以使用 WebGL2 wgpu 后端运行。
可以通过多种方式改进,但对于初始实现已经足够了。
视差贴图 #
Bevy 现在支持视差贴图和深度图。视差贴图在赋予材质“深度感”方面让法线贴图相形见绌。这段视频的上半部分使用视差贴图加上法线贴图,而下半部分只使用法线贴图。
注意,不仅仅是像素的阴影在改变,它们在屏幕上的实际位置也在改变。山顶隐藏了它们背后的山脊。高山比沿海地区移动得更快。
视差贴图根据几何体表面上的透视和深度移动像素。为平面添加真正的 3D 深度。
所有这一切,都没有向几何体添加任何顶点。整个地球只有 648 个顶点。与更原始的着色器(如置换贴图)不同,视差贴图只需要一个额外的灰度图像,称为 depth_map
。
游戏经常使用视差贴图来模拟鹅卵石或砖墙,所以让我们在 Bevy 中创建一个砖墙!首先,我们生成一个网格。
commands.spawn(PbrBundle {
mesh: meshes.add(shape::Box::new(30.0, 10.0, 1.0).into()),
material: materials.add(StandardMaterial {
base_color: Color::WHITE,
..default()
}),
..default()
});
当然,它只是一个扁平的白色盒子,我们没有添加任何纹理。所以让我们添加一个法线贴图。
normal_map_texture: Some(assets.load("normal_map.png")),
这要好得多。阴影也会根据光线方向而改变!但是,角落的镜面高光过于强烈,几乎很吵。
让我们看看深度图如何帮助我们。
depth_map: Some(assets.load("depth_map.png")),
我们消除了噪音!还有一种令人想起 90 年代游戏预渲染过场动画的甜蜜的 3D 感觉。
那么,到底发生了什么,为什么视差贴图消除了墙上的难看的镜面光呢?
这是因为视差贴图将砖块之间的脊线内缩,这样它们就会被砖块本身遮挡。
由于法线贴图不会“移动”阴影区域,只是用不同的方式对它们进行阴影处理,所以我们得到了那些奇怪的镜面高光。使用视差贴图,它们消失了。
拖动此图像进行比较


Bevy 中的视差贴图仍然非常有限。最痛苦的一点是它不是标准的 glTF 特性,这意味着如果材质来自 GLTF 文件,则需要以编程方式将深度纹理添加到材质中。
此外,视差贴图与时间抗锯齿着色器不兼容,在曲面上效果不佳,并且不会影响对象的轮廓。
然而,这些并不是视差贴图的根本限制,将来可能会得到解决。
天空盒 #
Bevy 现在内置支持将 HDRI 环境显示为场景背景。
只需将新的 Skybox
组件附加到您的 Camera
。它与现有的 EnvironmentMapLight
配合良好,后者将使用环境贴图来照亮场景。
我们还计划在将来添加对内置程序化天空盒的支持!
WebGPU 支持 #
Bevy 现在支持在 Web 上进行 WebGPU 渲染(除了 WebGL 2)。WebGPU 支持仍在不断推出,但是如果您拥有 支持的 Web 浏览器,您可以探索我们新的 实时 WebGPU 示例 页面。
什么是 WebGPU? #
WebGPU 是一个 令人兴奋的新 Web 标准,用于进行现代 GPU 图形和计算。它从 Vulkan、Direct3D 12 和 Metal 中汲取灵感。事实上,它通常在这些 API 之上实现。WebGPU 使我们能够访问比 WebGL2 更多的 GPU 功能(例如计算着色器),并且也有可能更快。这意味着 Bevy 的更多原生渲染器功能现在也适用于 Web。它还使用新的 WGSL 着色器语言。我们对 WGSL 随着时间的推移而发展感到非常满意,并且 Bevy 在内部使用它作为我们的着色器。我们还添加了诸如导入之类的可用性功能!但是使用 Bevy,您仍然可以选择使用 GLSL(如果您更喜欢)。
工作原理 #
Bevy 建立在 wgpu 库的基础之上,wgpu 是一种现代的低级 GPU API,可以针对几乎所有流行的 API:Vulkan、Direct3D 12、Metal、OpenGL、WebGL2 和 WebGPU。会为给定平台选择最佳的后台 API。它是一种“原生”渲染 API,但它通常遵循 WebGPU 的术语和 API 设计。与 WebGPU 不同,它可以提供对原生 API 的直接访问,这意味着 Bevy 处于“集所有优点于一身”的境地。
WebGPU 示例 #
点击下面的图像之一查看我们的实时 WebGPU 示例(如果您的 浏览器支持)。
改进的着色器导入 #
Bevy 的渲染引擎有很多很棒的选项和功能。例如,PBR StandardMaterial
管道支持桌面/webgpu 和 webgl,6 个可选的网格属性,4 个可选的纹理,以及大量可选的功能,例如雾、蒙皮和 Alpha 混合模式,并且每个版本都会添加更多功能。
许多功能组合需要专门的着色器变体,并且总共超过 3000 行着色器代码分布在 50 个文件中,基于文本替换的着色器处理器开始不堪重负。
在这个版本中,我们已切换到使用 naga_oil,它为我们提供了一个基于模块的着色器框架。它将每个文件单独编译为 naga 的 IR,然后根据需要将它们组合成最终的着色器。这还没有太大可见影响,但它确实带来了一些直接的好处。
- 引擎的着色器代码更易于导航,也更不神奇。以前只有一个全局作用域,因此即使是间接导入的项目也可以引用。这有时会使查找引用背后的实际代码变得困难。现在必须显式导入项目,因此您始终可以通过查看当前文件来了解变量或函数的来源。
- 着色器现在具有代码段报告功能,错误会将您指向着色器文件和行号,从而避免了在复杂的着色器代码库中进行大量查找。
- naga_oil 的预处理器支持更多条件指令,您可以使用
#else if
和#else ifndef
以及#else ifdef
(以前支持)。 - 函数、变量和结构体都有适当的作用域,因此着色器文件不需要使用全局唯一名称来避免冲突。
- 着色器定义可以直接添加到模块中。例如,任何导入
bevy_pbr::mesh_view_types
的着色器现在都会自动定义MAX_DIRECTIONAL_LIGHTS
,不再需要记住为每个使用该模块的新管道添加它。
未来的可能性更加令人兴奋。使用 naga IR 打开了一系列我们希望在未来版本中引入的优秀功能的大门。
- 自动绑定槽分配将允许插件扩展核心视图绑定组,这意味着用于照明和阴影方法、常见材质属性等功能的独立插件变得可行。这将使我们能够模块化核心管道,使代码库的增长(同时保持对多个目标的支持)更加可持续。
- “虚拟”着色器函数将允许用户修改核心函数(例如照明),并可能导致模板风格的材质系统,用户可以在其中提供将在管道中正确点调用的“钩子”。
- 语言互操作性:混合使用 glsl 和 wgsl,以便从您的 glsl 材质着色器访问 Bevy 的 pbr 管道功能,或者为 glsl 编写的实用程序可以在 wgsl 代码中使用。我们希望这也能扩展到 spirv(和 rust-gpu)。
- 我们还没有想到的其他更酷的事情。能够在运行时检查和修改着色器非常强大,并使许多事情成为可能!
UI 节点边框 #
UI 节点现在会绘制边框,其颜色可以使用新的 BorderColor
组件进行配置。
commands.spawn(ButtonBundle {
style: Style {
border: UiRect::all(Val::Px(5.0)),
..default()
},
border_color: BorderColor(Color::rgb(0.9, 0.9, 0.9)),
..default()
})
边框的每一侧都可以配置。
网格 UI 布局 #
在 Bevy UI 中,我们连接了我们使用的布局库(Taffy)中的新 grid
功能。这使 CSS 风格的网格布局成为可能。
这可以在 Style
组件上进行配置。
Style {
/// Use grid layout for this node
display: Display::Grid,
/// Make the grid have a 1:1 aspect ratio
/// This means the width will adjust to match the height
aspect_ratio: Some(1.0),
// Add 24px of padding around the grid
padding: UiRect::all(Val::Px(24.0)),
/// Set the grid to have 4 columns all with sizes minmax(0, 1fr)
/// This creates 4 exactly evenly sized columns
grid_template_columns: RepeatedGridTrack::flex(4, 1.0),
/// Set the grid to have 4 rows all with sizes minmax(0, 1fr)
/// This creates 4 exactly evenly sized rows
grid_template_rows: RepeatedGridTrack::flex(4, 1.0),
/// Set a 12px gap/gutter between rows and columns
row_gap: Val::Px(12.0),
column_gap: Val::Px(12.0),
..default()
},
以调度为中心的 ECS API #
在 Bevy 0.10 中,我们引入了 ECS 调度 V3,它极大地改进了 Bevy ECS 系统调度的功能:调度程序 API 人体工程学、系统链接、在调度中的任何点运行独占系统和应用延迟系统操作的能力、单一统一调度、可配置的系统集、运行条件以及更好的状态系统。
但是很快就很明显,新系统仍然有一些需要改进的地方。
- 基本集很难理解,而且容易出错:什么是基本集?我什么时候使用它们?它们为什么存在?为什么我的排序由于不兼容的基本集排序而隐式无效?为什么某些调度具有默认基本集,而其他调度没有?基本集令人困惑!
- 调度系统的方法太多:我们积累了太多的调度 API。截至 Bevy 0.10,我们有 六种不同的方法将系统添加到“启动”调度中。方法太多了!
- 太多的隐式配置:既有默认调度,也有默认基本集。在某些情况下,系统具有默认调度或默认基本集,但在其他情况下,它们没有!系统的调度和配置应该是明确且清晰的。
- 向调度中添加系统不符合人体工程学:诸如
add_system(foo.in_schedule(CoreSchedule::Startup))
之类的东西不好输入也不好阅读。我们创建了特殊情况帮助程序,例如add_startup_system(foo)
,但是 这需要更多的内部代码,用户定义的调度无法从特殊情况中受益,它完全隐藏了CoreSchedule::Startup
符号!。
消除复杂性 #
如果您在试图理解这一点时开始感到眼花缭乱,或者诸如“隐式添加到 CoreSet::Update
基本集”之类的短语让您感到恐惧... 别担心。经过 深思熟虑,我们已经消除了复杂性,构建了清晰简单的东西。
在Bevy 0.11中,“调度模型”变得更加简单,得益于先调度 ECS API
app
.add_systems(Startup, (a, b))
.add_systems(Update, (c, d, e))
.add_systems(FixedUpdate, (f, g))
.add_systems(PostUpdate, h)
.add_systems(OnEnter(AppState::Menu), enter_menu)
.add_systems(OnExit(AppState::Menu), exit_menu)
- 调度系统只有一种方式
- 调用
add_systems
,指定调度名称,并指定一个或多个系统
- 调用
- 为了方便,基集已被完全移除,取而代之的是调度,它们拥有友好简洁的名称
- 例如:
CoreSet::Update
基集已变成Update
- 例如:
- 不再存在隐式或默认的配置
- 默认调度和默认基集不存在
- 语法直观易懂,使用方便
- 调度位于首位,因此在格式化时它们会“排成一行”
为了比较,请展开此内容查看旧版本!
app
// Startup system variant 1.
// Has an implied default StartupSet::Startup base set
// Has an implied CoreSchedule::Startup schedule
.add_startup_systems((a, b))
// Startup system variant 2.
// Has an implied default StartupSet::Startup base set
// Has an implied CoreSchedule::Startup schedule
.add_systems((a, b).on_startup())
// Startup system variant 3.
// Has an implied default StartupSet::Startup base set
.add_systems((a, b).in_schedule(CoreSchedule::Startup))
// Update system variant 1.
// `CoreSet::Update` base set and `CoreSchedule::Main` are implied
.add_system(c)
// Update system variant 2 (note the add_system vs add_systems difference)
// `CoreSet::Update` base set and `CoreSchedule::Main` are implied
.add_systems((d, e))
// No implied default base set because CoreSchedule::FixedUpdate doesn't have one
.add_systems((f, g).in_schedule(CoreSchedule::FixedUpdate))
// `CoreSchedule::Main` is implied, in_base_set overrides the default CoreSet::Update set
.add_system(h.in_base_set(CoreSet::PostUpdate))
// This has no implied default base set
.add_systems(enter_menu.in_schedule(OnEnter(AppState::Menu)))
// This has no implied default base set
.add_systems(exit_menu.in_schedule(OnExit(AppState::Menu)))
请注意,普通“系统集”仍然存在!您仍然可以使用集来组织和排序您的系统
app.add_systems(Update, (
(walk, jump).in_set(Movement),
collide.after(Movement),
))
configure_set
API 也已调整,以实现一致性
// Bevy 0.10
app.configure_set(Foo.after(Bar).in_schedule(PostUpdate))
// Bevy 0.11
app.configure_set(PostUpdate, Foo.after(Bar))
嵌套系统元组和链式调用 #
现在可以无限嵌套 .add_systems
调用中的系统元组!
app.add_systems(Update, (
(a, (b, c, d, e), f),
(g, h),
i
))
乍一看,这似乎没什么用处。但与每个元组的配置结合使用,它可以让您轻松简洁地表达调度
app.add_systems(Update, (
(attack, defend).in_set(Combat).before(check_health),
check_health,
(handle_death, respawn).after(check_health)
))
.chain()
也已适应,以支持任意嵌套!上面的示例中的排序可以这样重新描述
app.add_systems(Update,
(
(attack, defend).in_set(Combat),
check_health,
(handle_death, respawn)
).chain()
)
这将首先(并行地)运行 attack
和 defend
,然后运行 check_health
,最后(并行地)运行 handle_death
和 respawn
。
这允许进行强大且表达力强的“图形化”排序表达
app.add_systems(Update,
(
(a, (b, c, d).chain()),
(e, f),
).chain()
)
这将并行运行 a
和 b->c->d
,然后在它们完成运行后,并行运行 e
和 f
。
Gizmos #
能够在 2D 和 3D 中绘制简单的形状和线条对于编辑器控件和调试视图非常有用。游戏开发是一项非常“空间”的工作,能够快速绘制形状就如同“打印调试信息”的视觉等效。它有助于回答诸如“这根射线是否在正确的方向上发射?”和“这个碰撞体是否足够大?”等问题。
在Bevy 0.11中,我们添加了一个“立即模式”Gizmos
绘制 API,使这些操作变得简单而高效。在 2D 和 3D 中,您可以绘制线条、矩形、圆形、圆弧、球体、立方体、线段以及更多!
2D Gizmos 3D Gizmos
您可以从任何系统中生成形状(用于 2D 和 3D)
fn system(mut gizmos: Gizmos) {
// 2D
gizmos.line_2d(Vec2::new(0., 0.), Vec2::new(0., 10.), Color::RED);
gizmos.circle_2d(Vec2::new(0., 0.), 40., Color::BLUE);
// 3D
gizmos.circle(Vec3::ZERO, Vec3::Y, 3., Color::BLACK);
gizmos.ray(Vec3::new(0., 0., 0.), Vec3::new(5., 5., 5.), Color::BLUE);
gizmos.sphere(Vec3::ZERO, Quat::IDENTITY, 3.2, Color::BLACK)
}
由于 API 是“立即模式”,因此 gizmos 仅在“排队”的帧上绘制,这意味着您无需担心清理 gizmo 状态!
Gizmos 以批次绘制,这意味着它们非常便宜。您可以拥有数十万个 gizmos!
ECS 音频 API #
Bevy 的音频播放 API 已重新设计,以更好地与 Bevy 的 ECS 集成。
在之前的 Bevy 版本中,您会这样播放音频
#[derive(Resource)]
struct MyMusic {
sink: Handle<AudioSink>,
}
fn play_music(
mut commands: Commands,
asset_server: Res<AssetServer>,
audio: Res<Audio>,
audio_sinks: Res<Assets<AudioSink>>
) {
let weak_handle = audio.play(asset_server.load("my_music.ogg"));
let strong_handle = audio_sinks.get_handle(weak_handle);
commands.insert_resource(MyMusic {
sink: strong_handle,
});
}
这对于仅仅播放一个声音来说,实在是太多样板代码了!然后为了调整播放,您会像这样访问 AudioSink
fn pause_music(my_music: Res<MyMusic>, audio_sinks: Res<Assets<AudioSink>>) {
if let Some(sink) = audio_sinks.get(&my_music.sink) {
sink.pause();
}
}
将音频播放视为资源导致了许多问题,尤其是在 Bevy 场景等方面表现不佳。在Bevy 0.11中,音频播放被表示为一个 Entity
,它具有 AudioBundle
组件
#[derive(Component)]
struct MyMusic;
fn play_music(mut commands: Commands, asset_server: Res<AssetServer>) {
commands.spawn((
AudioBundle {
source: asset_server.load("my_music.ogg"),
..default()
},
MyMusic,
));
}
mode
字段位于 PlaybackSettings
结构体中,提供了一种直接管理这些音频实体生命周期的方式。
通过传递一个 PlaybackMode
,您可以选择是播放一次还是重复播放,分别使用 Once
和 Loop
。如果您预计音频可能会再次播放,则可以通过使用 Despawn
暂时卸载它来节省资源,或者如果它是单次效果则通过使用 Remove
立即释放其内存。
AudioBundle {
source: asset_server.load("hit_sound.ogg"),
settings: PlaybackSettings {
mode: PlaybackMode::Despawn,
..default()
}
}
简单多了!为了调整播放,您可以查询 AudioSink
组件
fn pause_music(query_music: Query<&AudioSink, With<MyMusic>>) {
if let Ok(sink) = query.get_single() {
sink.pause();
}
}
全局音频音量 #
Bevy 现在有一个全局音量级别,可以通过 [GlobalVolume
] 资源进行配置
app.insert_resource(GlobalVolume::new(0.2));
场景中的资源支持 #
Bevy 的场景格式是将游戏状态序列化和反序列化到场景文件中的非常有用的工具。
以前,捕获的状态仅限于实体及其组件。在Bevy 0.11中,场景现在也支持序列化资源。
这在场景格式中添加了一个新的 resources
字段
(
resources: {
"my_game::stats::TotalScore": (
score: 9001,
),
},
entities: {
// Entity scene data...
},
)
场景过滤 #
将数据序列化到场景时,所有组件和 资源 默认情况下都会被序列化。在之前的版本中,您必须使用给定的 TypeRegistry
作为过滤器,排除您不想包含的类型。
在 0.11 中,现在有一个专门的 SceneFilter
类型,使过滤变得更简单、更干净、更直观。它可以与 DynamicSceneBuilder
一起使用,以对实际序列化的内容进行细粒度控制。
我们可以 allow
一部分类型
let mut builder = DynamicSceneBuilder::from_world(&world);
let scene = builder
.allow::<ComponentA>()
.allow::<ComponentB>()
.extract_entity(entity)
.build();
或者 deny
它们
let mut builder = DynamicSceneBuilder::from_world(&world);
let scene = builder
.deny::<ComponentA>()
.deny::<ComponentB>()
.extract_entity(entity)
.build();
默认字体 #
Bevy 现在支持可配置的默认字体,并嵌入了一个微小的默认字体(Fira Mono 的最小版本)。如果您在整个项目中使用一种常用字体,这将非常有用。而且,它还可以更轻松地使用“占位符字体”来原型化新的更改,而无需担心在每个节点上设置它。
UI 纹理图集支持 #
以前,UI ImageBundle
节点只能使用完整图像的句柄,而没有使用 UI 中 TextureAtlases
的便捷方法。在此版本中,我们添加了对 AtlasImageBundle
UI 节点的支持,它将现有的 TextureAtlas
支持引入 UI。
这是通过合并现有的机制来实现的,该机制允许文本渲染选择要使用的字形以及允许使用 TextureAtlasSprite
的机制。
手柄震动 API #
现在您可以使用 EventWriter<GamepadRumbleRequest>
系统参数来触发控制器的力反馈电机。
gilrs
是 Bevy 用于手柄支持的 crate,它允许控制力反馈电机。遗憾的是,在 Bevy 中没有简单的方法可以访问力反馈 API,而无需进行繁琐的簿记工作。
现在 Bevy 有了 GamepadRumbleRequest
事件来实现这一点。
fn rumble_system(
gamepads: Res<Gamepads>,
mut rumble_requests: EventWriter<GamepadRumbleRequest>,
) {
for gamepad in gamepads.iter() {
rumble_requests.send(GamepadRumbleRequest::Add {
gamepad,
duration: Duration::from_secs(5),
intensity: GamepadRumbleIntensity::MAX,
});
}
}
GamepadRumbleRequest::Add
事件会触发力反馈电机,控制震动持续时间、要激活的电机以及震动强度。GamepadRumbleRequest::Stop
会立即停止所有电机。
新的默认色调映射方法 #
在Bevy 0.10中,我们使色调映射可配置,并添加了许多新的色调映射选项。在Bevy 0.11中,我们将默认的色调映射方法从“Reinhard 亮度”色调映射切换到“TonyMcMapface”
拖动此图像进行比较


TonyMcMapface(由 Tomasz Stachowiak 创建)是一种更加中性的显示变换,它试图尽可能地接近输入“光线”。这有助于保留场景中的艺术选择。值得注意的是,亮度在整个光谱中都会发生去饱和(与 Reinhard 亮度不同)。与 Reinhard 亮度相比,它在与泛光结合使用时效果也更好。
EntityRef 查询 #
EntityRef
现在实现了 WorldQuery
,这使得在 ECS 系统中更容易查询任意组件
fn system(query: Query<EntityRef>) {
for entity in &query {
if let Some(mesh) = entity.get::<Handle<Mesh>>() {
let transform = entity.get::<Transform>().unwrap();
}
}
}
请注意,EntityRef
查询默认情况下会访问整个 World
中的每个实体和每个组件。这意味着它们将与任何“可变”查询冲突
/// These queries will conflict, making this system invalid
fn system(query: Query<EntityRef>, mut enemies: Query<&mut Enemy>) { }
为了解决冲突(或减少访问的实体数量),您可以添加过滤器
/// These queries will not conflict
fn system(
players: Query<EntityRef, With<Player>>,
mut enemies: Query<&mut Enemy, Without<Player>>
) {
// only iterates players
for entity in &players {
if let Some(mesh) = entity.get::<Handle<Mesh>>() {
let transform = entity.get::<Transform>().unwrap();
}
}
}
请注意,直接查询所需的组件通常仍然更符合人体工程学(而且更有效率)
fn system(players: Query<(&Transform, &Handle<Mesh>), With<Player>>) {
for (transform, mesh) in &players {
}
}
截图 API #
Bevy 现在有一个简单的截图 API,可以将给定窗口的截图保存到磁盘
fn take_screenshot(
mut screenshot_manager: ResMut<ScreenshotManager>,
input: Res<Input<KeyCode>>,
primary_window: Query<Entity, With<PrimaryWindow>>,
) {
if input.just_pressed(KeyCode::Space) {
screenshot_manager
.save_screenshot_to_disk(primary_window.single(), "screenshot.png")
.unwrap();
}
}
RenderTarget::TextureView #
现在,可以将 Camera
RenderTarget
设置为 wgpu TextureView
。这允许第三方 Bevy 插件管理 Camera
的纹理。这种方法启用的一种特别有趣的用例是 XR/VR 支持。一些社区成员已经 证明了这一点!
改进的文本换行 #
之前的 Bevy 版本没有正确换行文本,因为它在计算布局之前计算了实际文本。Bevy 0.11 添加了一个“文本测量步骤”,它在布局之前计算文本大小,然后在布局之后计算实际文本。
在 BreakLineOn
设置中还有一个新的 NoWrap
变体,当需要时可以完全禁用文本换行。
更快的 UI 渲染批处理 #
我们通过在纹理发生改变但下一个节点没有纹理的情况下避免拆分 UI 批次,获得了巨大的 UI 性能提升。
这是我们“许多按钮”压力测试的性能分析。红色表示优化之前,黄色表示优化之后
更好的反射代理 #
Bevy 的反射 API 有几个结构体,它们统称为“动态”类型。其中包括 DynamicStruct
、DynamicTuple
等等,它们用于在运行时动态构造任何形状或形式的类型。这些类型也用于创建通常称为“代理”的类型,这些类型是用于表示实际具体类型的动态类型。
这些代理是 Reflect::clone_value
方法的强大之处,该方法在幕后生成这些代理,以便构造数据的运行时克隆。
不幸的是,这会导致一些难以察觉的陷阱,可能会让用户感到意外,例如代理的哈希值与它们所代表的具体类型的哈希值不同,代理不被视为等效于它们的具体对应物,等等。
虽然此版本不一定修复了这些问题,但它确实为将来修复这些问题奠定了坚实的基础。它是通过改变代理的定义方式来实现的。
在 0.11 之前,代理仅仅通过克隆具体类型的Reflect::type_name
字符串并将其作为自身的 Reflect::type_name
返回来定义。
现在在 0.11 中,代理通过复制对具体类型静态TypeInfo
的引用来定义。这将允许我们动态地访问更多具体类型的类型信息,而无需 TypeRegistry
。在未来的版本中,我们将利用这一点,将哈希和比较策略直接存储在 TypeInfo
中,以缓解上面提到的代理问题。
FromReflect
人体工程学 #
Bevy 的反射 API 通常使用类型擦除的 dyn Reflect
特性对象传递数据。这通常可以通过 <dyn Reflect>::downcast_ref::<T>
将其向下转换为其具体类型;但是,如果底层数据已转换为“动态”表示(例如,结构体类型的 DynamicStruct
,列表类型的 DynamicList
等等),则此方法无效。
let data: Vec<i32> = vec![1, 2, 3];
let reflect: &dyn Reflect = &data;
let cloned: Box<dyn Reflect> = reflect.clone_value();
// `reflect` really is a `Vec<i32>`
assert!(reflect.is::<Vec<i32>>());
assert!(reflect.represents::<Vec<i32>>());
// `cloned` is a `DynamicList`, but represents a `Vec<i32>`
assert!(cloned.is::<DynamicList>());
assert!(cloned.represents::<Vec<i32>>());
// `cloned` is equivalent to the original `reflect`, despite not being a `Vec<i32>`
assert!(cloned.reflect_partial_eq(reflect).unwrap_or_default());
为了解决这个问题,可以使用FromReflect
特性将任何 dyn Reflect
特性对象转换回其具体类型——无论它实际上是否是该类型,还是它的动态表示。它甚至可以使用ReflectFromReflect
类型数据动态调用。
在 0.11 之前,用户必须手动为需要它的每个类型派生 FromReflect
,并手动注册 ReflectFromReflect
类型数据。这使得它使用起来很麻烦,也意味着它经常被遗忘,导致下游用户在进行反射转换时遇到困难。
现在在 0.11 中,FromReflect
会自动派生,并且 ReflectFromReflect
会自动为所有派生 Reflect
的类型注册。这意味着大多数类型默认情况下将具有 FromReflect
能力,从而减少了样板代码,并增强了围绕 FromReflect
的逻辑。
用户仍然可以通过在类型上添加#[reflect(from_reflect = false)]
属性来选择退出此行为。
#[derive(Reflect)]
struct Foo;
#[derive(Reflect)]
#[reflect(from_reflect = false)]
struct Bar;
fn test<T: FromReflect>(value: T) {}
test(Foo); // <-- OK!
test(Bar); // <-- ERROR! `Bar` does not implement trait `FromReflect`
Deref 派生属性 #
Bevy 代码倾向于大量使用newtype 模式,这就是我们为Deref
和DerefMut
提供专门派生的原因。
这以前只适用于只有一个字段的结构体。
#[derive(Resource, Deref, DerefMut)]
struct Score(i32);
对于 0.11,我们通过添加 #[deref]
属性改进了这些派生,这允许它们用于具有多个字段的结构体。这使得使用泛型 newtype 变得更容易。
#[derive(Component, Deref, DerefMut)]
struct Health<T: Character> {
#[deref] // <- use the `health` field as the `Deref` and `DerefMut` target
health: u16,
_character_type: PhantomData<T>,
}
更简单的 RenderGraph 构建 #
将 Node
添加到 RenderGraph
需要大量的样板代码。在这个版本中,我们尝试为大多数常见操作减少样板代码。没有移除现有的 API,这些只是帮助简化使用 RenderGraph
的辅助函数。
我们在 App
中添加了 RenderGraphApp
特性。此特性包含各种辅助函数,以减少向图形添加节点和边的样板代码。
RenderGraph
Node
的另一个痛点是通过每个节点传递视图实体,并手动更新该视图上的查询。为了解决这个问题,我们添加了 ViewNode
特性和 ViewNodeRunner
,它们将自动处理在视图实体上运行 Query
。我们还将视图实体作为 RenderGraph
的一等公民概念。因此,您现在可以在图形的任何地方访问正在运行的视图实体,而无需在每个 Node
之间传递它。
所有这些新 API 都假设您的 Node 实现了 FromWorld
或 Default
。
以下是 BloomNode
的实际应用示例。
// Adding the node to the 3d graph
render_app
// To run a ViewNode you need to create a ViewNodeRunner
.add_render_graph_node::<ViewNodeRunner<BloomNode>>(
CORE_3D,
core_3d::graph::node::BLOOM,
);
// Defining the node
#[derive(Default)]
struct BloomNode;
// This can replace your `impl Node` block of any existing `Node` that operated on a view
impl ViewNode for BloomNode {
// You need to define your view query as an associated type
type ViewQuery = (
&'static ExtractedCamera,
&'static ViewTarget,
&'static BloomSettings,
);
// You don't need Node::input() or Node::update() anymore. If you still need these they are still available but they have an empty default implementation.
fn run(
&self,
graph: &mut RenderGraphContext,
render_context: &mut RenderContext,
// This is the result of your query. If it is empty the run function will not be called
(camera, view_target, bloom_settings): QueryItem<Self::ViewQuery>,
world: &World,
) -> Result<(), NodeRunError> {
// When using the ViewNode you probably won't need the view entity but here's how to get it if you do
let view_entity = graph.view_entity();
// Run the node
}
}
#[reflect(default)]
用于枚举变体字段 #
使用 FromReflect
特性时,如果标记为 #[reflect(default)]
的字段在反射对象中不存在,则它们将被设置为其 Default
值。
以前,这仅支持结构体字段。现在,它也支持所有枚举变体字段。
#[derive(Reflect)]
enum MyEnum {
Data {
#[reflect(default)]
a: u32,
b: u32,
},
}
let mut data = DynamicStruct::default ();
data.insert("b", 1);
let dynamic_enum = DynamicEnum::new("Data", data);
let my_enum = MyEnum::from_reflect( & dynamic_enum).unwrap();
assert_eq!(u32::default(), my_enum.a);
延迟的资产热重载 #
Bevy 现在会在“文件系统上资产更改”事件之后等待 50 毫秒,然后再重新加载资产。在没有延迟的情况下重新加载会导致某些系统上读取无效的资产内容。等待时间是可配置的。
自定义 glTF 顶点属性 #
现在可以从 glTF 文件加载具有自定义顶点属性的网格。自定义属性可以映射到 Bevy 的MeshVertexAttribute
格式,该格式由Mesh
类型在GltfPlugin
设置中使用。这些属性然后可以在 Bevy 着色器中使用。有关示例,请查看我们的新示例。
稳定 TypePath #
Bevy 历来使用std::any::type_name
在许多地方使用友好的名称来标识 Rust 类型:Bevy Reflect、Bevy Scenes、Bevy Assets、Bevy ECS 等等。不幸的是,Rust 并没有对type_name
的稳定性或格式做出任何保证,这使得在理论上构建它很危险(尽管在实践中它迄今为止一直很稳定)。
也没有内置的方法来检索类型名称的“部分”。如果您想要简短名称,没有内部类型的泛型类型的名称,模块名称或包名称,则必须对type_name
进行字符串操作(这可能容易出错/非平凡)。
此外,type_name
无法自定义。在某些情况下,作者可能会选择使用除完整模块路径以外的其他内容来标识类型(例如,如果他们更喜欢更短的路径,或者想要抽象出私有/内部模块)。
出于这些原因,我们开发了一个新的稳定TypePath
,它会自动为所有派生Reflect
的类型实现。此外,它可以在没有派生Reflect
的情况下手动派生。
mod my_mod {
#[derive(Reflect)]
struct MyType;
}
/// prints: "my_crate::my_mod::MyType"
println!("{}", MyType::type_path());
/// prints: "MyType"
println!("{}", MyType::short_type_path());
/// prints: "my_crate"
println!("{}", MyType::crate_name().unwrap());
/// prints: "my_crate::my_mod"
println!("{}", MyType::module_path().unwrap());
这对泛型也有效,这可能很方便。
// prints: "Option<MyType>"
println!("{}", Option::<MyType>::short_type_path());
// prints: "Option"
println!("{}", Option::<MyType>::type_ident().unwrap());
TypePath
可以由类型作者自定义。
#[derive(TypePath)]
#[type_path = "some_crate::some_module"]
struct MyType;
我们正在将 Bevy 的内部type_name
使用移植到TypePath
上,这将在Bevy 0.12中实现。
系统元组的 run_if
#
现在可以将“运行条件” 添加到系统元组。
app.add_systems(Update, (run, jump).run_if(in_state(GameState::Playing)))
这将精确地评估一次“运行条件”,并将结果用于元组中的每个系统。
这使我们能够移除状态的 OnUpdate
系统集(以前用于在处于给定状态时运行系统组)。
Has
查询 #
您现在可以在查询中使用 Has<Component>
,如果该组件存在则返回 true,如果不存在则返回 false。
fn system(query: Query<Has<Player>>) {
for has_player in &query {
if has_player {
// do something
}
}
}
派生 Event
#
Bevy 的Event
特性现在是派生的,而不是为所有内容自动实现的。
#[derive(Event)]
struct Collision {
a: Entity,
b: Entity,
}
这可以防止某些类型的错误,使Event
类型更具自述性,并与其他 Bevy ECS 特性(如组件和资源)保持一致。它还为配置Event
存储类型打开了大门,我们计划在将来的版本中进行此操作。
三次贝塞尔曲线示例 #
一个展示如何绘制 3D 曲线以及如何沿路径移动对象的示例。
尺寸约束示例 #
一个交互式示例,展示了各种Style
尺寸约束如何影响 UI 节点。
显示和可见性示例 #
一个示例,展示了显示和可见性设置如何影响 UI 节点。
不再有 Bors! #
Bevy 历来使用 Bors 合并系统来确保我们永远不会合并 GitHub 上破坏了 CI 验证的拉取请求。这是确保我们能够安全有效地协作的关键基础设施。幸运的是,GitHub 终于推出了合并队列,它解决了与 Bors 相同的问题,但好处是与 GitHub 集成得更加紧密。
在这个发布周期中,我们迁移到了合并队列,我们对体验非常满意!
新的 CI 作业 #
我们添加了许多新的 CI 作业,以改善 Bevy 的开发体验。
- 每天都会在真实的 Android 和 iOS 设备上运行 Bevy 的移动示例的作业!这有助于防止编译器可能无法捕获的回归。
- 添加了在 CI 中截取屏幕截图的功能,这可以用来验证 Bevy 示例运行的结果。
- 一项在缺少功能或示例文档更新的 PR 上留下 GitHub 评论的作业。
接下来是什么? #
我们有很多工作已经接近完成,因此很有可能在Bevy 0.12中发布。
- Bevy Asset V2:一个全新的资产系统,它添加了“资产预处理”、可选的资产 .meta 文件、递归资产依赖关系跟踪和事件、异步资产 IO、更强大的资产句柄、更高效的资产存储以及各种可用性改进!这项工作已经接近完成。它几乎进入了 Bevy 0.11,但还需要更多时间来完善。
- PBR 材质光透射:透射/屏幕空间折射允许模拟玻璃、塑料、液体和凝胶、宝石、蜡等材料。这一个也即将完成!
- TAA 改进:我们正在为 TAA 进行一些更改,这将提高其质量、速度和在引擎中的支持。
- GPU 拾取:通过使用颜色 ID 在渲染中识别网格,在 GPU 上高效且准确地选择实体。
- 用于方向光和聚光灯阴影的 PCF:减少阴影边缘的锯齿
- UI 节点边框圆角和阴影:在您的 UI 节点上添加曲率和“投影阴影”!
- 延迟渲染:Bevy 已经通过为深度和法线提供可选的单独通道来实现“混合模式”正向渲染。我们目前正在尝试支持“完全延迟”渲染,这将为新效果和不同的性能折衷方案打开大门。
从高层次来看,我们计划在下个周期重点关注资产系统、UI、渲染功能和场景。
查看 Bevy 0.12 里程碑,了解目前正在考虑用于 Bevy 0.12 的最新工作列表。
支持 Bevy #
赞助有助于使我们对 Bevy 的工作可持续发展。如果您相信 Bevy 的使命,请考虑 赞助我们……每一份帮助都至关重要!
贡献者 #
Bevy 是由 一大群人 制作的。非常感谢 166 位贡献者,他们使此次发布(以及相关的文档)成为可能!按随机顺序排列
- @TheBlek
- @hank
- @TimJentzsch
- @Suficio
- @SmartManLudo
- @BlondeBurrito
- @lewiszlw
- @paul-hansen
- @boringsan
- @superdump
- @JonahPlusPlus
- @airingursb
- @Sheepyhead
- @nakedible
- @Testare
- @andresovela
- @SkiFire13
- @doup
- @BlackPhlox
- @nicoburns
- @wpederzoli
- @adtennant
- @LoopyAshy
- @KernelFreeze
- @ickshonpe
- @jim-ec
- @mrchantey
- @frengor
- @Joakker
- @arendjr
- @MJohnson459
- @TheTacBanana
- @IceSentry
- @ItsDoot
- @Anti-Alias
- @mwbryant
- @inodentry
- @LiamGallagher737
- @robtfm
- @mockersf
- @ndarilek
- @samtenna
- @Estus-Dev
- @InnocentusLime
- @p-hueber
- @B-Reif
- @Adamkob12
- @payload
- @JohnTheCoolingFan
- @djeedai
- @SludgePhD
- @s-lambert
- @kjolnyr
- @Skovrup1
- @Ababwa
- @Illiux
- @Carter0
- @luca-della-vedova
- @Neo-Zhixing
- @coreh
- @helvieq499
- @Carbonhell
- @BrandonDyer64
- @hymm
- @JMS55
- @iiYese
- @mtsr
- @jannik4
- @natasria
- @Trouv
- @minchopaskal
- @chrisjuchem
- @marlyx
- @valaphee
- @hankjordan
- @rparrett
- @Selene-Amanita
- @opstic
- @loganbenjamin
- @MrGunflame
- @pyrotechnick
- @mjhostet
- @VitalyAnkh
- @CatThingy
- @maniwani
- @Themayu
- @SET001
- @jakobhellermann
- @MrGVSV
- @nicopap
- @Wcubed
- @aevyrie
- @NiklasEi
- @bonsairobo
- @cart
- @TotalKrill
- @raffaeleragni
- @Aceeri
- @Shatur
- @orzogc
- @UncleScientist
- @Elabajaba
- @vyb
- @komadori
- @jnhyatt
- @harudagondi
- @konsti219
- @james7132
- @mvlabat
- @neithanmo
- @dgunay
- @Shfty
- @hate
- @B-head
- @MinerSebas
- @chescock
- @BorMor
- @lupan
- @CrazyRoka
- @bzm3r
- @Sixmorphugus
- @JoJoJet
- @eltociear
- @gakit
- @geieredgar
- @tjamaan
- @alice-i-cecile
- @NoahShomette
- @james-j-obrien
- @tinrab
- @Olle-Lukowski
- @TheRawMeatball
- @sarkahn
- @RobWalt
- @johanhelsing
- @SneakyBerry
- @beeryt
- @Vrixyz
- @wainwrightmark
- @EliasPrescott
- @konsolas
- @ameknite
- @Connor-McMillin
- @Weibye
- @SpecificProtagonist
- @danchia
- @vallrand
- @atornity
- @soqb
- @devil-ira
- @AnthonyKalaitzis
- @yyogo
- @NiseVoid
- @gajop
- @Gingeh
- @zendril
- @ezekg
- @ickk
- @Leonss23
- @kellencataldo
- @akappel
- @hazelsparrow
- @mattfbacon
- @gianzellweger
- @lakrsv
- @laundmo
完整变更日志 #
渲染 #
- Webgpu 支持
- 改进着色器导入模型
- 屏幕空间环境光遮蔽 (SSAO) MVP
- 时间抗锯齿 (TAA)
- 立即模式线/Gizmos 绘制
- 使渲染图槽对于大多数情况可选
- 拆分不透明和透明阶段
- 内置天空盒
- 将视差贴图添加到 bevy PBR
- 添加 AMD 的鲁棒对比度自适应锐化端口
- 添加 RenderGraphApp 以简化添加渲染节点
- 添加屏幕截图 API
- 添加变形目标
- wasm 中的屏幕截图
- 添加 ViewNode 以简化渲染节点管理
- 偏置纹理mipmap
- 基于
bevy_polyline
的 Gizmos 的实例化线渲染 - 添加
RenderTarget::TextureView
- 更改默认色调映射方法
- 允许使用自定义深度纹理
- 尽可能在主通道中使用预通道法线纹理
- 左手系 y 轴朝上的立方体贴图坐标
- 允许 SPIR-V 着色器在存在着色器定义时进行处理
- 从 DynamicUniformBuffer 和 DynamicStorageBuffer 中删除不必要的 Vec 值
- 添加
MAY_DISCARD
着色器定义,为大多数情况启用早期深度测试 - 为
Sprite
、TextureAtlasSprite
和Mesh2d
添加Aabb
计算 - Color::Lcha 构造函数
- 为 Color::Lcha 修正 Color::as_rgba_linear
- 为预通道着色器添加 Globals 结构
- 为 Collision 派生 Copy 和 Clone
- 修正启用 2d 相机上的 HDR 时发生的崩溃
- 抖动修正
- 在
GpuMesh
上为索引网格计算vertex_count
- 在 PreUpdate 调度程序中运行 update_previous_view_projections
- 添加
WebP
图片格式支持 - 添加对 pnm 纹理的支持
- 修正无效的骨骼权重
- 修正缺少 UV 时 pbr 着色器中断
- 修正 Plane UVs/纹理翻转
- 修正 look_to 导致 NaN 旋转
- 修正 look_to 变量命名
- 修正 2d Gizmos 发生的段错误
- 在更多地方使用 RenderGraphApp
- 修正视口更改检测
- 从所有缓冲区包装器类型中删除 capacity 字段
- 同步 pbr_types.wgsl StandardMaterial 值
- 避免在没有绘制任何 Gizmos 时生成 Gizmo 网格
- 对 AABB Gizmos 颜色使用一致的种子
- bevy_pbr:不要从级联中剔除没有 Aabbs 的网格
- 在默认预通道片段着色器中处理 vertex_uvs(如果存在)
- 将 (Vec2, Vec2) 更改为 Camera::logical_viewport_rect 中的 Rect
- 使 glsl 和 spirv 支持可选
- 修正预通道法线映射
- [u8; 4] 和 Color 之间的转换
- 添加选项以禁用特定相机的 Gizmo 渲染
- 修正变形目标预通道着色器
- 修正 bloom wasm 支持
- 修正启用 SSAO 时出现的黑色斑点(由于 NAN 导致)
- 修正法线预通道
- Refs #8975 -- 为 RenderDevice::poll() 添加返回值
- 修正 Adreno GPU 的 WebGL 模式
- 修正视差贴图
- 添加 Vec 到 BufferVec - Issue #3531
- 修正具有显式 FullscreenVertexOutput 导入的 CAS 着色器
- 使
TextureAtlas::texture_handles
成为pub
而不是pub(crate)
(#8633) - 使 Material2d 管道系统公开
- 修正 Wayland + Nvidia 上的屏幕截图
- 应用代码库更改,为
StandardMaterial
传输做准备 - 对 TAA 使用 ViewNode
- 将 Camera3dBundle::tonemapping 更改为 Default
- 为 AlphaMode 删除
Component
派生 - 使 Opaque3dPrepass 和 AlphaMask3dPrepass 阶段项目的设置与其他项目一致
- 将
Plane
结构体重命名为HalfSpace
- 扩展
FallbackImage
以包括每个可能的TextureViewDimension
的GpuImage
- 级联阴影贴图:修正预通道正交深度钳位
- 修正 WebGPU 中的 Gizmos
- 修正 AsBindGroup 派生、纹理属性、可见性标志解析
- 在窗口关闭时禁用相机
- 反映
BloomSettings
的Component
和Default
- 为 TextureAtlasSprite 添加反射宏
- 在 NoFrustumCulling 上实现 Reflect
音频 #
诊断 #
场景 #
变换 + 层级结构 #
Gizmo #
反射 #
- reflect:稳定类型路径 v2
- bevy_reflect:更好的代理
- bevy_reflect:FromReflect 人体工程学实现
- bevy_reflect:允许在枚举变体字段上使用
#[reflect(default)]
- 在使用 Reflect 的地方添加 FromReflect
- 为 bevy_reflect::Map trait 添加 get_at_mut
- Reflect 现在需要 DynamicTypePath。删除 Reflect::get_type_path()
- bevy_ui:添加
FromReflect
派生 - 为 AssetPath 添加 Reflect 和 FromReflect
- bevy_reflect:修正尾随逗号破坏派生
- 修正 Box
包含哈希映射的结构体在对其调用 clone_value 时发生恐慌 - bevy_reflect:将
ReflectFromReflect
添加到 prelude - bevy_reflect:允许在 bevy_reflect crate 之外构造 MapIter。
- bevy_reflect:在 where 子句中消除类型边界歧义。
- 为 Cow<'static, [T]> 添加反射
- 不要在 ParsedPath::element_mut 上要求 mut
- Reflect UUID
- 不要忽略
UntypedReflectDeserializerVisitor
中的额外条目 - 构造 Box
从世界为 ReflectComponent - reflect:避免 GenericTypeCell 中的死锁
- 允许在
add_plugins
中使用元组和单个插件,弃用add_plugin
- 将 ScheduleRunnerSettings 合并到 ScheduleRunnerPlugin
- 在默认 run_once 运行程序中正确设置所有内容
- 修正
Plugin::build
检测 - 修正未在
ScheduleRunnerPlugin
中调用 App::finish 和 App::cleanup - 将运行程序类型从 Fn 放宽到 FnOnce
- 在 app::edit_schedule 中将 FnMut 放宽到 FnOnce
- 延迟资产热重载
- 添加对自定义 glTF 顶点属性的支持。
- 修正使用 debug_asset_server 时发生的恐慌
- 在禁用
filesystem_watcher
功能时构建时出现unused_variables
警告 - bevy_asset:添加
LoadContext::get_handle_untyped
- 将光标位置移动到内部状态
- 在窗口创建期间设置光标命中测试
- 不要在窗口创建期间无条件地设置命中测试
- 将 winit 的
wayland-csd-adwaita
功能添加到 Bevy 的wayland
功能中 - 支持设置窗口主题,并公开系统窗口主题更改事件
- 触控板放大和旋转事件
- 修正系统界面缩放时窗口未正确居中
- 公开 WindowDestroyed 事件
- UI 节点边框
- 为
bevy_ui
添加 CSS Grid 支持 text_system
拆分- 将文本系统中的本地文本队列替换为存储在组件中的标志
NoWrap
Text
特性- 添加默认字体
- UI 纹理图集支持
- 改进 UI 渲染批处理
- 一致的屏幕空间坐标
UiImage
辅助函数- 对每个文本执行文本缩放计算,而不是每个字形
- 修复裁剪文本字形的尺寸。
- 将缩放因子应用于
ImageMeasure
尺寸 - 通过添加一个扁平插值属性来修复“ui_pipeline”中的 WebGPU 错误
- 重命名 Interaction::Clicked -> Interaction::Pressed
- 扁平化使用
Size
的 UIStyle
属性 + 删除Size
- 按轴拆分 UI
Overflow
- 添加用于计算 UI 节点大小和位置的方法
- 跳过未纹理化 UI 节点的 UV 计算
- 修复文本测量算法
- 在将 UI 坐标从物理坐标转换为逻辑坐标时除以 UiScale
MeasureFunc
改进- 在
Children
中公开排序方法 - 修复使用大小值的最小和最大尺寸
- 修复
Text2d
文本锚的错误水平对齐 - 删除
Val::Undefined
Val
视口单元变体- 在删除
CalculatedSize
组件时,从 Taffy 中删除相应的测量值。 UiRect
轴构造函数- 修复裁剪和翻转 ImageNodes 的 UV 计算
- 修复在 #8026 中解决合并冲突时文本系统崩溃的问题
- 允许
bevy_ui
crate 在不启用text
特性的情况下编译 - 修复
flex_node_system
中的双叶节点更新 - 在禁用特性时也导入默认句柄
measure_text_system
文本查询修复- 修复示例中的 panic:text_wrap_debug.rs
- UI 布局树调试打印
- 修复
Node::physical_rect
并添加一个physical_size
方法 - 在
ui_focus_system
中以矢量方式执行relative_cursor_position
计算 - 添加
UiRect::px()
和UiRect::percent()
工具 - 为
bevy_text
特性添加缺失的依赖项 - 删除非文本系统使用的导入上的“bevy_text”特性属性
- 正在增长 UI 节点的修复
- Schedule-First:全新改进的 add_systems
- 添加 OnTransition 调度,它在 OnExit 和 OnEnter 之间运行
- 通过匿名系统集为
SystemConfigs
添加run_if
- 删除 OnUpdate 系统集
- 重命名 apply_system_buffers 为 apply_deferred
- 将 Command 的“write”方法重命名为“apply”
- 要求所有事件使用
#[derive(Event)]
- 为 EntityRef 实现 WorldQuery
- 改进或与不相交检查
- 添加使用
&World
运行只读系统的方法 - 减少插入组件时的分支
- 使
#[system_param(ignore)]
和#[world_query(ignore)]
不再必要 - 删除
#[system_param(ignore)]
和#[world_query(ignore)]
- 将
WorldQuery
宏扩展到元组结构体 - 使状态私有,只能通过 State 资源的获取器访问
- 为
State<S>
实现Deref
- 内联更多 ECS 函数
- 为世界调度添加
scope
API - 简化系统管道并使其更加灵活
- 添加
any_component_removed
条件 - 使用
UnsafeWorldCell
来提高SystemParam
的代码质量 - 使用
UnsafeWorldCell
提高多线程执行程序的安全性 - 将引擎的其余部分迁移到
UnsafeWorldCell
- 使
Condition
特性泛型化 - 将 or_else 组合器添加到 run_conditions.rs
- 添加 iter_many_manual QueryState 方法
- 通过 UnsafeWorldCell 访问世界存储
- 添加 Has
WorldQuery 类型 - 添加/修复
track_caller
属性,用于恐慌的实体访问器方法 - 提高变更检测的类型安全性与清晰度
- 使
WorldQuery
元类型不可命名 - 为
Mut<T>
添加一个公共构造函数 - 删除 ChangeTrackers
- 为 Tick 派生 Eq, PartialEq
- 在调用
.in_schedule
时初始化空调度,如果它们不存在 - 将对
add_system
的多次调用替换为add_systems
- 不要在未知歧义时恐慌
- 将 Clone 添加到常见条件
- 使 BundleInfo 的字段不为 pub(crate)
- 将查询变更标记传递给
QueryParIter
,而不是始终使用来自World
的变更标记。 - 删除
Entities::get
中的多余边界检查 - 添加 World::try_run_schedule
- 将未实现更改为自定义系统结构
- 修复
SystemParam
和WorldQuery
宏导致的命名冲突 - 在
assert_is_system
中检查冲突访问 - 修复只读
WorldQuery
类型的字段可见性 Or<T>
应该是一种新的PhantomData<T>
类型- 使标准命令更符合人体工程学(在利基情况下)
- 删除
ScheduleBuildError
的基本集错误变体 - 将一些不安全的系统执行器代码替换为安全代码
- 将
increment_change_tick
更新为返回强类型Tick
- 将事件跟踪移至详细跟踪!
- 仅当
next_state != old_state
时触发状态转换 - 修复使用世界调度时的恐慌和文档
- 改进对标记为非发送的 Send 资源的警告
- 重新组织系统模块
- 修复框标签
- 简化世界调度方法
- 只打印出名称字符串,而不是整个 Name 结构体
- 为
EventId
手动实现常用特性 - 用 UI 系统查询中的
Ref
替换&T, Changed<T>
的剩余使用 - 重命名
UnsafeWorldCell::read_change_tick
- 改进命令的封装并添加文档
- 修复 all_tuples + 添加文档。
- 为
Ref
添加new
和map
方法 - 允许无尺寸类型作为
Ref::map
中的映射值 - 为
CombinatorSystem
实现Clone
- 为 EntityRef 添加 get_ref
- 使
QueryParIter::for_each_unchecked
私有 - 简化
ComponentIdFor
类型 - 将 last_changed_tick 和 added_tick 添加到 ComponentTicks
- 在
QueryState::par_iter
中要求只读查询 - 修复 any_component_removed
- 弃用
WorldQuery::Fetch
的类型别名 - bevy_ecs:添加用于插入组件和捆绑包的无类型方法
- 将 AppTypeRegistry 移动到 bevy_ecs
- 跳过对 apply_deferred 系统的检查变更标记
- 拆分 bevy_ecs reflect.rs 模块
- 使 ecs Reflect* 的函数指针公开
- 重新导出 glam_assert 特性
- 修复 CubicCurve::iter_samples 迭代次数
- 为
Rect
添加整数等效项 - 添加
CubicCurve::segment_count
+iter_samples
调整 - 将 Taffy 要求升级到 v0.3.5
- 更新 ruzstd 和 basis universal
- 更新到 wgpu 0.16.0、wgpu-hal 0.16.0 和 naga 0.12.0
- 将 sysinfo 要求从 0.28.1 更新到 0.29.0
- 将 libloading 要求从 0.7 更新到 0.8
- 更新 syn、encase、glam 和 hexasphere
- 将 android_log-sys 要求从 0.2.0 更新到 0.3.0
- 将 bitflags 更新到 2.3
- 将 ruzstd 要求从 0.3.1 更新到 0.4.0
- 将 notify 要求从 5.0.0 更新到 6.0.0
- 将 hashbrown 提升到 0.14
- 更新 ahash 和 hashbrown
- 提升 accesskit 和 accesskit_winit