Bevy 0.9
发布日期:2022 年 11 月 12 日 作者:Carter Anderson (
@cart
@cart_cart
cartdev )

感谢 **159** 位贡献者、**430** 个拉取请求、社区审阅者以及我们的 慷慨的赞助者,我很高兴在 crates.io 上宣布发布 **Bevy 0.9**!
对于那些不知道的人来说,Bevy 是一个用 Rust 构建的清新简洁、数据驱动的游戏引擎。你可以查看我们的 快速入门指南 以立即尝试它。它是免费的,并且永远开源!你可以在 GitHub 上获取完整的 源代码。查看 Bevy 资产 以获取社区开发的插件、游戏和学习资源的集合。
要将现有的 Bevy App 或插件更新到 **Bevy 0.9**,请查看我们的 0.8 到 0.9 迁移指南.
自从我们几个月前发布上一个版本以来,我们已经添加了很多新的功能、错误修复和质量改进,但以下是其中的一些亮点
- HDR 后处理、色调映射和光晕:Bevy 拥有一个新的 HDR 后处理和色调映射管道,我们使用它来实现“光晕”后处理效果!
- FXAA:添加了快速近似抗锯齿,它为用户提供了一种新的廉价屏幕空间抗锯齿选项。
- 去带抖动:使用这种新的后处理效果来隐藏梯度精度错误!
- 其他后处理改进:视图目标双缓冲和自动渲染目标格式处理。
- 新的场景格式:Bevy 的新场景格式更小、更容易手动组成,也更容易阅读。它有“人类可读”和“二进制”两种变体!
- 代码驱动的场景构建:使用查询和特定实体引用,从现有的应用程序动态构建场景。
- 改进的实体/组件 API:现在,使用组件生成实体比以往任何时候都更简单、更符合人体工程学!
- 独占系统重构:独占系统(具有唯一 ECS 世界访问权限的系统)现在只是“普通”系统,具有显着改善的可用性。
- 枚举反射:Bevy Reflect 现在可以反映枚举类型,这将它们公开给 Bevy 的场景系统,并为枚举打开编辑器工具的大门。
- 时间着色器全局变量:时间现在作为全局变量传递给着色器,使得在自定义着色器中进行时间驱动的动画变得容易!
- 插件设置:插件现在可以拥有设置,这些设置可以在插件组中被覆盖,从而简化插件配置故事。
- Bevy UI Z 索引:使用局部和全局 Z 索引来控制 UI 元素如何堆叠在彼此之上
HDR 后处理、色调映射和光晕 #
Bevy 现在支持“光晕”后处理效果,它由对我们的 HDR(高动态范围)渲染管道的许多内部改进提供支持。
光晕在明亮的光线周围产生“模糊”效果,它模拟了相机(以及我们的眼睛)在现实世界中感知光线的方式。高质量的光晕建立在 HDR 渲染管道之上,HDR 渲染管道使用超过标准 8 位每通道(rgba)来表示光线和颜色,而标准 8 位每通道(rgba)在其他地方使用。在之前的版本中,Bevy 已经在其 PBR 着色器中 内部进行 HDR 照明,但由于我们将渲染结果渲染到“普通”(低动态范围)纹理中,因此在将 HDR 照明映射到 LDR 纹理(使用称为色调映射的过程)时,我们必须丢失额外的 HDR 信息。
在 **Bevy 0.9** 中,你现在可以配置相机以渲染到 HDR 纹理,这样在完成“主要通道”渲染后,将保留高动态范围信息
Camera {
// Currently this defaults to false, but we will likely
// switch this to true by default in future releases
hdr: true,
..default()
}
这使得后处理效果(如光晕)能够访问原始 HDR 信息。当启用 HDR 纹理时,我们将“色调映射”延迟到“HDR 后处理效果”在我们的 渲染图 中运行之后。
通过将 BloomSettings
组件添加到启用 HDR 纹理的相机来启用光晕效果
commands.spawn((
Camera3dBundle {
camera: Camera {
hdr: true,
..default()
},
..default()
},
BloomSettings::default(),
));
如果配置错误,光晕效果可能会过于强烈。 BloomSettings
具有许多选项来对其进行调整,但最重要的是 intensity
,它可以(也应该)用于调整应用效果的程度。
说真的……这种效果可能很讨厌
在大多数情况下,最好保持微妙。
HDR 渲染也可以在 2D 中使用,这意味着你也可以在 2D 中使用光晕效果!
FXAA:快速近似抗锯齿 #
**Bevy 0.9** 添加了对 FXAA(快速近似抗锯齿)的支持。FXAA 是一种流行的(且廉价的!)抗锯齿方法,它使用亮度数据对比度来识别边缘并对其进行模糊处理
Bevy 已经支持 MSAA(多重采样抗锯齿),MSAA 在渲染几何体边缘时进行多次采样,这使得这些边缘更加清晰
选择抗锯齿实现都是权衡取舍
- MSAA:清晰、高质量的几何体边缘。保留图像的其他部分(如纹理和阴影)不受影响,这可能是一个优点(更清晰的输出)或缺点(更多锯齿)。比 FXAA 更昂贵。
- FXAA:在模糊时考虑整个图像,包括纹理,这可能是一个优点(纹理和阴影得到抗锯齿处理)或缺点(图像整体变得更加模糊)。运行成本低(对于移动或 Web AA 来说是一个不错的选择)。
现在,我们的后处理管道已经成熟,我们计划在未来的 Bevy 版本中添加更多抗锯齿选项。我们已经开始开发 TAA(时间抗锯齿)和 SMAA(亚像素形态抗锯齿)实现!
去带抖动 #
“颜色带状”是使用 8 位颜色通道(几乎所有设备/屏幕都需要)时已知的限制。
当尝试为低噪声纹理(例如,“纯绿色”材质的照明)渲染平滑梯度时,这一点最为明显
如果你仔细观察绿色平面 *或* 棕褐色立方体,你会注意到每个颜色阴影都有明显的条带。解决此问题的流行方法是对最终图像进行“抖动”。
Bevy 0.9 现在在色调映射阶段默认执行“去带抖动”
你可以按相机启用和禁用它
commands.spawn(Camera3dBundle {
tonemapping: Tonemapping::Enabled {
deband_dither: true,
},
..default()
});
后处理:视图目标双缓冲 #
渲染后处理效果需要输入纹理(包含“当前”渲染)和输出纹理(应用效果后的“新”渲染)。Bevy 的先前版本只有一个主要的“视图目标”图像。这意味着,在简单情况下,后处理效果需要管理并渲染到它们自己的“中间”纹理,然后将其 *写回* 主要目标。这显然效率低下,因为我们为每个效果都有一个新的纹理分配 *并且* 我们需要额外的工作将中间纹理复制回主要纹理。
为了解决这个问题,在 **Bevy 0.9** 中,我们现在对视图目标纹理进行“双缓冲”,这意味着我们拥有它们的两个副本,我们在这两个副本之间切换。在给定的时间点,一个副本是当前的“主要”纹理,而另一个副本是“下一个”主要纹理。后处理效果开发人员现在可以触发“后处理写入”,这将返回一个 source
和 destination
纹理。它假定效果将 source
写入 destination
(有或没有修改)。 destination
然后将成为新的“主要”纹理。
let post_process = view_target.post_process_write();
render_some_effect(render_context, post_process.source, post_process.destination);
这减少了后处理效果开发人员的复杂性负担,并使我们的管道保持良好且高效。新的 FXAA 效果 是使用这个新系统实现的。后处理插件开发人员可以使用该实现作为参考。
改进的渲染目标纹理格式处理 #
**Bevy 0.9** 现在检测并使用每个窗口/表面的首选 TextureFormat
,而不是使用硬编码的编译时选定的每平台格式。这意味着我们自动支持不常见的平台和配置。此外,Bevy 的主要通道和后处理通道现在渲染到稳定/一致的 TextureFormats
(例如:Rgba16Float
用于 HDR)。我们从这些“标准”纹理到最终渲染目标的首选格式执行最终的 blit。这简化了渲染管道构建,允许跨渲染目标(即使它们的格式不匹配)重复使用渲染管道,并提供一致且可预测的渲染管道行为。
这也意味着,在渲染到纹理时,纹理格式不再需要与表面的纹理格式匹配。例如,你现在可以渲染到仅具有红色通道的纹理
新的场景格式 #
**Bevy 0.9** 引入了一种 *大有改进* 的场景格式,它使场景更小、更容易手动组成,也更容易阅读。这是由对 Bevy Reflect(Bevy 的 Rust 运行时反射系统)的许多改进所支持的。对 Bevy 场景格式的大多数改进实际上是对所有 Bevy Reflect 序列化的通用改进!
// The New Bevy Scene Format
(
entities: {
0: (
components: {
"game::Player": (
name: "Reyna",
position: (
x: 0.0,
y: 0.0,
),
),
"game::Health": (
current: 5,
max: 10,
),
"game::Team": A,
},
),
1: (
components: {
"game::Player": (
name: "Sova",
position: (
x: 10.0,
y: 0.0,
),
),
"game::Health": (
current: 10,
max: 10,
),
"game::Team": B,
},
),
},
)
将其与旧格式进行比较
// The Old Bevy Scene Format
[
(
entity: 0,
components: [
{
"type": "game::Player",
"struct": {
"name": {
"type": "alloc::string::String",
"value": "Reyna",
},
"position": {
"type": "glam::f32::vec2::Vec2",
"struct": {
"x": {
"type": "f32",
"value": 0.0,
},
"y": {
"type": "f32",
"value": 0.0,
},
},
},
},
},
{
"type": "game::Health",
"struct": {
"current": {
"type": "usize",
"value": 5,
},
"max": {
"type": "usize",
"value": 10,
},
},
},
{
"type": "game::Team",
"value": A,
},
],
),
(
entity: 1,
components: [
{
"type": "game::Player",
"struct": {
"name": {
"type": "alloc::string::String",
"value": "Sova",
},
"position": {
"type": "glam::f32::vec2::Vec2",
"struct": {
"x": {
"type": "f32",
"value": 10.0,
},
"y": {
"type": "f32",
"value": 0.0,
},
},
},
},
},
{
"type": "game::Health",
"struct": {
"current": {
"type": "usize",
"value": 10,
},
"max": {
"type": "usize",
"value": 10,
},
},
},
{
"type": "game::Team",
"value": B,
},
],
),
]
有如此多的改进,你可能很难全部挑出来!
更简单的结构体语法 #
结构体现在使用结构体格式,而不是复杂的基于 map 的表示。
// Old
{
"type": "game::Health",
"struct": {
"current": {
"type": "usize",
"value": 5,
},
"max": {
"type": "usize",
"value": 10,
},
},
},
// New
"game::Health": (
current: 5,
max: 10,
),
更简单的原始序列化 #
类型现在可以选择直接使用 serde 序列化,这使得原始值更容易操作。
// Old
"name": {
"type": "alloc::string::String",
"value": "Reyna",
},
// New
name: "Reyna",
更友好的枚举语法 #
考虑枚举
pub enum Team {
A,
B,
}
让我们比较一下它的序列化方式
// Old
{
"type": "game::Team",
"value": A,
},
// New
"game::Team": A,
另外需要注意的是,Bevy Reflect 直到 **Bevy 0.9** 才直接支持枚举。旧版本的 Bevy 需要将 #[reflect_value]
与普通的 serde 结合使用来处理枚举,这要复杂得多。有关详细信息,请参阅本博文中的 枚举反射 部分!
更友好的元组 #
// Old
{
"type": "(f32, f32)",
"tuple": [
{
"type": "f32",
"value": 1.0
},
{
"type": "f32",
"value": 2.0
}
]
}
// New
{
"(f32, f32)": (1.0, 2.0)
}
顶级结构体 #
Bevy 场景现在有一个顶级结构体,这使得我们能够在未来向 Bevy 场景格式添加额外的值和元数据(例如版本号、ECS 资源、资产等)。
// Old
[
/* entities here */
]
// New
(
entities: (
/* entities here */
)
)
在适当的地方使用 Map #
实体 ID 和组件值在 Bevy ECS 中必须是唯一的。为了更好地体现这一点,我们现在使用 map 语法,而不是列表。
// Old
[
(
entity: 0,
components: [ ],
),
(
entity: 1,
components: [ ],
),
]
// New
(
entities: {
0: (
components: { },
),
1: (
components: { },
),
},
)
二进制场景格式 #
Bevy 场景可以序列化和反序列化为/从二进制格式,例如 bincode
、postcard
和 rmp_serde
。这需要在新的场景格式中添加对“非自描述”格式的支持。
在 postcard 的情况下,这可能小近 5 倍(对于上面的场景,小了 4.53 倍)!如果你试图保持场景在磁盘上的大小,或者通过网络发送场景,这非常有用。
动态场景构建器 #
Bevy 场景现在可以使用新的 DynamicSceneBuilder
动态构建。旧版本的 Bevy 已经支持 将“整个世界”写入场景,但在某些情况下,用户可能只想将特定实体写入场景。**Bevy 0.9** 的 DynamicSceneBuilder
使这成为可能
// Write players to a scene
fn system(world: &World, players: Query<Entity, With<Player>>) {
let builder = DynamicSceneBuilder::from_world(world);
builder.extract_entities(players.iter());
let dynamic_scene = builder.build();
}
extract_entities
接受任何 Entity
迭代器。
你也可以传入特定的实体
builder.extract_entity(entity);
更多场景构建工具 #
Scenes
现在可以克隆
let scene = scene.clone_with(type_registry).unwrap();
DynamicScenes
现在可以转换为 Scenes
let scene = Scene::from_dynamic_scene(dynamic_scene, type_registry).unwrap();
改进的实体/组件 API #
使用组件生成实体,以及从实体添加/删除组件变得更加容易!
首先是一些快速的基础知识:Bevy ECS 使用 Components
来向实体添加数据和逻辑。为了使实体组合更轻松,Bevy ECS 还具有 Bundles
,它定义了要一起添加的组件组。
就像 Bevy 的先前版本一样,Bundle 可以是组件的元组
(Player { name: "Sova" }, Health::new(10), Team::A)
Bundle
特性也可以推导出来
#[derive(Bundle)]
struct PlayerBundle {
player: Player,
health: Health,
team: Team,
}
在 **Bevy 0.9** 中,Component
类型现在也自动实现了 Bundle
特性,这使我们能够将所有实体组件操作整合到新的 spawn
、insert
和 remove
API 中。以前,我们有针对 Bundle
(例如:insert_bundle(SomeBundle)
)和 Component
(例如:.insert(SomeComponent)
)的不同变体。
Bundle
特性现在也为 Bundles
的元组实现,而不仅仅是 Components
的元组。这一点的价值将在稍后说明。
首先,spawn
现在接受一个 bundle
// Old (variant 1)
commands.spawn().insert_bundle(SpriteBundle::default());
// Old (variant 2)
commands.spawn_bundle(SpriteBundle::default());
// New
commands.spawn(SpriteBundle::default());
我们已经节省了一些字符,但我们才刚刚开始!因为 Component
实现了 Bundle
,我们现在也可以将单个组件传入 spawn
// Old
commands.spawn().insert(Player { name: "Sova" });
// New
commands.spawn(Player { name: "Sova" });
当我们将 Bundle
元组引入其中时,事情变得更加有趣,这使我们能够将许多操作(涵盖组件和 bundle)合并到单个 spawn
调用中
// Old
commands
.spawn_bundle(PlayerBundle::default())
.insert_bundle(TransformBundle::default())
.insert(ActivePlayer);
// New
commands.spawn((
PlayerBundle::default(),
TransformBundle::default(),
ActivePlayer,
));
这更容易键入和阅读。除此之外,从 Bevy ECS 的角度来看,这是一个单一的“bundle spawn”,而不是多个操作,这减少了 “原型移动”。这使得这个单一的生成操作更加高效!
这些原则也适用于 insert API。
// Old
commands
.insert_bundle(PlayerBundle::default())
.insert(ActivePlayer);
// New
commands.insert((PlayerBundle::default(), ActivePlayer));
它们也适用于 remove API。
// Old
commands
.remove_bundle::<PlayerBundle>()
.remove::<ActivePlayer>();
// New
commands.remove::<(PlayerBundle, ActivePlayer)>();
排他性系统重构 #
为了为在最新合并的(但尚未实施的)无阶段 RFC 中概述的更大规模的调度程序更改做准备,我们已经开始模糊“排他性系统”(具有对 ECS World
的“排他性”完全可变访问权限的系统)和普通系统之间的界限,这些系统在历史上是具有严格界限的不同类型。
在 **Bevy 0.9** 中,排他性系统现在实现了正常的 System
特性!这最终将带来更大的影响,但在 **Bevy 0.9** 中,这意味着你不再需要在将排他性系统添加到调度程序时调用 .exclusive_system()
fn some_exclusive_system(world: &mut World) { }
// Old
app.add_system(some_exclusive_system.exclusive_system())
// New
app.add_system(some_exclusive_system)
我们还扩展了排他性系统以支持更多系统参数,这极大地改善了编写排他性系统的用户体验,并通过在执行之间缓存状态来提高效率。
SystemState
使得能够在排他性系统内部使用“普通”系统参数
// Old
fn some_system(world: &mut World) {
let mut state: SystemState<(Res<Time>, Query<&mut Transform>)> =
SystemState::new(&mut world);
let (time, mut transforms) = state.get_mut(world);
}
// New
fn some_system(world: &mut World, state: &mut SystemState<(Res<Time>, Query<&mut Transform>)>) {
let (time, mut transforms) = state.get_mut(world);
}
QueryState
使得能够缓存对单个查询的访问
// Old
fn some_system(world: &mut World) {
let mut transforms = world.query::<&Transform>();
for transform in transforms.iter(world) {
}
}
// New
fn some_system(world: &mut World, transforms: &mut QueryState<&Transform>) {
for transform in transforms.iter(world) {
}
}
Local
使得能够在排他性系统内部存储本地数据
// Old
#[derive(Resource)]
struct Counter(usize);
fn some_system(world: &mut World) {
let mut counter = world.resource_mut::<Counter>();
counter.0 += 1;
}
// New
fn some_system(world: &mut World, mut counter: Local<usize>) {
*counter += 1;
}
Bevy ECS 现在使用 GATS!#
Rust 1.65.0 稳定了 GATs(泛型关联类型),这使得我们能够显著简化 Bevy ECS 查询内部。
有一段时间,Bevy ECS 一直在用一个复杂的特性嵌套(WorldQuery
、WorldQueryGats
(一个“缺少真正的 GATs”的特性)和 Fetch
)来解决缺少 GATs 的问题。
在 **Bevy 0.9** 中,我们现在只有一个 WorldQuery
特性!这使得 Bevy ECS 更易于维护、扩展、调试、记录和理解。
推导资源 #
Resource
特性现在不再自动为所有类型实现。它必须推导出来
#[derive(Resource)]
struct Counter(usize);
这个改变是在 对 Component
类型做出相同决定 之后进行的。简而言之
自动为每种类型实现
Resource
使得非常容易意外地插入“错误”的值,例如插入构造函数指针而不是值本身struct Counter(usize); // This inserts the constructor function pointer as a resource! // Weird and confusing! app.insert_resource(Counter); // This is how it should be done! app.insert_resource(Counter(0));
推导
Resource
以结构化的方式记录意图。如果没有推导,资源性默认情况下是隐式的。自动实现意味着插件可以在冲突的方式(例如:
std
类型,例如Vec<usize>
)中使用相同的“通用”类型。默认情况下不实现意味着插件不能以冲突的方式使用这些通用类型。他们必须创建新的类型。这为使用 Rust 类型系统配置资源类型打开了大门(就像我们已经对组件所做的那样)。
系统歧义解决 API 改进 #
Bevy ECS 默认情况下会并行调度系统。它会安全地并行调度系统,遵守系统之间的依赖关系并强制执行 Rust 的可变性规则。默认情况下,这意味着如果系统 A 读取资源,而系统 B 写入资源(并且它们之间没有定义任何顺序),那么系统 A 可能会在系统 B之前或之后执行。我们称这些系统为“歧义”系统。在某些情况下,这种歧义可能很重要,而在其他情况下可能无关紧要。
Bevy 已经有一个 系统歧义检测系统,它允许用户检测歧义系统并解决歧义(要么通过添加排序约束,要么通过忽略歧义)。用户可以将系统添加到“歧义集合”中,以忽略这些集合中系统之间的歧义
#[derive(AmbiguitySet)]
struct AmbiguousSystems;
app
.add_system(a.in_ambiguity_set(AmbiguousSystems))
.add_system(b.in_ambiguity_set(AmbiguousSystems))
这有点难以理解,并且比必要时引入了更多的样板代码。
在 **Bevy 0.9** 中,我们已经用更简单的 ambiguous_with
调用替换了歧义集合
app
.add_system(a)
.add_system(b.ambiguous_with(a))
这基于现有的 [SystemLabel
] 方法,这意味着你也可以使用标签来实现“集合式”歧义解决
#[derive(SystemLabel)]
struct Foo;
app
.add_system(a.label(Foo))
.add_system(b.label(Foo))
.add_system(b.ambiguous_with(Foo))
Bevy ECS 优化 #
我们在 **Bevy 0.9** 中获得了巨大的性能提升,这要归功于 @james7132
- 查询获取抽象 已重新设计,以便将常见部分提升到单个迭代之外,从而在某些基准测试中将迭代器性能提高了约 10-20%。
Query::get
的性能也有一些改进。 - 从我们的数据访问 API 中删除了一些不必要的分支,从而将我们大多数 ECS 基准测试的性能提高了约 5-20%!
- 并行执行器 现在在
prepare_systems
步骤运行时开始运行系统,当有很多系统几乎没有工作要做时,消除了很多延迟。这从我们的many_foxes
动画基准测试中削减了近 1 毫秒(约 12% 的改进)。这是一个非常大的进步! - 迭代器现在 在迭代查询时跳过空原型和表,这在原型为空时显著减少了每原型迭代开销。
@JoJoJet
还优化了 Query::get_many
访问,方法是使用循环替换 array::map
,将 get_many
优化了约 20-30%!
ECS 更改检测旁路 #
Bevy ECS 通过一些非常精妙的 Rust 用法自动检测对组件和资源的更改。
但是,有时用户可能会进行他们不希望被检测到的更改。在 **Bevy 0.9** 中,更改检测现在可以绕过
fn system(mut transforms: Query<&mut Transform>) {
for transform in &mut transforms {
transform.bypass_change_detection().translation.x = 1.0;
}
}
枚举反射 #
Bevy Reflect 现在原生支持 Rust 枚举!Bevy Reflect 是 Bevy 的“Rust 反射系统”,它允许我们在运行时动态地访问值的 Rust 类型信息。
在 Bevy 的早期版本中,我们需要通过将枚举类型视为“反射值”来绕过 Bevy Reflect 缺乏枚举支持的限制,这需要为每种类型做更多工作,而且它提供的关于该类型的反射信息更少。
// Old
#[derive(Copy, Clone, PartialEq, Debug, Default, Serialize, Deserialize, Reflect)]
#[reflect_value(PartialEq, Serialize, Deserialize)]
enum SomeEnum {
A,
B(usize),
C {
foo: f32,
bar: bool,
},
}
// New
#[derive(Reflect)]
enum SomeEnum {
A,
B(usize),
C {
foo: f32,
bar: bool,
},
}
不再需要魔法咒语了!
就像其他反射类型一样,枚举反射提供了许多新的运行时功能。
// Access variant names
let value = SomeEnum::A;
assert_eq!("A", value.variant_name());
// Get the variant type
match value.variant_type() {
VariantType::Unit => {},
VariantType::Struct => {},
VariantType::Tuple => {},
}
let mut value = SomeEnum::C {
foo: 1.23,
bar: false
};
// Read/write specific fields by name
*value.field_mut("bar").unwrap() = true;
// Iterate over the entire collection of fields
for field in value.iter_fields() {
}
// Detect the value type and retrieve information about it
if let TypeInfo::Enum(info) = value.type_info() {
if let VariantInfo::Struct(struct_info) = value.variant("C") {
let first_field = struct_info.field_at(0).unwrap();
assert_eq!(first_field.name(), "foo");
}
}
在枚举上派生 Reflect
也会自动添加对“基于反射的序列化”的支持,从 Bevy 0.9 开始,现在 有了一个更友好的语法。
其他 Bevy Reflect 改进 #
我们对 Bevy Reflect 做了许多其他改进!
“容器”反射特征(Map、List、Array、Tuple)现在可以被清空以获取所有权值。
let container: Box<dyn List> = Box::new(vec![1.0, 2.0]);
let values: Vec<Box<dyn Reflect>> = container.drain();
反射字段现在可以选择不进行序列化,而不会选择不进行整个反射。
#[derive(Reflect)]
struct Foo {
a: i32,
// fully invisible to reflection, including serialization
#[reflect(ignore)]
b: i32,
// can still be reflected, but will be skipped when serializing
#[reflect(skip_serializing)]
c: i32,
}
装箱的“反射类型”特征(Struct、Enum、List 等)现在可以转换为更通用的 Box<dyn Reflect>
。
let list: Box<dyn List> = Box::new(vec![1.0, 2.0]);
let reflect: Box<dyn Reflect> = list.into_reflect();
现在可以获取反射类型的拥有所有权的变体。
let value: Box<Sprite> = Box::new(Sprite::default());
if let ReflectOwned::Struct(owned) = value.reflect_owned() {
// owned is a Box<dyn Struct>
}
“反射路径 API”中的数组现在可以使用列表语法。
#[derive(Reflect)]
struct Foo {
bar: [u8; 3],
}
let foo = Foo {
bar: [10, 20, 30],
};
assert_eq!(*foo.get_path("bar[1]").unwrap(), 20);
反射的 List 现在有一个 pop 操作。
let mut list: Box<dyn List> = Box::new(vec![1u8, 2u8]);
let value: Box<dyn Reflect> = list.pop().unwrap();
assert_eq!(*value.downcast::<u8>().unwrap(), 2u8);
示例:游戏手柄查看器 #
Bevy 现在有一个游戏手柄输入查看器应用程序,它可以使用 cargo run --example gamepad_viewer
从 Bevy 仓库运行。
轴和按钮设置验证 #
[InputAxis
] 和 [ButtonSettings
] 现在使用 getter 和 setter 来确保设置的完整性。setter 会返回错误,而不是允许无效状态。
例如,尝试将按钮的“按下阈值”设置为低于“释放阈值”的值会导致错误。
button_settings.set_release_threshold(0.65);
// this is too low!
assert!(button_settings.try_set_press_threshold(0.6).is_err())
ScanCode 输入资源 #
Bevy 0.9 添加了一个 Input<ScanCode>
资源,它像 Input<KeyCode>
一样,但忽略键盘布局。
fn system(scan_code: Res<Input<ScanCode>>, key_code: Res<Input<KeyCode>>) {
// 33 is the scan code for F on a physical keyboard
if scan_code.pressed(ScanCode(33)) {
log!("The physical F key is pressed on the keyboard");
}
if keycode.pressed(KeyCode::F) {
log!("The logical F key is pressed on the keyboard, taking layout into account.");
}
}
时间着色器全局变量 #
Bevy 着色器终于可以访问内置时间值,无需用户手动计算和传递时间值。时间在着色器中非常有用,因为它为动画化值打开了大门。
这是一个使用时间在黑色和红色之间进行动画化的简单着色器。
@fragment
fn fragment(in: FragmentInput) -> @location(0) vec4<f32> {
return vec4<f32>(sin(globals.time * 10.0), 0.0, 0.0, 1.0);
}
Bevy 着色器现在可以访问以下全局变量
time
:自启动以来的时间(以秒为单位),在 1 小时后重置为 0。delta_time
:自上一帧以来的时间(以秒为单位)。frame_count
:自应用程序启动以来的帧数,在达到u32
的最大值后重置为 0。
高实体渲染器减速优化 #
Bevy 的渲染器 在“主世界”和“渲染世界”之间同步实体状态,这使得并行流水线渲染成为可能。为了实现这一点,我们每帧清除渲染实体以确保提取状态的完整性。
然而,很明显,我们用于每帧清除实体的方法会产生每实体的成本,在非常高的实体数量下,这种成本会变得很明显。
在 Bevy 0.9 中,我们显著优化了实体清除,将清除 5,000,000 个实体的成本从大约 360 微秒降低到大约 120 微秒。我们还在考虑一个“保留状态”提取模型,它依托于 Bevy ECS 的内置变更检测,这将完全消除清除实体的需要(并且更一般地优化提取过程)。但是,实现这一点将是一项更大的工作量!
顶点属性完全可选 #
在之前的版本中,我们 通过专门针对网格顶点属性使顶点属性可选。但我们保留了一些常用属性作为必需项:位置和法线。Bevy 0.9 完成了这项工作。现在所有标准网格顶点属性都完全可选。如果你的网格由于某种原因不需要位置,Bevy 不会阻止你!
公开多绘制间接 #
Wgpu 对支持它们的平台上的“多绘制间接” API 提供了选择性支持,这些 API 是实现高效“GPU 驱动渲染”的关键部分。Bevy 现在通过其“跟踪渲染通道”抽象公开这些 API,使开发者能够使用这些 API 构建渲染功能。
KTX2 数组/立方体贴图/立方体贴图数组纹理 #
Bevy 现在可以正确加载 KTX2 数组、立方体贴图和立方体贴图数组纹理资产,这为天空盒等场景打开了大门。
Bevy 还没有对天空盒提供高级支持,但我们有一个示例 说明用户如何实现此功能。
Camera::viewport_to_world #
通常希望将“屏幕上”的位置转换为指向该位置从相机向外发出的射线。例如,如果你想在 3D 场景中点击某个东西来选择它,你可能会从相机视角中的那个点投射一条射线,看看它是否与场景中的任何“碰撞器”相交。
Bevy 相机现在有一个 viewport_to_world
函数,它提供了此功能。
let ray = camera.viewport_to_world(transform, cursor_position).unwrap();
if let Some(entity) = physics_context.cast_ray(ray.origin, ray.direction) {
// select entity
}
以下由光标驱动的选择使用 viewport_to_world
计算从光标“射出”的射线,然后将其馈送到 bevy_rapier
物理库以检测和拾取光标下的卡片。
多个方向光 #
Bevy 现在支持多个方向光(新限制为一次 10 个)。就像我们对点光源所做的那样,我们很可能在将来为支持存储缓冲区的平台使其无限制,但这只是朝着保持所有平台兼容性的方向迈出的第一步。
精灵矩形 #
Sprites
现在可以定义“矩形”,这些矩形选择其纹理的特定区域用作“精灵”。
Sprite {
rect: Some(Rect {
min: Vec2::new(100.0, 0.0),
max: Vec2::new(200.0, 100.0),
}),
..default()
}
这类似于 TextureAtlasSprite
/ “精灵表”的工作方式,但不需要定义纹理图集。
插件设置 #
在 Bevy 的早期版本中,“不可变”插件设置表示为普通的 ECS 资源,这些资源在插件初始化时被读取。这带来了许多问题。
- 如果用户在插件初始化后插入插件设置资源,它将被静默忽略(并使用默认值)。
- 用户可以在插件初始化后修改插件设置资源。这给用户造成了一种对无法更改的设置拥有控制权的错觉。
对于 WindowDescriptor
资源来说,这些问题尤其严重且令人困惑,但它是一个普遍问题。
为了解决这个问题,在 Bevy 0.9 中,我们将插件设置移到了插件本身,并创建了用于覆盖默认设置的新 API。
app.add_plugins(DefaultPlugins
.set(AssetPlugin {
watch_for_changes: true,
..default()
})
.set(WindowPlugin {
window: WindowDescriptor {
width: 400.0,
..default()
},
..default()
})
)
这使得设置和插件之间的联系变得清晰,并将这些“插件初始化”设置与“运行时可配置”设置(仍然表示为 ECS 资源)区分开来。
插件现在默认情况下是唯一的 #
插件现在默认情况下是唯一的。尝试将唯一的插件多次添加到应用程序会导致错误。不打算唯一的插件可以覆盖默认的 is_unique
方法。
impl Plugin for MyPlugin {
fn build(&self, app: &mut App) {
app.add_system(some_system);
}
fn is_unique(&self) -> bool {
false
}
}
任务池:作用域上的嵌套生成 #
Bevy 的任务池现在支持“作用域上的嵌套生成”。
let results = task_pool.scope(|scope| {
scope.spawn(async move {
scope.spawn(async move { 1 });
2
});
});
assert!(results.contains(&1));
assert!(results.contains(&2));
这使得在执行其他任务时可以向任务池作用域添加新任务!这是实现新合并(但尚未实现)的 无阶段 RFC 的要求,但它为任何在 Bevy 中生成异步任务的人提供了新的模式!
任务池恐慌处理 #
Bevy 使用它自己的自定义异步任务池来管理调度并行、异步任务。在 Bevy 的早期版本中,如果任务在这些池中的一个中出现恐慌,则除非每个计划的任务使用 catch_unwind
(这不可行),否则将无法恢复。这也将永久杀死全局任务池中的工作线程。
Bevy 0.9 通过在任务池执行器中调用 catch_unwind
来解决这个问题。
层次结构查询方法 #
为了便于导航层次结构,我们在 Query<&Children>
和 Query<&Parent>
中添加了一些便捷方法。
#[derive(Resource)]
struct SomeEntity(Entity);
fn system(children: Query<&Children>, some_entity: Res<SomeEntity>) {
// iterate all descendents of some_entity
for entity in children.iter_descendants(some_entity.0) {
}
}
fn other_system(parents: Query<&Parent>, some_entity: Res<SomeEntity>) {
// iterate all ancestors of some_entity
for entity in parents.iter_ancestors(some_entity.0) {
}
}
Bevy UI:原点现在位于左上角 #
Bevy UI 现在认为窗口的“左上角”为“原点”,它向“下方”扩展(Y 向下)。为了说明这一点,请考虑以下情况,其中在“默认”位置(原点)生成了一个小部件。
左上角原点(新) #
左下角原点(旧) #
我们之所以做出这个改变,是因为几乎整个 UI 生态系统都使用左上角作为原点(Web、Godot、GTK、GPU 图像等)。
在 Bevy 还处于起步阶段的早期,我(@cart
)最初选择了左下角(Y 向上)以与 Bevy 的世界空间二维和三维坐标系保持一致。从理论上讲,我认为这将使所有事情更容易推理。但实际上,事实证明,这种一致性并没有给我们带来任何好处。而且当涉及 UI 默认行为时,这种行为违背了用户的预期。UI 倾向于向下扩展(从顶部开始),而不是向上扩展(从底部开始),因此覆盖默认值是一种常见的做法。
幸运的是,在 Bevy 0.9 中,我们现在与生态系统的其他部分保持一致!
Bevy UI:Z 索引 #
Bevy UI 元素现在对它们的“Z 索引”(它们是“在前面”还是“在后面”)有了更多控制。在 Bevy 的早期版本中,这完全由层次结构决定:子项堆叠在父项和较早的兄弟姐妹之上。这是一个很好的“默认值”,适用于大部分 UI,但某些类型的 UI 需要对元素排序进行更多控制。
如果你是一名前端开发者,你曾经使用过 z-index
CSS 属性,那么我们现在讨论的问题就是你遇到的问题。
Bevy 0.9 添加了一个新的 ZIndex
组件,它是一个枚举,包含两种模式。
ZIndex::Local(i32)
:覆盖相对于其兄弟姐妹的深度。ZIndex::Global(i32)
:覆盖相对于 UI 根节点的深度。设置它实际上允许 UI 元素“逃离”相对于其父节点的 Z 顺序,而是相对于整个 UI 进行排序。
在上下文中(本地 vs 全局)具有较高 Z 级的 UI 项将显示在具有较低 Z 级的 UI 项的前面。Z 级的平局会回退到层次结构顺序。“后面的”子项堆叠在“前面的”子项之上。
为了说明这一点,请考虑以下 UI。
root (green)
child1 (red)
child2 (blue)
默认情况下,它们都具有 0 的 Z 索引。根节点位于底部,每个后续子项都“堆叠”在“顶部”。
如果我们希望蓝色子节点堆叠在之前的红色子节点“后面”,我们可以将其 z-index 设置为小于默认值 0 的“局部”值。
blue.z_index = ZIndex::Local(-1);
如果我们希望蓝色子节点堆叠在绿色根节点“后面”,我们可以将其 z-index 设置为小于默认值 0 的“全局”值。
blue.z_index = ZIndex::Global(-1);
非常有用的东西!
Bevy UI 缩放 #
Bevy UI 的全局“像素缩放”现在可以使用 UiScale
资源来设置。
// Render UI pixel units 2x bigger
app.insert_resource(UiScale { scale: 2.0 })
这允许开发人员在需要灵活性的情况下向用户公开任意缩放配置。
音频播放切换 #
现在可以切换音频播放,它将在播放和暂停之间切换。
// Old, manual toggling (still possible)
if audio_sink.is_paused() {
audio_sink.play();
} else {
audio_sink.pause();
}
// New, automatic toggling
audio_sink.toggle();
时间缩放 #
现在可以在 Time
上配置“全局”时间缩放,这会缩放诸如 Time::delta_seconds()
之类的常用函数返回的值。
time.set_relative_speed(2.0);
在需要未缩放的值的情况下,可以使用这些函数的新的“原始”变体。
// The number of seconds elapsed since the last update, with time scaling taken into account.
let delta = time.delta_seconds();
// The number of seconds elapsed since the last update, with time scaling ignored.
let raw_delta = time.raw_delta_seconds();
时间包装 #
在某些情况下,例如着色器,需要将经过的时间值表示为 f32
,这很快就会遇到精度问题。为了解决这个问题,Time
已扩展为支持“时间包装”。
// Wrap once every hour
time.wrapping_period = Duration::from_secs(60 * 60):
// If one hour and 6 seconds have passed since the app started,
// this will return 6 seconds.
let wrapped = time.seconds_since_startup_wrapped_f32();
下一步是什么? #
以下是一些事项。
- 高级后期处理堆栈:现在我们已经有了核心后期处理管道,我们需要创建一个更高级的系统,使用户能够更轻松地选择、配置和重新排序每个摄像机的后期处理效果。此外,出于性能原因,我们希望尽可能将多个后期处理效果组合到一个通道中,因此我们需要一套能够促进这一过程的后期处理 API。
- 更多后期处理效果:更多抗锯齿选项(TAA、SMAA)、更多色调映射算法选项(例如 ACES)、SSAO。
- 资产预处理:我们将大力投入资产管道,重点关注以下方面。
- 预处理资产以在“开发期间”完成昂贵的操作,以便 Bevy 应用程序可以部署具有更漂亮、更小和/或加载速度更快的资产。
- 启用使用 .meta 文件配置资产。例如,您可以定义纹理压缩级别、它应该使用的过滤器或目标格式。
- Bevy UI 改进:我们将继续改进 Bevy UI 的功能并扩展其窗口小部件库,重点关注启用编辑器体验。
- 更多场景改进:嵌套场景、隐式默认值和内联资产。
- Bevy 编辑器:我们将开始对 Bevy 编辑器体验进行原型设计,首先是场景编辑器工具。
- 无阶段 ECS:现在 无阶段 RFC 已合并,我们可以开始实现无阶段调度!有关即将推出的改进概述,请参阅 RFC。这将是一场变革!
我们还在寻找一些关键领域的专家。我们目前的大多数开发人员都专注于上述工作,因此,如果您对以下领域感兴趣并有经验,我们很乐意与您取得联系!
- 动画:动画混合、程序化动画和更高级别的动画系统。查看 GitHub 上标记为
A-Animation
的问题,并在我们 Discord 的#animation-dev
频道上自我介绍。 - 音频:我们需要对音频播放有更多控制,尤其是在叠加效果方面。查看 GitHub 上标记为
A-Audio
的问题,并在我们 Discord 的#audio-dev
频道上自我介绍。
支持 Bevy #
赞助有助于使我们在 Bevy 上的工作可持续发展。如果您相信 Bevy 的使命,请考虑赞助我们……每一份帮助都很重要!
- Carter Anderson (@cart):Bevy 的全职首席开发人员、项目经理和创建者。专注于构建核心引擎系统、指导项目方向和管理社区。
- Alice Cecile (@alice-i-cecile):技术项目经理、疯狂科学家和文档主管。虽然她经常领导探索新领域的探险队,但 ECS 将永远是她的家。
- François Mockers (@mockersf):CI 专家。确保一切顺利运行,并通过一次又一次的 PR 改进 Bevy。
- Rob Swain (@superdump):光之掌控者。将数据变成闪耀的东西,并实现大规模并行化。目前正在业余时间进行黑客攻击,所以请捐赠或赞助团队的其他成员。❤️
贡献者 #
衷心感谢使此次发布(以及相关文档)成为可能的 159 位贡献者!按随机顺序列出。
- @Edwox
- @targrub
- @fvacek
- @xtr3m3nerd
- @timokoesters
- @Suficio
- @Sergi-Ferrez
- @hymm
- @MrGVSV
- @SleepySwords
- @nicopap
- @Vrixyz
- @McSpidey
- @VitalyAnkh
- @ramirezmike
- @jiftoo
- @TheNeikos
- @ManevilleF
- @KDecay
- @Zearin
- @marlyx
- @StarArawn
- @Ixentus
- @hmeine
- @emersonmx
- @gilescope
- @inodentry
- @robtfm
- @yrns
- @Lucidus115
- @kurtkuehnert
- @zmarlon
- @leereilly
- @galkowskit
- @DGriffin91
- @Ptrskay3
- @strattonbrazil
- @Pand9
- @PROMETHIA-27
- @zicklag
- @lewiszlw
- @contagnas
- @EMachad0
- @SpecificProtagonist
- @BoxyUwU
- @jkb0o
- @xgbwei
- @andresovela
- @0x182d4454fb211940
- @TehPers
- @pcone
- @CleanCut
- @makspll
- @64kramsystem
- @Wandalen
- @coreh
- @Fracey
- @Azervu
- @SyamaMishra
- @BeastLe9enD
- @Weibye
- @Pietrek14
- @NiklasEi
- @TheRawMeatball
- @jgoday
- @7flash
- @light4
- @Ian-Yy
- @Carter0
- @slyedoc
- @devil-ira
- @MDeiml
- @NathanSWard
- @robem
- @Bleb1k
- @bzm3r
- @anchpop
- @aevyrie
- @amiani
- @x3ro
- @NoahShomette
- @bjorn3
- @djeedai
- @bwhitt7
- @oceantume
- @micron-mushroom
- @JMS55
- @asherkin
- @afonsolage
- @shuoli84
- @harudagondi
- @Demiu
- @TimJentzsch
- @gak
- @dataphract
- @raffimolero
- @Moulberry
- @james7132
- @torsteingrindvik
- @jakobhellermann
- @hakolao
- @themasch
- @CatThingy
- @Metadorius
- @merelymyself
- @SludgePhD
- @CGMossa
- @sullyj3
- @ian-h-chamberlain
- @lain-dono
- @mwcz
- @thebluefish
- @manokara
- @mirkoRainer
- @hankjordan
- @cryscan
- @WaffleLapkin
- @mahulst
- @AlexOkafor
- @Davier
- @jwagner
- @CAD97
- @alice-i-cecile
- @james-j-obrien
- @rparrett
- @tguichaoua
- @YohDeadfall
- @msvbg
- @komadori
- @maniwani
- @Shatur
- @LarsDu
- @DJMcNab
- @JoJoJet
- @polarvoid
- @KirmesBude
- @Aceeri
- @ottah
- @IceSentry
- @Piturnah
- @lovelymono
- @maxwellodri
- @oledfish
- @BorisBoutillier
- @mockersf
- @Nilirad
- @elbertronnie
- @maccesch
- @vertesians
- @superdump
- @wanderrful
- @Neo-Zhixing
- @rustui
- @cart
- @JohnTheCoolingFan
- @pascualex
- @fishykins
- @Carlrs
- @leath-dub
完整变更日志 #
添加了 #
- 绽放
- 添加 FXAA 后期处理
- 通过在量化之前抖动图像来修复颜色带状现象
- 插件拥有自己的设置。重新设计 PluginGroup 特性。
- 添加全局时间缩放
- 向网格视图绑定组添加全局变量
- 添加 UI 缩放
- 为 Timer 添加 FromReflect
- 在
time_system
中重新添加本地布尔值has_received_time
- 为 Timer 和 Stopwatch 添加 Serialize 和 Deserialize 的默认实现
- 向 Time 添加时间包装
- Stopwatch 经过时间秒数 f64
- Timer 中剩余的函数
- 在 KTX2 中支持数组/立方体贴图/立方体贴图数组纹理
- 添加用于消除系统顺序歧义警告的方法
- bevy_dynamic_plugin:使其能够处理加载错误
- 可以从应用程序中获取插件的设置
- 仅在设置时使用资源的插件设置
- 添加
TimeUpdateStrategy
资源以手动更新Time
- 动态场景构建器
- 从动态场景创建场景
- 场景示例:在任务中写入文件
- 向场景示例添加场景数据的写入
- 可以克隆场景
- 添加“主通道后期处理结束”渲染图节点
- 添加
Camera::viewport_to_world
- 精灵:允许使用图像的子区域(矩形)
- 添加 bevy_math 类型缺失的类型注册
- 向
bevy_core
添加serialize
特性 - 向 bevy_transform 添加 serialize 特性
- 向
Transform
及其朋友添加关联常量IDENTITY
。 - bevy_reflect:添加
Reflect::into_reflect
- 添加 reflect_owned
Reflect
用于Tonemapping
和ClusterConfig
- 向 std 类型添加
ReflectDefault
- 为 Visibility 添加 FromReflect
- 在
CameraPlugin
中注册RenderLayers
类型 - 启用构造 ReflectComponent/Resource
- 支持多个
#[reflect]
/#[reflect_value]
+ 改进错误消息 - Reflect Default 用于 GlobalTransform
- 为 PathBuf 和 OsString 实现 Reflect
- Reflect Default 用于
ComputedVisibility
和Handle<T>
- 注册
Wireframe
类型 - 为
Transform
和GlobalTransform
导出FromReflect
- 使数组在反射中表现得像列表一样
- 为动态类型实现
Debug
- 为所有范围实现
Reflect
- 为
List
特性添加pop
方法。 - bevy_reflect:
GetTypeRegistration
用于SmallVec<T>
- 注册缺失的反射类型
- bevy_reflect:获取所有字段
- bevy_reflect:向 prelude 添加
FromReflect
- 为
Input<T>
实现Reflect
,对 reflect value 导出进行一些杂项改进 - 为反射注册
Cow<'static, str>
- bevy_reflect:放宽
Option<T>
的边界 - 删除
ReflectMut
,转而使用Mut<dyn Reflect>
- 向错误消息添加来自
ReflectPathError
的一些信息 - 为非零整数类型添加了反射/从反射实现
- bevy_reflect:更新枚举导出
- 添加
reflect(skip_serializing)
,它保留反射,但禁用自动序列化 - bevy_reflect:反射枚举
- 在 bevy_ecs、bevy_reflect 和 bevy 中禁用默认特性支持
- 公开窗口 alpha 模式
- 使 bevy_window 和 bevy_input 事件可序列化
- 添加窗口调整大小示例
- 功能:添加 GamepadInfo,公开游戏手柄名称
- 为输入类型导出
Reflect
+FromReflect
- 使 TouchInput 和 ForceTouch 可序列化
- 向示例添加游戏手柄查看器工具
- 为
bevy_input
事件导出Copy
特性,为bevy_input
和bevy_windows
中的事件导出Serialize
/Deserialize
,为两者中的事件导出PartialEq
,并尽可能为两者导出Eq
。 - 支持更多手柄按钮和轴
- 添加键盘扫描输入事件
- 在
EntityCommands
中添加set_parent
和remove_parent
- 在
Query<&Children>
和Query<&Parent>
中添加方法,用于迭代子代和祖先 - 在
Task<T>
中添加is_finished
- 在 bevy_math/glam 中公开 mint 特性
- Val 的实用程序方法
- 注册缺失的 bevy_text 类型
- 为
UiRect
添加额外的构造函数,用于指定特定字段的值 - 为
Size
添加 AUTO 和 UNDEFINED 常量构造函数 - 在诊断中添加指数移动平均值
- 在
WorldCell
中添加send_event
及其相关方法 - 添加一个方法,用于访问
Table
的宽度 - 在 World 中添加 iter_entities #6228
- 为 App、Stage、Schedule、Query、QueryState 等添加 Debug 实现
- 添加一个方法,用于映射
Mut<T>
->Mut<U>
- 实现 #[bundle(ignore)]
- 允许通过
World::resource_scope
访问非发送资源 - 在 Commands 中添加 get_entity
- 添加了获取或设置系统上次更改时间戳的能力。
- 添加一个模块,用于常见的系统
chain
/pipe
适配器 - 当前系统名称的 SystemParam
- 缺少事件的警告消息
- 添加更改检测旁路,并手动控制更改时间戳
- 在 EntityMut 中添加 into_world_mut
- 在
Local<T>
中为T
添加FromWorld
约束 - 为 EntityRef 添加
From<EntityMut>
(修复 #5459) - 为 ECS 包装类型实现 IntoIterator。
- 添加
Res::clone
- 添加 CameraRenderGraph::set
- 使用 wgsl saturate
- 为
Visibility
组件添加 mutatingtoggle
方法 - 在 mesh2d 中添加全局结构体
- 添加对 .comp glsl 着色器的支持
- 为
&Extract<P>
实现IntoIterator
- 为 Circle 添加 Debug、Copy、Clone 派生
- 为 Image 添加 TextureFormat::Rg16Unorm 支持,并为 SpecializedComputePipelines 派生 Resource
- 将
bevy_render::texture::ImageSettings
添加到 prelude - 将
Projection
组件添加到 prelude。 - 公开
Image
转换函数 (修复 #5452) - 加载内部二进制资产的宏
- 为
AssetPath<'a>
添加From<String>
- 为 AssetPath 添加 Eq & PartialEq
- 添加
ReflectAsset
和ReflectHandle
- 在 web 上使用 load_folder 时添加警告
- 在 bevy_audio 中公开 rodio 的 Source 和 Sample 特性
- 添加一种方法来切换
AudioSink
更改 #
- 分离色调映射和上采样通道
- 重新设计 ViewTarget 以更好地支持后期处理
- bevy_reflect:进一步改进序列化格式
- bevy_reflect:二进制格式
- 独特的插件
- 支持任意 RenderTarget 纹理格式
- 使
Resource
特性为 opt-in,需要#[derive(Resource)]
V2 - 将
WorldQueryGats
特性替换为实际 gats - 将 UI 坐标系改为以左上角为原点
- 将光标原点移回左下角
- 添加 z-index 支持,并提供可预测的 UI 堆栈
- TaskPool 恐慌处理
- 为
Component
实现Bundle
。使用Bundle
元组进行插入 - Spawn 现在接受一个 Bundle
- 使
WorldQuery
非常扁平 - 接受 Bundles 进行插入和移除。弃用 insert/remove_bundle
- 独占系统现在实现
System
。灵活的独占系统参数 - bevy_scene:将实体序列化为映射
- bevy_scene:在
DynamicSceneBuilder
中稳定实体顺序 - bevy_scene:将根列表替换为结构体
- bevy_scene:为场景
components
使用映射 - 在 prepare_systems 运行时开始运行系统
- 将资源提取到它们自己的专用存储中
- 在渲染器初始化后获取正确的纹理格式,修复 #3897
- 为
InputAxis
和ButtonSettings
添加 getter 和 setter - 清理 Fetch 代码
- 在作用域上嵌套生成
- 在迭代查询时跳过空原型和表格
- 将
MAX_DIRECTIONAL_LIGHTS
从 1 增加到 10 - bevy_pbr:规范化蒙皮法线
- 移除强制性网格属性
- 将
play
重命名为start
,并添加新的play
方法,该方法不会覆盖现有的动画(如果它正在播放)。 - 将
Timer
的bool
参数替换为TimerMode
- 改进 add_system_to_stage 和 add_system_set_to_stage 的恐慌消息
- 为 Entity 使用默认 serde 实现
- scenes:简化 iter_instance_entities 的返回值类型
- 在示例中始终使用
PI
指定角度。 - 移除
Transform::apply_non_uniform_scale
- 将
Transform::mul_vec3
重命名为transform_point
并改进文档 - 使
TypeRegistry
上的register
成为幂等的 - 如果没有要求,则不要在窗口创建时设置光标抓取。
- 使
Window
和ExtractedWindow
中的raw_window_handle
字段成为Option
。 - 支持所有窗口模式的显示器选择。
Gamepad
类型为Copy
;在Gamepads
API 中不要求/返回对它的引用- 将 tracing-chrome 更新到 0.6.0
- 更新到 ron 0.8
- 将 clap 要求从 3.2 更新到 4.0
- 更新 glam 0.22、hexasphere 8.0、encase 0.4
- 将
wgpu
更新到 0.14.0,naga
更新到0.10.0
,winit
更新到 0.27.4,raw-window-handle
更新到 0.5.0,ndk
更新到 0.7 - 更新到 notify 5.0 稳定版
- 将 rodio 要求从 0.15 更新到 0.16
- 移除 copyless
- 将
Task
标记为#[must_use]
- 将 num_cpus 替换为 std::thread::available_parallelism
- 清理 NodeBundle,以及一些 UI 模块的轻微重组
- 使
NodeBundle
的默认背景颜色为透明色 - 将
UiColor
重命名为BackgroundColor
- 将诊断从秒更改为毫秒
- 移除查询访问中不必要的分支/恐慌
debug_checked_unwrap
应该跟踪其调用者- 加快
Query::get_many
的速度,并添加基准测试 - 将系统链接重命名为系统管道
- [修复 #6059]
Entity
的“ID”应该命名为“index” Query
过滤器类型必须是ReadOnlyWorldQuery
- 移除歧义集
- 放松更改检测类型周围的
Sized
约束 - 从 QueryCombinationIter 中移除 ExactSizeIterator
- 从 Command 中移除 Sync 约束
- 使大多数
Entity
方法成为const
- 移除
insert_resource_with_id
- 避免使
Fetch
s 成为Clone
- 从
Local
中移除Sync
约束 - 将
many_for_each_mut
替换为iter_many_mut
。 - bevy_ecs:在没有 AtomicI64 的平台上使用 32 位实体 ID 光标
- 针对“hdr-ness”专门化 UI 管道
- 允许将
glam
向量类型作为顶点属性传递 - 添加多重绘制间接绘制调用
- 在计算阴影贴图体积时,考虑 DirectionalLight 的 GlobalTransform(不仅仅是方向)。
- 在使用 linear()/nearest() 创建 ImageDescriptor 时,尊重 mipmap_filter。
- 如果表面尚未可用,则使用 bevy 默认纹理格式
- 更早地记录管道缓存错误
- 将 TextureAtlas::from_grid_with_padding 合并到 TextureAtlas::from_grid 中,通过可选参数。
- 在呈现模式更改时重新配置表面
- 使用 PipelineKey 的 3 位来存储 MSAA 采样计数
- 限制 FontAtlasSets
- 将
sprite::Rect
移到bevy_math
中 - 在 bevy_sprite 中使顶点颜色无需纹理也能工作
- 在后期处理中为纹理格式使用 bevy_default()
- 不要渲染完全透明的 UI 节点
- 使 TextLayoutInfo 成为一个组件
- 使
Handle::<T>
字段 id 私有,并用 getter 替换 - 移除
AssetServer::watch_for_changes()
- 将 Handle::as_weak() 重命名为 cast_weak()
- 移除
Decodable::Decoder
中的Sync
要求
修复 #
- 优化在高实体数量下渲染速度变慢的问题
- bevy_reflect:修复
DynamicScene
在序列化期间不尊重组件注册的问题 - 修复场景示例中 Vec3 和 Quat 的类型,以从日志中移除 WARN。
- 修复动画结束时的索引越界问题
- bevy_reflect:移除不必要的
Clone
约束 - bevy_reflect:修复
Option<T>
的apply
方法 - 修复
WindowDescriptor::transparent
的过时且格式错误的文档 - 禁用 ios 的窗口预创建
- 移除 wasm 上
WinitWindows
的不必要的不安全的Send
和Sync
实现。 - 修复 scale_factor 不为 1.0 时窗口居中问题
- 修复退出/关闭窗口系统的顺序
- bevy_input:修复处理触摸事件
- 修复:显式指定所需的 async-task 版本
- 修复
clippy::iter_with_drain
- 使用
cbrt()
而不是powf(1./3.)
- 修复
RemoveChildren
命令 - 修复不一致的子项移除行为
- 滴答本地执行器
- 修复关闭主窗口时的恐慌问题
- UI 缩放修复
- 修复 UI 中的剪切问题
- 修复 UI Y 轴反转后滚动示例
- 修复 text2d 的错误字形定位
- 在移除 UI 节点实体时清理 taffy 节点
- 修复不安全的
EntityMut::remove_children
。添加EntityMut::world_scope
- 修复生成空 Bundle
- 修复 query.to_readonly().get_component_mut() 的健全性错误
- #5817:derive_bundle 宏不卫生
- 如果存在,则在
insert_resource_by_id
中删除旧值 - 修复
From
实现的寿命约束NonSendMut
->Mut
- 修复没有法线的网格的
mesh.wgsl
错误 - 修复在 wasm 构建中使用全局一致性的恐慌问题
- 解决大多数剩余的执行顺序歧义
- 使用正确的参数调用
mesh2d_tangent_local_to_world
- 修复 Camera 由于核心功能中缺少注册而无法序列化的问题。
- 修复聚光灯方向的 NaN 错误
- 即使在非光照情况下也使用 alpha 遮罩
- 忽略 Linux AMD & Intel 上的
Timeout
错误 - 调整视口原点的聚类索引
- 如果视口更改,则更新相机投影
- 确保在批处理之前对 2D 相位项进行排序
- bevy_pbr:修复不正确且不必要的法线映射代码
- 在
update_frusta
和camera_system
之间添加显式排序 - bevy_pbr:修复切线和法线的规范化
- 修复着色器语法
- 在
Add<Color>
和AddAssign<Color>
中正确使用 as_hsla_f32,修复 #5543 - 同步 bevy_sprite 和 bevy_ui 着色器 View 结构体
- 通过添加 ViewUniform 中存在的缺失字段,修复 View
- 释放可见实体向量持有的内存
- 正确解析包含 '#' 的标签