Bevy 0.13
发布日期:2024 年 2 月 17 日,作者:Bevy 贡献者
感谢 **198** 位贡献者、**672** 次拉取请求、社区审阅者以及我们**慷慨的赞助商**,我们很高兴在 crates.io 上发布 **Bevy 0.13**!
对于那些不了解 Bevy 的人来说,Bevy 是一个用 Rust 编写的令人耳目一新、简单易用的数据驱动游戏引擎。您可以查看我们的快速入门指南,立即试用。它是永远免费的开源软件!您可以在 GitHub 上获取完整的源代码。查看 Bevy 资产,了解由社区开发的插件、游戏和学习资源的集合。要亲身体验引擎的功能,请查看最新 Bevy Jam 中的参赛作品,包括获胜者That's a LOT of beeeeees。
要将现有的 Bevy 应用程序或插件更新到 **Bevy 0.13**,请查看我们的0.12 到 0.13 迁移指南。
自从我们几个月前的上一个版本以来,我们添加了许多新功能、错误修复和生活质量调整,但以下是一些亮点
- 光照贴图: 一种快速且流行的烘焙全局照明技术,适用于静态几何体(在 The Lightmapper 等程序中外部烘焙)。
- 辐射体积 / 体素全局照明: 一种烘焙的全局照明形式,它在长方体内的体素中心对光进行采样(在 Blender 等程序中外部烘焙)。
- 近似间接镜面遮挡: 通过减少镜面遮挡导致的镜面光泄漏,提高了照明的真实感。
- 反射探头: 一种烘焙的轴对齐环境贴图形式,允许为静态几何体提供逼真的反射(在 Blender 等程序中外部烘焙)
- 基本形状: 基本形状是游戏引擎和视频游戏的核心构建块:我们添加了一个经过精心设计、随时可用的集合!
- 系统步进: 彻底暂停并逐帧或逐系统地推进游戏,以便以交互方式调试游戏逻辑,同时渲染继续更新。
- 动态查询: 从系统内部细化查询具有极高的表达能力,并且是运行时定义类型以及第三方模组和脚本集成最后的关键部分。
- 自动推断的命令刷新点: 不再需要考虑在哪里放置
apply_deferred
以及为什么命令没有被应用?我们也一样!现在,Bevy 的调度程序使用普通的.before
和.after
约束,并检查系统参数以自动推断(并消除重复)同步点。 - 切片、贴图和九宫格 2D 图像: 九宫格布局是用于平滑缩放风格化图集和 UI 的一种流行工具。现在在 Bevy 中可以使用了!
- 相机驱动的 UI: UI 实体树现在可以被有选择地添加到任何相机,而不是全局应用到所有相机,从而实现诸如分屏 UI 等功能!
- 相机曝光: 通过 EV100、f 值、快门速度和 ISO 感光度,对相机曝光进行逼真/“真实世界”控制。灯光也已调整,使其单位更逼真。
- 动画插值模式: Bevy 现在支持导出 glTF 动画中的非线性插值模式。
初始烘焙照明 #
实时计算照明非常昂贵;但对于场景中永远不会移动的元素(如房间或地形),我们可以通过提前使用全局照明计算照明和阴影来获得更漂亮的照明和阴影,然后将结果存储在永远不会改变的“烘焙”形式中。全局照明是一种更逼真(且更昂贵)的照明方法,通常使用光线追踪。与 Bevy 的默认渲染不同,它考虑了光线从其他物体上反弹的情况,通过包含间接光来产生更逼真的效果。
光照贴图 #
光照贴图是存储预计算全局照明结果的纹理。几十年来,它们一直是实时图形的主流。Bevy 0.13 添加了对在其他程序(如 The Lightmapper)中计算的光照贴图进行渲染的初步支持。最终,我们希望在 Bevy 中直接添加对烘焙光照贴图的支持,但这一步打开了光照贴图工作流程!
正如lightmaps 示例所示,只需加载烘焙的光照贴图图像,然后在相应的网格上插入一个Lightmap
组件。
辐射体积 / 体素全局照明 #
辐射体积(或体素全局照明)是一种通过首先将场景划分为立方体(体素),然后对每个体素的中心存在的光量进行采样,从而近似间接光的方法。然后,随着物体在该空间中移动,该光会被添加到这些物体上,从而适当地改变这些物体上的环境光级别。
我们选择使用基于 Half Life 2 的环境立方体算法。这使我们能够匹配 Blender 的 Eevee 渲染器,为用户提供一个简单且免费的路径来为他们自己的场景创建美观的辐射体积。
注意,由于辐射体积,这个球体在移动时会微妙地拾取环境的颜色
目前,您需要使用 Blender 等外部工具烘焙辐射体积,但在将来,我们希望在 Bevy 中直接支持烘焙辐射体积!
最小反射探头 #
环境贴图是 2D 纹理,用于在 3D 场景中模拟照明、反射和天空盒。反射探头将环境贴图推广,允许在同一个场景中使用多个环境贴图,每个环境贴图都有自己的轴对齐边界框。这是基于物理的渲染器的标准功能,灵感来自 Blender 的 Eevee 渲染器中的相应功能。
在反射探头 PR 中,我们添加了对它们的初步支持,为 Bevy 游戏中的漂亮、高性能反射奠定了基础。与上面讨论的烘焙全局照明工作一样,这些目前必须在外部预计算,然后导入到 Bevy 中。正如 PR 中所讨论的,存在一些注意事项:WebGL2 支持实际上不存在,由于没有混合,将观察到尖锐且突然的过渡,并且同一个世界中给定类型(漫射或镜面)的所有立方体贴图必须具有相同的尺寸、格式和mipmap 数量。
近似间接镜面遮挡 #
Bevy 当前的 PBR 渲染器过度亮化了图像,尤其是在掠射角,菲涅尔效应往往使表面像镜子一样。这种过度亮化是由于表面必须反射某些东西,但如果没有路径追踪或屏幕空间反射,渲染器必须猜测反射什么。它能做出的最佳猜测是采样环境立方体贴图,即使光线可能在到达环境光之前撞击了其他东西。这种忽略光遮挡的伪像被称为镜面光泄漏。
考虑一个汽车轮胎;虽然橡胶可能很光滑,但您不会期望它在轮毂井内有明亮的镜面高光,因为汽车本身正在阻挡(遮挡)会导致这些反射的光线。完全检查遮挡在计算上可能很昂贵。
Bevy 0.13 添加了对近似间接镜面遮挡的支持,它利用我们现有的 屏幕空间环境光遮挡 来近似镜面遮挡,可以在实时运行的同时产生相当高质量的结果。
拖动此图像进行比较


将来,这可以通过屏幕空间反射 (SSR) 进一步改进。但是,传统观点认为,您应该将镜面遮挡与 SSR 结合使用,因为 SSR 仍然存在光线泄漏伪像。
基本形状 #
几何形状在整个游戏开发中被广泛使用,从基本网格形状和调试工具到物理碰撞体和射线投射。尽管在多个领域中被如此广泛地使用,但 Bevy 实际上并没有任何通用的形状表示。
这在Bevy 0.13中正在发生改变,引入了第一方基本形状!它们是轻量级的几何基元,旨在实现最大程度的互操作性和可重用性,允许 Bevy 和第三方插件使用相同的基本形状集,并增强生态系统内的凝聚力。有关更多详细信息,请参阅原始的 RFC。
内置的 基元集合 已经相当庞大。
2D | 3D |
---|---|
矩形 | 长方体 |
圆形 | 球体 |
椭圆形 | |
二维三角形 | |
二维平面 | 三维平面 |
二维线 | 三维线 |
二维线段 | 三维线段 |
Polyline2d 、BoxedPolyline2d | Polyline3d 、BoxedPolyline3d |
Polygon 、BoxedPolygon | |
正多边形 | |
二维胶囊体 | 三维胶囊体 |
圆柱体 | |
圆锥体 | |
圆锥台 | |
环面 |
更多基元 将在将来的版本中添加。
基本形状的一些用例包括网格化、工具、包围体、碰撞体和射线投射功能。其中一些已经在 0.13 中实现!
渲染 #
基本形状可以使用网格和工具进行渲染。在本节中,我们将仔细研究新的 API。
下面,您可以看到使用网格和工具渲染的长方体和环面。您可以在新的 渲染基元 示例中查看所有可以渲染的基元。
网格化 #
以前的 Bevy 版本包含诸如 Quad
、Box
和 UVSphere
之类的类型,用于从基本形状创建网格。这些类型已弃用,取而代之的是使用新的几何基元的构建器式 API。
支持网格化的基元实现了 Meshable
特性。对于某些形状,mesh
方法直接返回一个 Mesh
let before = Mesh::from(Quad::new(Vec2::new(2.0, 1.0)));
let after = Rectangle::new(2.0, 1.0).mesh(); // Mesh::from also works
但是,对于大多数基元,它返回一个用于可选配置的构建器。
// Create a circle mesh with a specified vertex count
let before = Mesh::from(Circle {
radius: 1.0,
vertices: 64,
});
let after = Circle::new(1.0).mesh().resolution(64).build();
以下是一些使用新的基元进行网格化的更多示例。
// Icosphere
let before = meshes.add(
Mesh::try_from(Icosphere {
radius: 2.0,
subdivisions: 8,
})
.unwrap()
);
let after = meshes.add(Sphere::new(2.0).mesh().ico(8).unwrap());
// Cuboid
// (notice how Assets::add now also handles mesh conversion automatically)
let before = meshes.add(Mesh::from(shape::Box::new(2.0, 1.0, 1.0)));
let after = meshes.add(Cuboid::new(2.0, 1.0, 1.0));
// Plane
let before = meshes.add(Mesh::from(Plane::from_size(5.0)));
let after = meshes.add(Plane3d::default().mesh().size(5.0, 5.0));
随着基元的添加,网格化也支持更多形状,例如 Ellipse
、Triangle2d
和 Capsule2d
。但是,请注意,网格化尚未在所有基元中实现,例如 Polygon
和 Cone
。
您可以在下面的 2d_shapes
和 3d_shapes
示例中看到一些网格。
网格形状尺寸的一些默认值也已更改,以实现更高的一致性。
工具 #
基元也可以使用 Gizmos
进行渲染。有两个新的泛型方法
gizmos.primitive_2d(primitive, position, angle, color)
gizmos.primitive_3d(primitive, position, rotation, color)
某些基元可能具有类似于现有 Gizmos
绘制方法的附加配置选项。例如,使用 Sphere
调用 primitive_3d
会返回一个 SphereBuilder
,它提供了一个 segments
方法来控制球体的细节级别。
let sphere = Sphere { radius };
gizmos
.primitive_3d(sphere, center, rotation, color)
.segments(segments);
包围体 #
在游戏开发中,空间检查具有许多宝贵的用例,例如获取在摄像机视锥体中或靠近玩家的所有实体,或查找可能发生碰撞的物理对象对。为了加快这些检查,包围体用于逼近更复杂的形状。
Bevy 0.13 添加了一些新的公开可用的包围体:Aabb2d
、Aabb3d
、BoundingCircle
和 BoundingSphere
。这些可以手动创建,也可以从基元形状生成。
每个包围体都实现了 BoundingVolume
特性,提供了一些通用功能和帮助程序。可以使用 IntersectsVolume
特性来测试与这些包围体的交点。此特性在包围体本身中实现,因此您可以测试它们之间的交点。这在所有现有的包围体类型之间都支持,但仅支持同一维度的那些类型。
以下是如何构造包围体以及如何执行交点测试的示例
// We create an axis-aligned bounding box that is centered at position
let position = Vec2::new(100., 50.);
let half_size = Vec2::splat(20.);
let aabb = Aabb2d::new(position, half_size);
// We create a bounding circle that is centered at position
let position = Vec2::new(80., 70.);
let radius = 30.;
let bounding_circle = BoundingCircle::new(position, radius);
// We check if the volumes are intersecting
let intersects = bounding_circle.intersects(&aabb);
还有两个用于生成包围体的特性:Bounded2d
和 Bounded3d
。这些在新的基元形状中实现,因此您可以轻松地为它们计算包围体。
// We create a primitive, a hexagon in this case
let hexagon = RegularPolygon::new(50., 6);
let translation = Vec2::new(50., 200.);
let rotation = PI / 2.; // Rotation in radians
// Now we can get an Aabb2d or BoundingCircle from this primitive.
// These methods are part of the Bounded2d trait.
let aabb = hexagon.aabb_2d(translation, rotation);
let circle = hexagon.bounding_circle(translation, rotation);
射线投射和体积投射 #
包围体还支持基本的射线投射和体积投射。射线投射测试一个包围体是否与给定射线相交,该射线从一个原点沿一个方向投射,直到最大距离。体积投射的工作原理类似,但就像沿着射线移动一个体积一样。
此功能通过新的 RayCast2d
、RayCast3d
、AabbCast2d
、AabbCast3d
、BoundingCircleCast
和 BoundingSphereCast
类型提供。它们可用于检查与包围体的交点,以及计算从投射原点到交点的距离。
下面,您可以看到射线投射、体积投射和交点测试在实际中的应用
为了更容易地理解不同维度中的射线投射,旧的 Ray
类型也已拆分为 Ray2d
和 Ray3d
。新的 Direction2d
和 Direction3d
类型用于确保射线方向保持归一化,从而提供类型级保证,即向量始终为单位长度。这些类型已经在其他一些 API 中使用,例如某些基元和工具方法。
系统步进 #
Bevy 0.13 添加了对系统步进的支持,这为系统添加了类似于调试器的执行控制。
可以使用 Stepping
资源来控制计划中每帧执行哪些系统,并提供步进、断点和继续功能以启用实时调试。
let mut stepping = Stepping::new();
您将要逐步执行的计划添加到 Stepping
资源中。这些计划中的系统可以被认为是“步进帧”。“步进帧”中的系统除非发生相关的步进或继续操作,否则不会运行。未添加的计划将在每次更新时运行,即使在步进过程中也是如此。这使核心功能(如渲染)能够继续工作。
stepping.add_schedule(Update);
stepping.add_schedule(FixedUpdate);
即使插入了资源,步进默认情况下也是禁用的。要在应用程序中启用它,功能标志、开发控制台和模糊的热键都可以很好地工作。
#[cfg(feature = "my_stepping_flag")]
stepping.enable();
最后,将 Stepping
资源添加到 ECS World
中。
app.insert_resource(stepping);
系统步进和继续帧 #
“步进帧”操作会运行步进光标处的系统,并在下一渲染帧中向前移动光标。这对于查看系统所做的单个更改以及在执行系统之前查看世界的状态非常有用。
stepping.step_frame()
“继续帧”操作将在下一帧中从步进光标开始执行系统,直到步进帧结束。如果遇到带有断点的系统,它可能会在步进帧结束之前停止。这对于快速遍历整个帧、到达下一帧的开头或与断点结合使用非常有用。
stepping.continue_frame()
这段视频演示了这些操作在 breakout 示例中的应用,该示例使用自定义的 egui
接口。当我们点击 step
按钮时,可以看到步进光标在系统列表中移动。当点击 continue
按钮时,可以看到游戏每次点击都前进一个步进帧。
断点 #
当计划发展到一定程度时,可能需要花费很长时间才能遍历计划中的每个系统,只是为了查看几个系统的效果。在这种情况下,步进提供系统断点。
这段视频说明了 check_for_collisions()
上的断点在“步进”和“继续”操作中的行为。
在步进期间禁用系统 #
在调试期间,禁用系统以缩小问题的来源可能会有所帮助。可以使用 Stepping::never_run()
和 Stepping::never_run_node()
在启用步进时禁用系统。
将系统从步进中排除 #
可能需要确保某些系统在启用步进时仍然运行。虽然最佳实践是将它们放在一个未添加到 Stepping
资源的计划中,但可以将系统配置为在启用步进时始终运行。这主要对于事件和输入处理系统有用。
可以通过调用 `Stepping::always_run()` 或 `Stepping::always_run_node()` 将系统配置为始终运行。当系统配置为始终运行时,即使启用了步进,它也会在每个渲染帧运行。
局限性 #
步进的初始实现有一些局限性。
- **读取事件的系统可能无法正常步进:**因为在启用步进时帧仍然正常前进,所以在步进系统读取事件之前,事件可能会被清除。这里最好的方法是将基于事件的系统配置为始终运行,或者将它们放到未添加到 `Stepping` 的调度中。在这种情况下,“继续”带断点可能也有效。
- **条件系统在步进时可能无法按预期运行:**与基于事件的系统类似,如果运行条件仅在短时间内为真,则系统在步进时可能不会运行。
详细示例 #
- 基于文本的步进示例
- 非交互式 bevy UI 示例步进插件 用于 Breakout 示例
- 交互式 egui 步进插件 用于演示视频
相机曝光 #
在现实世界中,相机捕获的图像亮度由曝光量决定:相机传感器或胶片吸收的光量。这由相机的几种机制控制。
- **光圈:**以 F 档表示,光圈打开和关闭以控制允许进入相机传感器或胶片的光量,通过物理方式阻挡来自特定角度的光线,类似于眼睛的瞳孔。
- **快门速度:**相机快门打开的时间,即相机传感器或胶片曝光的光线持续时间。
- **ISO 感光度:**相机传感器或胶片对光的敏感度。值越高表示对光的敏感度越高。
这些因素都会影响最终图像接收的光量。它们可以组合成最终的 EV 值(曝光值),例如半标准 EV100(ISO 100 的曝光值)。更高的 EV100 值意味着需要更多的光线才能获得相同的结果。例如,阳光明媚的场景可能需要约 15 的 EV100,而光线昏暗的室内场景可能需要约 7 的 EV100。
在 **Bevy 0.13** 中,您现在可以使用新的 Exposure
组件按相机配置 EV100。您可以使用 Exposure::ev100
字段直接设置它,或者使用新的 PhysicalCameraParameters
结构使用“现实世界”相机设置(例如 f 档、快门速度和 ISO 感光度)计算 EV100。
这很重要,因为 Bevy 的“基于物理”渲染器(PBR)有意扎根于现实。我们的目标是让人们能够在他们的灯光和材质中使用现实世界单位,并使它们的行为尽可能接近现实。
拖动此图像进行比较


请注意,Bevy 的早期版本为其某些灯光类型硬编码了静态 EV100。在 **Bevy 0.13** 中,它是可配置的,并且在所有灯光类型中保持一致。我们还将默认 EV100 提升到 9.7,这是一个 我们选择与 Blender 的默认曝光最匹配的数字。它恰好也是一个不错的“中间值”,位于室内照明和阴天室外照明之间。
您可能会注意到,点光源现在需要 *显著* 更高的强度值(以流明表示)。这种(有时)百万流明的值可能感觉过高。请放心,(1)在阴天室外环境中,实际上需要大量的灯光才能产生有意义的照明,(2)Blender 以这种规模导出灯光(而我们校准得尽可能接近它们)。
相机驱动 UI #
从历史上看,Bevy 的 UI 元素在主窗口的上下文中进行缩放和定位,与相机设置无关。这种方法使一些 UI 体验(例如分屏多人游戏)难以实现,而另一些(例如在多个窗口中使用 UI)则不可能实现。
**Bevy 0.13** 引入了 **相机驱动 UI**。每个相机现在都可以拥有自己的 UI 根节点,根据其视口、缩放因子和目标进行渲染,目标可以是辅助窗口,甚至可以是纹理。
此更改解锁了各种新的 UI 体验,包括分屏多人游戏、多个窗口中的 UI、在 3D 世界中显示非交互式 UI 等等。
如果世界上只有一个相机,您无需进行任何操作;您的 UI 将显示在该相机的视口中。
commands.spawn(Camera3dBundle {
// Camera can have custom viewport, target, etc.
});
commands.spawn(NodeBundle {
// UI will be rendered to the singular camera's viewport
});
当需要更多控制,或存在多个相机时,我们引入了 TargetCamera
组件。此组件可以添加到根 UI 节点以指定应将其渲染到哪个相机。
// For split-screen multiplayer, we set up 2 cameras and 2 UI roots
let left_camera = commands.spawn(Camera3dBundle {
// Viewport is set to left half of the screen
}).id();
commands
.spawn((
TargetCamera(left_camera),
NodeBundle {
//...
}
));
let right_camera = commands.spawn(Camera3dBundle {
// Viewport is set to right half of the screen
}).id();
commands
.spawn((
TargetCamera(right_camera),
NodeBundle {
//...
})
);
通过此更改,我们还删除了 UiCameraConfig
组件。如果您正在使用它来隐藏 UI 节点,则可以通过在根节点上配置 Visibility
组件来实现相同的效果。
commands.spawn(Camera3dBundle::default());
commands.spawn(NodeBundle {
visibility: Visibility::Hidden, // UI will be hidden
// ...
});
纹理切片和贴图 #
3D 渲染得到了很多关注,但 2D 功能也很重要!我们很高兴在 **Bevy 0.13** 中为 bevy_sprite
和 bevy_ui
添加基于 CPU 的 *切片和贴图*!
此行为由一个新的可选组件控制:ImageScaleMode
。
9 切片 #
在具有精灵或 UI 捆绑的实体上添加 ImageScaleMode::Sliced
会启用 9 切片,在调整大小期间保持图像比例,避免纹理拉伸。
这对于 UI 非常有用,允许您的漂亮纹理在元素大小改变时保持正确的显示。
commands.spawn((
SpriteSheetBundle::default(),
ImageScaleMode::Sliced(TextureSlicer {
// The image borders are 20 pixels in every direction
border: BorderRect::square(20.0),
// we don't stretch the corners more than their actual size (20px)
max_corner_scale: 1.0,
..default()
}),
));
贴图 #
在您的 2D 精灵实体上添加 ImageMode::Tiled { .. }
会启用 *纹理贴图*:重复图像,直到它们的整个区域都被填充。这通常用于背景和表面。
commands.spawn((
SpriteSheetBundle::default(),
ImageScaleMode::Tiled {
// The image will repeat horizontally
tile_x: true,
// The image will repeat vertically
tile_y: true,
// The texture will repeat if the drawing rect is larger than the image size
stretch_value: 1.0,
},
));
动态查询 #
在 Bevy ECS 中,查询使用类型驱动的 DSL。查询的完整类型(要访问的组件、要使用的过滤器)必须在编译时指定。
有时我们无法在编译时知道查询要访问哪些数据。有些场景根本无法使用静态查询来完成。
- 在 Lua 或 JavaScript 等脚本语言中定义查询。
- 从脚本语言中定义新的组件并查询它们。
- 向实体检查器(如
bevy-inspector-egui
)添加运行时过滤器。 - 向 Quake 风格的控制台 添加功能,以便从提示符在运行时修改或查询组件。
- 创建具有远程功能的 编辑器。
动态查询使所有这些成为可能。而这些只是我们迄今为止听到的计划!
定义 Query
的标准方法是将它们用作系统参数。
fn take_damage(mut player_health: Query<(Entity, &mut Health), With<Player>>) {
// ...
}
**这不会改变。**对于大多数(如果不是全部)游戏玩法用例,您将继续愉快地使用令人愉快的简单 Query
API。
但是,请考虑这种情况:作为游戏或模组开发者,我想通过文本提示列出具有特定组件的实体。类似 Quake 控制台的工作方式。这将是什么样子呢?
#[derive(Resource)]
struct UserQuery(String);
// user_query is entered as a text prompt by the user when the game is running.
// In a system, it's quickly apparent that we can't use `Query`.
fn list_entities_system(user_query: Res<UserQuery>, query: Query<FIXME, With<FIXME>>) {}
// Even when using the more advanced `World` API, we are stuck.
fn list_entities(user_query: String, world: &mut World) {
// FIXME: what type goes here?
let query = world.query::<FIXME>();
}
根据 `user_query` 的值选择类型是不可能的!QueryBuilder
解决了这个问题。
fn list_entities(
user_query: String,
type_registry: &TypeRegistry,
world: &mut World,
) -> Option<()> {
let name = user_query.split(' ').next()?;
let type_id = type_registry.get_with_short_type_path(name)?.type_id();
let component_id = world.components().get_id(type_id)?;
let query = QueryBuilder::<FilteredEntityRef>::new(&mut world)
.ref_id(component_id)
.build();
for entity_ref in query.iter(world) {
let ptr = entity_ref.get_by_id(component_id);
// Convert `ptr` into a `&dyn Reflect` and use it.
}
Some(())
}
它仍然是一个容易出错、复杂且不安全的 API,但它使以前不可能的事情成为可能。我们希望第三方 crate 为 `QueryBuilder` API 提供便捷的包装器,其中一些包装器无疑会进入上游。
查询转化 #
您是否曾经想将查询传递给函数,但不是拥有 Query<&Transform>
,而是拥有 Query<(&Transform, &Velocity), With<Enemy>>
?在 **Bevy 0.13** 中,您可以做到这一点,这得益于新的 QueryLens
和 Query::transmute_lens()
方法。
查询转化允许您将查询更改为不同的查询类型,只要访问的组件是原始查询的子集。如果您尝试访问不在原始查询中的数据,此方法将出现恐慌。
fn reusable_function(lens: &mut QueryLens<&Transform>) {
let query = lens.query();
// do something with the query...
}
// We can use the function in a system that takes the exact query.
fn system_1(mut query: Query<&Transform>) {
reusable_function(&mut query.as_query_lens());
}
// We can also use it with a query that does not match exactly
// by transmuting it.
fn system_2(mut query: Query<(&mut Transform, &Velocity), With<Enemy>>) {
let mut lens = query.transmute_lens::<&Transform>();
reusable_function(&mut lens);
}
请注意,QueryLens
仍然会遍历与它派生自的原始 Query
相同的实体。从 Query<(&Transform, &Velocity)>
中获取的 QueryLens<&Transform>
仅包含具有 `Transform` 和 `Velocity` 组件的实体的 `Transform`。
除了删除参数,您还可以以有限的方式将它们更改为不同的智能指针类型。其中最有用的是将 &mut
更改为 &
。有关更多详细信息,请参阅 文档。
需要注意的是,转化并非免费的。它通过创建一个新状态并在原始查询中复制缓存数据来工作。这不是一项昂贵的操作,但您应该避免在热循环中进行。
WorldQuery
特性拆分 #
Query
具有两个类型参数:一个用于要获取的数据,第二个可选参数用于过滤器。
在 Bevy 的早期版本中,这两个参数都只需要 WorldQuery
:没有任何东西阻止您在数据位置(反之亦然)中使用作为过滤器的类型。
除了使 Query
项目的类型签名更复杂(见下例)之外,这通常效果很好,因为大多数过滤器在任一位置的行为相同。
不幸的是,情况并非如此 Changed
和 Added
它们在数据位置有不同的(未记录的)行为,这会导致用户代码中的错误。
为了让我们能够在编译时阻止这种类型的错误,WorldQuery
特性已被两个特性替换:QueryData
和 QueryFilter
。现在,Query
的数据参数必须是 QueryData
,过滤器参数必须是 QueryFilter
。
大多数用户代码应该不受影响或易于迁移。
// Probably a subtle bug: `With` filter in the data position - will not compile in 0.13
fn my_system(query: Query<(Entity, With<ComponentA>)>)
{
// The type signature of the query items is `(Entity, ())`, which is usable but unwieldy
for (entity, _) in query.iter(){
}
}
// Idiomatic, compiles in both 0.12 and 0.13
fn my_system(query: Query<Entity, With<ComponentA>>)
{
for entity in query.iter(){
}
}
自动插入 `apply_deferred` 系统 #
在编写游戏玩法代码时,您通常会遇到一个系统希望立即看到另一个系统中排队的命令的效果。在**Bevy 0.13**之前,您需要在两个系统之间手动插入一个`apply_deferred`系统,这是一个特殊系统,会在遇到这些命令时应用它们。现在,Bevy 会检测带有命令的系统相对于其他系统的排序情况,并为您插入`apply_deferred`系统。
// Before 0.13
app.add_systems(
Update,
(
system_with_commands,
apply_deferred,
another_system,
).chain()
);
// After 0.13
app.add_systems(
Update,
(
system_with_commands,
another_system,
).chain()
);
这解决了常见的初学者错误:如果两个系统是按顺序排列的,那么第二个系统不应该总是看到第一个系统的结果吗?
自动插入的`apply_deferred`系统通过自动合并它们(如果可能的话)来进行优化。在大多数情况下,建议删除所有手动插入的`apply_deferred`系统,因为允许 Bevy 按需插入和合并这些系统通常会更快,并且涉及更少的样板代码。
// This will only add one apply_deferred system.
app.add_systems(
Update,
(
(system_1_with_commands, system_2).chain(),
(system_3_with_commands, system_4).chain(),
)
);
如果这种新行为不适合您,请查阅迁移指南。有几个新 API 允许您选择退出。
更灵活的一次性系统 #
在**Bevy 0.12**中,我们引入了一次性系统,这是一种方便的方法,可以在需要时调用系统,而无需将它们添加到调度程序中。最初的实现对哪些系统可以和不能用作一次性系统有一些限制。在**Bevy 0.13**中,这些限制已得到解决。
一次性系统现在支持输入和输出。
fn increment_sys(In(increment_by): In<i32>, mut counter: ResMut<Counter>) -> i32 {
counter.0 += increment_by;
counter.0
}
let mut world = World::new();
let id = world.register_system(increment_sys);
world.insert_resource(Counter(1));
let count_one = world.run_system_with_input(id, 5).unwrap(); // increment counter by 5 and return 6
let count_two = world.run_system_with_input(id, 2).unwrap(); // increment counter by 2 and return 8
运行系统现在将系统输出作为`Ok(output)`返回。请注意,通过命令调用一次性系统时,由于其延迟性质,无法返回输出。
独占系统现在可以注册为一次性系统。
world.register_system(|world: &mut World| { /* do anything */ });
装箱系统现在可以使用`register_boxed_system`注册。
这些改进极大地完善了一次性系统:它们现在应该与其他任何 Bevy 系统一样工作。
wgpu 0.19 升级和渲染性能改进 #
在**Bevy 0.13**中,我们从`wgpu` 0.17 升级到`wgpu` 0.19,其中包括期待已久的`wgpu` arcanization,它允许我们异步编译着色器以避免着色器编译卡顿,以及多线程绘制调用创建,以在 CPU 受限场景中获得更好的性能。
由于 wgpu 0.19 中的更改,我们在 Bevy 中添加了一个新的`webgpu`特性,现在在针对 WebGPU 进行 WebAssembly 构建时需要该特性。在针对 WebGPU 时,不再需要禁用`webgl2`特性,但新的`webgpu`特性在启用时当前会覆盖`webgl2`特性。库作者,请不要默认启用`webgpu`特性。将来,我们计划允许您在同一个 WebAssembly 二进制文件中同时针对 WebGL2 和 WebGPU,但我们还没有完全实现这一目标。
我们交换了材质和网格绑定组,因此网格数据现在位于绑定组 1 中,而材质数据位于绑定组 2 中。这与更改不透明通道的排序函数以按管道和网格排序相结合,极大地改善了我们的绘制调用批处理。以前,我们按距离相机排序。这些批处理改进意味着我们执行的绘制调用更少,从而提高了 CPU 性能,尤其是在较大的场景中。我们还删除了着色器中的`get_instance_index`函数,因为它仅用于解决 wgpu 0.19 中已修复的上游错误。有关其他着色器或渲染更改,请参阅迁移指南和wgpu 的更改日志。
对 Bevy 和`wgpu`的许多细微更改加起来在真实的 3D 场景中产生了适度但可衡量的性能差异!我们在同一台机器上对四个复杂场景(Bistro、Sponza、San Miguel 和 Hidden Alley)运行了一些快速测试,分别在**Bevy 0.12**和**Bevy 0.13**上。
如您所见,这些场景比大多数视频游戏环境详细得多,但该屏幕截图是在 Bevy 中以 1440p 分辨率以超过 60 FPS 的速度渲染的!在 Bevy 0.12 和 Bevy 0.13 之间,我们看到测试场景的帧时间减少了约 5-10%。干得好!
从 RAM 中卸载渲染资产 #
网格和用于定义其材质的纹理占用大量的内存:在许多游戏中,内存使用是游戏分辨率和多边形数量的最大限制!此外,将这些数据从系统 RAM(CPU 使用)传输到 VRAM(GPU 使用)可能会成为真正的性能瓶颈。
**Bevy 0.13** 添加了从系统 RAM 中卸载这些数据的功能,一旦它们已成功传输到 VRAM。要为您的资产配置此行为,请设置RenderAssetUsages
字段,以指定是在主(CPU)世界、渲染(GPU)世界还是两者中保留数据。
这种行为目前在大多数资产类型中默认处于关闭状态,因为它存在一些注意事项(因为资产变得对 CPU 上的逻辑不可用),但我们强烈建议您尽可能为您的资产启用它,以获得显著的内存使用量提升(我们可能会在将来将其默认启用)。
纹理图集和字体图集现在只提取实际使用的部分数据到 VRAM,而不是浪费工作量,每帧将所有可能的图像或字符发送到 VRAM。真棒!
通过更智能的排序获得更好的批处理 #
用于加速渲染的核心技术之一是同时绘制许多相似的对象。在这种情况下,Bevy 已经使用了一种称为“批处理”的技术,它允许我们合并多个相似的操作,减少正在进行的昂贵的绘制调用(对 GPU 的指令)数量。
但是,我们定义这些批次的策略远非最佳。以前,我们按距离相机排序,然后检查该排序列表中是否有多个相同的网格彼此相邻。在真实的场景中,这不太可能找到许多合并候选者!
在**Bevy 0.13**中,我们首先按管道(实际上是使用的材质类型)排序,然后按网格标识排序。这种策略会导致更好的批处理,在测试的真实场景中将整体 FPS 提高了两位数的百分比!
动画插值方法 #
通常,动画由它们的关键帧定义:在时间轴上某个时刻的对象位置(和其他状态)的快照。但是,这些关键帧之间发生了什么?游戏引擎需要在它们之间进行插值,平滑地从一个状态过渡到另一个状态。
最简单的插值方法是线性:动画对象每单位时间都向下一个关键帧移动相同距离。但这并不总是我们想要的效果!定格动画风格和更平滑的动画都有其用武之地。
Bevy 现在支持动画中的分步和三次样条插值。大多数情况下,这将直接从 glTF 文件中正确解析,但在手动设置VariableCurve
时,有一个新的Interpolation
字段需要设置。
Animatable
特性 #
当您想到“动画”时:您可能想到的是在空间中移动对象。将它们来回平移,旋转它们,甚至可能挤压和拉伸它们。但在现代游戏开发中,动画是一组功能强大的共享工具和概念,用于“随时间改变事物”。变换只是开始:颜色、粒子效果、不透明度,甚至诸如可见性之类的布尔值都可以进行动画处理!
在**Bevy 0.13**中,我们迈出了这一愿景的第一步,使用了Animatable
特性。
/// An animatable value type.
pub trait Animatable: Reflect + Sized + Send + Sync + 'static {
/// Interpolates between `a` and `b` with a interpolation factor of `time`.
///
/// The `time` parameter here may not be clamped to the range `[0.0, 1.0]`.
fn interpolate(a: &Self, b: &Self, time: f32) -> Self;
/// Blends one or more values together.
///
/// Implementors should return a default value when no inputs are provided here.
fn blend(inputs: impl Iterator<Item = BlendInput<Self>>) -> Self;
/// Post-processes the value using resources in the [`World`].
/// Most animatable types do not need to implement this.
fn post_process(&mut self, _world: &World) {}
}
这是朝着动画混合和资产驱动动画图迈出的第一步,这对于在 Bevy 中发布大型 3D 游戏至关重要。但就目前而言,这只是一个构建块。我们已经为一些关键类型(Transform
、f32
和 `glam` 的 `Vec` 类型)实现了这一点,并发布了该特性。将其插入到您的游戏和板条箱中,并与其他贡献者合作,帮助`bevy_animation`变得与引擎的其余部分一样令人愉快和功能丰富。
无扩展名资产支持 #
在 Bevy 的早期版本中,为特定资产选择AssetLoader
的默认方法完全基于文件扩展名。最近添加了 .meta 文件,允许指定更细粒度的加载行为,但仍然需要文件扩展名。在**Bevy 0.13**中,现在可以使用资产类型推断AssetLoader
。
// Uses AudioSettingsAssetLoader
let audio = asset_server.load("data/audio.json");
// Uses GraphicsSettingsAssetLoader
let graphics = asset_server.load("data/graphics.json");
这是因为每个AssetLoader
都需要声明它加载的资产类型,而不仅仅是它支持的扩展名。由于load
方法在AssetServer
上已经对要返回的资产类型进行了泛化,因此该信息已经可用于AssetServer
。
// The above example with types shown
let audio: Handle<AudioSettings> = asset_server.load::<AudioSettings>("data/audio.json");
let graphics: Handle<GraphicsSettings> = asset_server.load::<GraphicsSettings>("data/graphics.json");
现在,我们也可以用它来选择AssetLoader
本身。
加载资产时,加载器将通过按顺序检查以下内容来选择:
- 资产`meta` 文件
- 要返回的`Handle<A>` 类型
- 文件扩展名
// This will be inferred from context to be a glTF asset, ignoring the file extension
let gltf_handle = asset_server.load("models/cube/cube.gltf");
// This still relies on file extension due to the label
let cube_handle = asset_server.load("models/cube/cube.gltf#Mesh0/Primitive0");
// ^^^^^^^^^^^^^^^^^
// | Asset path label
文件扩展名现在是可选的 #
由于可以使用资产类型推断加载器,因此要加载的文件或AssetLoader
都不需要文件扩展名。
pub trait AssetLoader: Send + Sync + 'static {
/* snip */
/// Returns a list of extensions supported by this [`AssetLoader`], without the preceding dot.
fn extensions(&self) -> &[&str] {
// A default implementation is now provided
&[]
}
}
以前,没有扩展名的资产加载器很难使用。现在,它们可以使用起来与其他任何加载器一样容易。同样,如果文件缺少其扩展名,Bevy 现在可以选择适当的加载器。
let license = asset_server.load::<Text>("LICENSE");
仍然建议使用适当的文件扩展名来进行良好的项目管理,但这现在只是一个建议,而不是硬性要求。
具有相同资产的多个资产加载器 #
现在,只要它们是不同的资产类型,单个路径就可以被多个资产句柄使用。
// Load the sound effect for playback
let bang = asset_server.load::<AudioSource>("sound/bang.ogg");
// Load the raw bytes of the same sound effect (e.g, to send over the network)
let bang_blob = asset_server.load::<Blob>("sound/bang.ogg");
// Returns the bang handle since it was already loaded
let bang_again = asset_server.load::<AudioSource>("sound/bang.ogg");
请注意,上面的示例使用了turbofish 语法以提高清晰度。在实践中,这不是必需的,因为加载的资产类型通常可以在调用点推断出来。
#[derive(Resource)]
struct SoundEffects {
bang: Handle<AudioSource>,
bang_blob: Handle<Blob>,
}
fn setup(mut effects: ResMut<SoundEffects>, asset_server: Res<AssetServer>) {
effects.bang = asset_server.load("sound/bang.ogg");
effects.bang_blob = asset_server.load("sound/bang.ogg");
}
已经更新了custom_asset
示例,以演示这些新功能。
纹理图集重做 #
纹理图集有效地将多个图像组合到一个名为图集的更大的纹理中。
**Bevy 0.13** 对它们进行了重大重做,以减少样板代码并使它们更面向数据。告别`TextureAtlasSprite` 和`UiTextureAtlasImage` 组件(以及它们相应的`Bundle` 类型)。现在通过向普通精灵和图像实体添加一个附加组件来启用纹理图集:TextureAtlas
。
为什么? #
纹理图集(有时称为精灵表)只是绘制给定纹理的自定义部分。这仍然是精灵或图像行为,我们只是绘制了一个子集。新的 TextureAtlas
组件通过存储来体现这一点
- 一个
Handle<TextureAtlasLayout>
,一个将索引映射到纹理的Rect
部分的资产 - 一个
usize
索引,用于定义我们要显示的布局的哪个Rect
部分
光照 RenderLayers
#
RenderLayers
是 Bevy 对通过过滤相机所能看到的内容来快速隐藏和显示大量实体的答案……非常适合自定义角色手中持有物品的第一人称视角(或确保吸血鬼不会出现在你的镜子里!)。
RenderLayers
现在与灯光配合良好,修复了一个严重的限制,以确保这个很棒的功能能够适当发挥作用!
绑定组布局项 #
我们添加了一个新的 API,其灵感来自 0.12 中的绑定组项 API,用于声明绑定组布局。这个新的 API 基于使用内置函数来定义绑定组布局资源并根据其位置自动设置索引。
以下是一个关于如何声明新布局的简短示例
let layout = render_device.create_bind_group_layout(
"post_process_bind_group_layout",
&BindGroupLayoutEntries::sequential(
ShaderStages::FRAGMENT,
(
texture_2d_f32(),
sampler(SamplerBindingType::Filtering),
uniform_buffer::<PostProcessingSettings>(false),
),
),
);
RenderGraph
的类型安全标签 #
Bevy 在定义标签时广泛使用 Rust 的类型系统,让开发人员可以依靠工具来捕获拼写错误并简化重构。但这不适用于 Bevy 的渲染图。在渲染图中,使用硬编码(并且可能重叠)的字符串来定义节点和子图。
// Before 0.13
impl MyRenderNode {
pub const NAME: &'static str = "my_render_node"
}
在 **Bevy 0.13** 中,我们使用一种更健壮的方式来命名渲染节点和渲染图,借助于 bevy_ecs
已经使用的类型安全标签模式。
// After 0.13
#[derive(Debug, Hash, PartialEq, Eq, Clone, RenderLabel)]
pub struct PrettyFeature;
有了这些,常量值的较长路径将变得更短和更简洁
// Before 0.13
render_app
.add_render_graph_node::<ViewNodeRunner<PrettyFeatureNode>>(
core_3d::graph::NAME,
PrettyFeatureNode::NAME,
)
.add_render_graph_edges(
core_3d::graph::NAME,
&[
core_3d::graph::node::TONEMAPPING,
PrettyFeatureNode::NAME,
core_3d::graph::node::END_MAIN_PASS_POST_PROCESSING,
],
);
// After 0.13
use bevy::core_pipeline::core_3d::graph::{Node3d, Core3d};
render_app
.add_render_graph_node::<ViewNodeRunner<PrettyFeatureNode>>(
Core3d,
PrettyFeature,
)
.add_render_graph_edges(
Core3d,
(
Node3d::Tonemapping,
PrettyFeature,
Node3d::EndMainPassPostProcessing,
),
);
当您需要渲染节点的动态标签时,仍然可以通过例如元组结构来实现这些标签
#[derive(Debug, Hash, PartialEq, Eq, Clone, RenderLabel)]
pub struct MyDynamicLabel(&'static str);
这特别好,因为我们不必在这里存储字符串:我们可以使用整数、自定义枚举或任何其他可散列类型。
Winit 升级 #
通过我们贡献者和审阅者的英勇努力,Bevy 现已升级 为使用 winit 0.29
。 winit
是我们的窗口库:它抽象化了最终用户可能拥有的所有不同操作系统和输入设备,并提供了一个统一的 API 来实现一次编写,随处运行的体验。虽然这带来了通常的许多有价值的 错误修复和稳定性改进,但关键变化与如何处理 KeyCode
有关。
以前,KeyCode
代表键盘上一个键的逻辑意义:在 QWERTY 和 AZERTY 键盘布局之间切换时,在同一个键盘上按下同一个按钮会产生不同的结果!现在,KeyCode
代表按键的物理位置。WASD 游戏爱好者知道,这对于游戏来说是一个更好的默认值。对于大多数 Bevy 开发者来说,您可以保持现有代码不变,只需享受非 QWERTY 键盘或布局的用户更好的默认键绑定即可。如果您需要有关按下的逻辑键的信息,请使用 ReceivedCharacter
事件。
多个 Gizmo 配置 #
Gizmos 允许您使用即时模式 API 快速绘制形状。以下是使用方法
// Bevy 0.12.1
fn set_gizmo_width(mut config: ResMut<GizmoConfig>) {
// set the line width of every gizmos with this global configuration resource.
config.line_width = 5.0;
}
fn draw_circles(mut gizmos: Gizmos) {
// Draw two circles with a 5 pixels outline
gizmos.circle_2d(vec2(100., 0.), 120., Color::NAVY);
gizmos.circle_2d(vec2(-100., 0.), 120., Color::ORANGE);
}
添加一个 Gizmos
系统参数,只需调用几个方法。很酷!
Gizmos 也非常适合板条箱作者,他们可以使用相同的 API。例如,oxidized_navigation
导航网格库使用 gizmos 来进行其调试覆盖。很不错!
但是,只有一个全局配置。因此,依赖项很可能会影响游戏的 gizmos。它甚至可能使它们完全无法使用。
不太好。如何解决?Gizmo 组。
现在,Gizmos
带有一个可选参数。默认情况下,它使用全局配置
fn draw_circles(mut default_gizmos: Gizmos) {
default_gizmos.circle_2d(vec2(100., 0.), 120., Color::NAVY);
}
但是使用 GizmoConfigGroup
参数,Gizmos
可以选择不同的配置
fn draw_circles(
mut default_gizmos: Gizmos,
// this uses the distinct NavigationGroup config
mut navigation_gizmos: Gizmos<NavigationGroup>,
) {
// Two circles with different outline width
default_gizmos.circle_2d(vec2(100., 0.), 120., Color::NAVY);
navigation_gizmos.circle_2d(vec2(-100., 0.), 120., Color::ORANGE);
}
通过派生 GizmoConfigGroup
并将其注册到 App
中来创建您自己的 gizmo 配置组
#[derive(Default, Reflect, GizmoConfigGroup)]
pub struct NavigationGroup;
impl Plugin for NavigationPlugin {
fn build(&mut self, app: &mut App) {
app
.init_gizmo_group::<NavigationGroup>()
// ... rest of plugin initialization.
}
}
以下是将 gizmo 组的配置设置为不同值的方式
// Bevy 0.13.0
set_gizmo_width(mut config_store: ResMut<GizmoConfigStore>) {
let config = config_store.config_mut::<DefaultGizmoConfigGroup>().0;
config.line_width = 20.0;
let navigation_config = config_store.config_mut::<NavigationGroup>().0;
navigation_config.line_width = 10.0;
}
现在,导航 gizmos 拥有一个完全独立的配置,不会与游戏的 gizmos 发生冲突。
不仅如此,游戏开发者还可以根据自己的意愿将导航 gizmos 与他们自己的调试工具集成并切换。无论是热键、调试覆盖 UI 按钮,还是 RPC 调用。世界是你的牡蛎。
glTF 扩展 #
glTF 是一种流行的标准化开放文件格式,用于在不同程序之间存储和共享 3D 模型和场景。不过,标准的问题在于您最终会想要对其进行自定义,即使只是一点点,以更好地满足您的需求。Khronos Group 预见了这一点,明智地定义了一种标准化方式来定制格式,称为 **扩展**。
扩展可以轻松地从其他工具(如 Blender)导出,并包含 各种 其他有用的信息:从尖端的基于物理的材料信息(如各向异性)到性能提示(如如何实例化网格)。
由于 Bevy 将加载的 glTF 解析成我们自己的基于实体的对象层次结构,因此在您想做新的渲染操作时访问这些信息可能很困难!凭借 CorneliusCornbread 的更改,您可以配置加载器以将 glTF 文件的原始副本与您的加载资产一起存储,允许您根据自己的需要解析和重新处理这些信息。
资产转换器 #
资产处理的核心是实现 Process
特性,它接收一些表示资产的字节数据,对其进行转换,然后返回处理后的字节数据。但是,手动实现 Process
特性有点复杂,因此编写了一个通用的 LoadAndSave<L: AssetLoader, S: AssetSaver>
Process
实现,以使资产处理更加符合人体工程学。
使用 LoadAndSave
Process
实现,以前的资产处理管道有四个阶段
AssetReader
读取一些资产源(文件系统、http 等)并获取资产的字节数据。AssetLoader
读取字节数据并将其转换为 BevyAsset
。AssetSaver
获取 BevyAsset
,对其进行处理,然后将其转换回字节数据。- 然后,
AssetWriter
将资产字节数据写回资产源。
AssetSaver
负责转换资产并将其转换为字节数据。但是,这对于代码可重用性来说有点问题。每次您想要转换某个资产(例如图像)时,都需要重写将资产转换为字节数据的部分。为了解决这个问题,AssetSaver
现在仅负责将资产转换为字节数据,并引入了负责转换资产的 AssetTransformer
。添加了一个新的 LoadTransformAndSave<L: AssetLoader, T: AssetTransformer, S: AssetSaver>
Process
实现来利用新的 AssetTransformer
。
使用 LoadTransformAndSave
Process
实现的新资产处理管道有五个阶段
AssetReader
读取一些资产源(文件系统、http 等)并获取资产的字节数据。AssetLoader
读取字节数据并将其转换为 BevyAsset
。AssetTransformer
获取资产并以某种方式对其进行转换。AssetSaver
获取 BevyAsset
并将其转换回字节数据。- 然后,
AssetWriter
将资产字节数据写回资产源。
除了具有更好的代码可重用性之外,此更改还鼓励为各种常见资产类型编写 AssetSaver
,这可以用于将运行时资产保存功能添加到 AssetServer
中。
以前的 LoadAndSave
Process
实现仍然存在,因为在某些情况下资产转换步骤是不必要的,例如在将资产保存为压缩格式时。
请参阅 资产处理示例,详细了解如何使用 LoadTransformAndSave
来处理自定义资产。
实体优化 #
Entity
(Bevy 用于实体的 64 位唯一标识符)在本周期中进行了多次更改,为关系奠定了更多基础,并提供了相关且不错的性能优化。这里的工作涉及大量深入研究编译器代码生成/汇编输出,运行大量基准测试并进行测试,以确保所有更改都不会造成破坏或重大问题。虽然这里的工作主要处理安全代码,但底层假设发生了很多变化,可能会影响其他地方的代码。这是 Bevy 0.13 中最侧重于“微优化”的一组更改。
- #9797:创建了一个统一的标识符类型,为我们使用相同快速、复杂的代码铺平了道路,无论是在我们的
Entity
类型中,还是在期待已久的关联关系中 - #9907:允许我们以与
Entity
相同的位数存储Option<Entity>
,方法是更改Entity
类型的布局,为None
变体保留一个u64
值 - #10519:将我们切换到为
Entity
手动制作的PartialEq
和Hash
实现,以提高速度并在热循环中节省指令 - #10558:将 #9907 和 #10519 的方法结合起来,进一步优化
Entity
的布局,并优化了我们的PartialOrd
和Ord
实现! - #10648:进一步优化了我们的实体散列,更改了我们在散列中进行乘法的机制,以在优化后的编译器输出中节省一条宝贵的汇编指令
也应向在 #2372 和 #3788 中进行类似工作的作者表示衷心的感谢:虽然他们的工作最终没有被合并,但它对这些最新的更改来说是一个非常宝贵的灵感和先前艺术的来源。
以上结果显示了我们从开始(optimised_eq
是第一个引入基准测试的 PR)到现在的所有优化都已到位(optimised_entity
)。所有方面都取得了改进,具有明显的性能优势,这些优势应该会影响代码库的多个区域,而不仅仅是在散列实体时。
链接的 PR 中包含大量清晰易懂的细节,其中还有一些关于汇编输出分析的有趣内容。如果您感兴趣,可以在后台打开一些新标签!
将 Query::for_each
移植到 QueryIter::fold
覆盖 #
目前,为了充分利用对查询的迭代性能,必须使用 Query::for_each
来利用编译器可以应用的自动矢量化和内部迭代优化。但是,这在 Rust 中并不符合惯例,而且不是迭代器方法,因此您无法在迭代器链上使用它。然而,对于某些迭代器方法,有可能获得相同的优势,为此 #6773 by @james7132 试图实现这一点。通过对 QueryIter::fold
提供覆盖,可以移植 Query::for_each
的迭代策略,使 Query::iter
等方法能够获得相同的收益。目前并非所有迭代器方法都受益于此,因为它们需要覆盖 QueryIter::try_fold
,但目前这仍然是仅限夜间版本的优化。这种相同的方法在 Rust 标准库中也有体现。
这在几个方面去除了重复的代码,例如不再需要同时使用 Query::for_each
和 Query::for_each_mut
,因为只需要调用 Query::iter
或 Query::iter_mut
即可。所以像这样的代码:
fn some_system(mut q_transform: Query<&mut Transform, With<Npc>>) {
q_transform.for_each_mut(|transform| {
// Do something...
});
}
变成
fn some_system(mut q_transform: Query<&mut Transform, With<Npc>>) {
q_transform.iter_mut().for_each(|transform| {
// Do something...
});
}
还比较了主分支与 PR 中的汇编输出,没有发现旧 Query::for_each
与新 QueryIter::for_each()
输出之间有任何明显的差异,验证了这种方法并确保应用了内部迭代优化。
此外,Query::par_for_each
中的相同内部迭代优化现在重用了 for_each
中的代码,也去除了那里的重复代码,并允许用户使用 par_iter().for_each()
。总的来说,这意味着不再需要 Query::for_each
、Query::for_each_mut
、Query::_par_for_each
、Query::par_for_each_mut
,因此这些方法在 0.13 版本中已弃用,将在 0.14 版本中删除。
减少 TableRow
的 as
转换 #
我们 ECS 内部并非所有改进都集中在性能上。进行了一些小的更改,以提高类型安全性并清理部分代码库,减少各种调用站点对 TableRow
进行的 as
转换。as
转换的问题在于,在某些情况下,转换会通过静默截断值而失败,这可能会导致通过访问错误的行等情况造成混乱。#10811 by @bushrat011899 提出清理 TableRow
周围的 API,提供由 assert
支持的便捷方法,以确保转换操作永远不会失败,或者如果失败,它们会正确地出现恐慌。
自然,在潜在的热点代码路径中添加断言会引起一些担忧,需要进行大量的基准测试工作,以确认是否存在回归以及回归程度。通过仔细放置新的 assert
,检测到的回归情况约为 0.1%,远在噪声范围之内。但带来的好处是一个更不容易出错的 API 和更健壮的代码。对于像 bevy_ecs
这样的复杂不安全代码库,任何微小的改进都有帮助。
事件生命周期更长 #
事件是将数据传递到系统和系统之间的一个有用工具。
在内部,Bevy 事件是双缓冲的,因此一旦缓冲区交换两次,给定的事件将被静默丢弃。Events<T>
资源就是这样设置的,以便事件在可预测的时间段后被丢弃,防止它们的队列无限增长并导致内存泄漏。
在 0.12.1 之前,事件队列在每次更新(即每帧)时交换。这对于在 FixedUpdate
中有逻辑的游戏来说是一个问题,因为这意味着事件通常会在下一个 FixedUpdate
中的系统读取它们之前消失。
Bevy 0.12.1 将交换节奏改为“每次运行 FixedUpdate
一次或多次的更新”(仅当安装了 TimePlugin
时)。此更改确实解决了最初的问题,但随后在另一个方向上造成了问题。用户惊讶地发现,他们的一些带有 run_if
条件的系统会迭代比预期更旧的事件。(事后看来,我们应该将其视为重大变更,并将其推迟到此版本)。此更改还引入了一个错误(在本版本中已修复),其中只有一种类型的事件被丢弃。
一个提出的未来解决方案是,使用事件时间戳来更改 EventReader<T>
可见的事件默认范围,从而解决 Update
和 FixedUpdate
之间持续存在的但无意中的耦合。这样,Update
中的系统将跳过比一帧更旧的任何事件,而 FixedUpdate
中的系统仍然可以看到它们。
目前,可以通过简单地删除 EventUpdateSignal
资源来恢复 <=0.12.0
的行为。
fn main() {
let mut app = App::new()
.add_plugins(DefaultPlugins);
/* ... */
// If this resource is absent, events are dropped the same way as <=0.12.0.
app.world.remove_resource::<EventUpdateSignal>();
/* ... */
app.run();
}
接下来是什么? #
我们还有很多工作正在进行中!其中一部分可能会在Bevy 0.14 中发布。
查看 Bevy 0.14 里程碑,了解贡献者正在为Bevy 0.14 集中精力完成的当前工作的最新列表。
更多编辑器实验 #
在才华横溢的 JMS55 的带领下,我们开放了一个自由形式的 游乐场,来定义和回答关于 bevy_editor
设计的 关键问题:不是通过讨论,而是通过具体的原型设计。我们应该使用一个进程内编辑器(对游戏崩溃的鲁棒性较差)还是一个外部编辑器(更复杂)?我们应该发布一个编辑器二进制文件(非常适合非程序员)还是将其嵌入游戏本身(可高度定制)?让我们通过实践来找出答案!
有一些令人难以置信的模型、功能原型和与第三方编辑器相关的项目。一些亮点
- 由 Discord 上的
@!!&Amy
创建的 Bevy 品牌的编辑器 UI 模型,想象一个基于 ECS 的编辑器 可能是什么样子 bevy_animation_graph
:一个完全功能的、由资产驱动的动画图 crate,它有自己的基于节点的编辑器,适用于 Bevyspace_editor
:一个经过打磨的 Bevy 原生第三方场景编辑器,您可以立即使用它!Blender_bevy_components_workflow
:一套功能强大的工具生态系统,可以让你立即使用 Blender 作为游戏无缝的关卡和场景编辑器。@coreh
对 基于反射的远程协议 的实验,结合一个交互式基于 Web 的编辑器,允许开发人员从其他进程、语言甚至设备检查和控制他们的 Bevy 游戏!立即试用!
看到这些进展真的令人激动,我们渴望将这些能量和经验投入到官方的一方努力中。
bevy_dev_tools
#
顺利进行游戏开发的秘诀是强大的工具。是时候为 Bevy 开发者提供他们需要的工具来检查、调试和分析他们的游戏,作为第一方体验的一部分了。从 FPS 计到系统步进,再到与出色的 bevy-inspector-egui
相当的第一方等效物:将这些工具放在 Bevy 本身中,有助于我们对其进行打磨,为新用户指明正确的方向,并让我们在 bevy_editor
本身中使用它们。
新的场景格式 #
场景 是 Bevy 用于将 ECS 数据序列化到磁盘的通用答案:跟踪实体、组件和资源,用于保存游戏和加载预制关卡。但是,现有的基于 .ron 的场景格式很难手动编写,过于冗长且脆弱;对你的代码(或你的依赖项的代码!)的更改会迅速使保存的场景失效。Cart 一直在酝酿一个 修订后的场景格式,该格式具有紧密的 IDE 和代码集成,解决了这些问题,并使在 Bevy 中创作内容(包括 UI!)成为一种乐趣。无论你是编写代码、编写场景文件还是从 GUI 生成场景文件。
bevy_ui
改进 #
bevy_ui
有不少问题和局限性,既有普通的,也有架构上的;但是,我们能够并且正在做一些切实的事情来改进它:改进的场景格式为定义布局时结束样板提供了帮助,圆角 边框 只需要审阅者稍加关注,而且强大的且广受欢迎的 [bevy_mod_picking
] 中的对象拾取计划将被上游用于 UI 和游戏玩法。今天已经存在大量令人惊叹的 第三方 UI 解决方案,从这些解决方案中学习,并致力于 UI 逻辑和响应能力的核心架构是重中之重。
Meshlet 渲染 #
将网格分割成称为 meshlet 的三角形簇,这会带来许多效率提升。在 0.13 开发周期中,我们在这项功能上取得了 很大进展。我们实现了一个 GPU 驱动的 meshlet 渲染器,它可以扩展到更多的三角形密集场景,而 CPU 负载要低得多。但是,内存使用量非常高,我们还没有实现 LOD 或压缩。与其发布半成品,我们将会继续迭代,并非常高兴地(希望)在未来发布中为您提供此功能。
朝着关系迈进的稳步步伐 #
实体-实体关系,即直接在 ECS 中跟踪和管理实体之间连接的能力,多年来一直是人们要求最多的 ECS 功能之一。遵循 flecs
开辟的道路,#ecs-dev
中的疯狂科学家们正在稳步地 重塑我们的内部结构、尝试外部实现,并发布构建快速、健壮和符合人体工程学解决方案所需的通用构建块(如动态查询或 生命周期钩子)。
支持 Bevy #
赞助有助于使我们在 Bevy 上的工作可持续。如果你相信 Bevy 的使命,请考虑 赞助我们……任何帮助都非常感谢!
贡献者 #
Bevy 是由 一群人 共同打造的。非常感谢 198 位贡献者,使这次发布(以及相关的文档)成为可能!按随机顺序排列
- @ickk
- @orph3usLyre
- @tygyh
- @nicopap
- @NiseVoid
- @pcwalton
- @homersimpsons
- @Henriquelay
- @Vrixyz
- @GuillaumeGomez
- @porkbrain
- @Leinnan
- @IceSentry
- @superdump
- @solis-lumine-vorago
- @garychia
- @tbillington
- @Nilirad
- @JMS55
- @kirusfg
- @KirmesBude
- @maueroats
- @mamekoro
- @NiklasEi
- @SIGSTACKFAULT
- @Olle-Lukowski
- @bushrat011899
- @cbournhonesque-sc
- @daxpedda
- @Testare
- @johnbchron
- @BlackPhlox
- @MrGVSV
- @Kanabenki
- @SpecificProtagonist
- @rosefromthedead
- @thepackett
- @wgxer
- @mintlu8
- @AngelOnFira
- @ArthurBrussee
- @viridia
- @GabeeeM
- @Elabajaba
- @brianreavis
- @dmlary
- @akimakinai
- @VitalyAnkh
- @komadori
- @extrawurst
- @NoahShomette
- @valentinegb
- @coreh
- @kristoff3r
- @wackbyte
- @BD103
- @stepancheg
- @bogdiw
- @doup
- @janhohenheim
- @ekropotin
- @thmsgntz
- @alice-i-cecile
- @tychedelia
- @soqb
- @taizu-jin
- @kidrigger
- @fuchsnj
- @TimJentzsch
- @MinerSebas
- @RomainMazB
- @cBournhonesque
- @tripokey
- @cart
- @pablo-lua
- @cuppar
- @TheTacBanana
- @AxiomaticSemantics
- @rparrett
- @richardhozak
- @afonsolage
- @conways-glider
- @ItsDoot
- @MarkusTheOrt
- @DavJCosby
- @thebluefish
- @DGriffin91
- @Shatur
- @MiniaczQ
- @killercup
- @Ixentus
- @hecksmosis
- @nvdaz
- @james-j-obrien
- @seabassjh
- @lee-orr
- @Waridley
- @wainwrightmark
- @robtfm
- @asuratos
- @Ato2207
- @DasLixou
- @SludgePhD
- @torsteingrindvik
- @jakobhellermann
- @fantasyRqg
- @johanhelsing
- @re0312
- @ickshonpe
- @BorisBoutillier
- @lkolbly
- @Friz64
- @rodolphito
- @TheBlckbird
- @HeyZoos
- @nxsaken
- @UkoeHB
- @GitGhillie
- @ibotha
- @ManevilleF
- @andristarr
- @josfeenstra
- @maniwani
- @Trashtalk217
- @benfrankel
- @notverymoe
- @simbleau
- @aevyrie
- @Dig-Doug
- @IQuick143
- @shanecelis
- @mnmaita
- @Braymatter
- @LeshaInc
- @esensar
- @Adamkob12
- @Kees-van-Beilen
- @davidasberg
- @andriyDev
- @hankjordan
- @Jondolf
- @SET001
- @hxYuki
- @matiqo15
- @capt-glorypants
- @hymm
- @HugoPeters1024
- @RyanSpaker
- @bardt
- @tguichaoua
- @SkiFire13
- @st0rmbtw
- @Davier
- @mockersf
- @antoniacobaeus
- @ameknite
- @Pixelstormer
- @bonsairobo
- @matthew-gries
- @NthTensor
- @tjamaan
- @Architector4
- @JoJoJet
- @TrialDragon
- @Gadzev
- @eltociear
- @scottmcm
- @james7132
- @CorneliusCornbread
- @Aztro-dev
- @doonv
- @Malax
- @atornity
- @Bluefinger
- @kayhhh
- @irate-devil
- @AlexOkafor
- @kettle11
- @davidepaci
- @NathanSWard
- @nfagerlund
- @anarelion
- @laundmo
- @nelsontkq
- @jeliag
- @13ros27
- @Nathan-Fenner
- @softmoth
- @xNapha
- @asafigan
- @nothendev
- @SuperSamus
- @devnev
- @RobWalt
- @ThePuzzledDev
- @rafalh
- @dubrowgn
- @Aceeri
完整变更日志 #
上面提到的更改仅是我们本轮更新中最引人注目、影响最大的更改。还有无数的错误修复、文档更改和 API 易用性调整也包含在内。有关更改的完整列表,请查看下面列出的 PR。
A-Rendering + A-Windowing #
- 允许 prepare_windows 在主线程之外运行。
- 允许 prepare_windows 在所有平台的主线程之外运行
- 如果不需要,不要运行
create_surfaces
系统 - 修复 create_surfaces 系统顺序
A-Animation + A-Reflection #
A-Assets #
- 不要在
AssetPath::try_parse
中.unwrap()
- feat: 为
AssetMode
实现Debug
- 从嵌入式资产!文档中删除 rogue :
- 使用
tree
语法解释 bevy_rock 文件结构 - 使 AssetLoader/Saver Error 类型边界与 anyhow::Error 兼容
- 修复未类型化的标记资产加载
- 将
load_untyped
添加到 LoadContext - 修复 wasm 上的示例 custom_asset_reader
ReadAssetBytesError::Io
公开了失败的路径- 添加了允许管道式资产加载的方法
- 为 load_folder 和 load_untyped 添加缺少的资产加载错误日志
- 修复启用了 file_watcher 的 wasm 构建
- 在无法创建资产文件夹时不要 panic (#10613)
- 在 SceneSpawner 中使用句柄进行排队场景
- 修复 file_watcher 功能无限期挂起
- 为枚举派生资产
- 确保 Un/Typed
AssetId
和Handle
之间的一致性 - 修复资产加载错误
- 删除句柄的 typeid 的双重哈希
- AssetMetaMode
- 修复 GLTF 场景依赖项并使完整场景渲染可预测
- 打印精确且正确的监视警告(并且仅在必要时)
- 允许使用活动句柄删除和重新加载资产
- 添加 GltfLoaderSettings
- 重构
bevy_asset
中的process_handle_drop_internal()
- 修复加载 gltf 文件时的 base64 填充
- 资产应默认保留在 CPU 上
- 不要自动创建资产文件夹
- 为
Assets::add
使用impl Into<A>
- 将
reserve_handle
添加到Assets
。 - 在不正确的资产标签上提供更好的错误消息
- GLTF 扩展支持
- 修复嵌入式观察器以与外部板条箱一起工作
- 添加了 AssetLoadFailedEvent、UntypedAssetLoadFailedEvent
- 如果需要,自动创建导入的资产文件夹
- 修复轻微的拼写错误
- 在 get_meta_path panic 消息中包含资产路径
- 修复
AssetReader::is_directory
函数的文档 - AssetSaver 和 AssetTransformer 拆分
- AssetPath 源解析修复
- 允许 AssetLoader 中使用 TextureAtlasBuilder
- 为
AssetServer
添加一个用于获取资产监视状态的获取器 - 使 SavedAsset::get_labeled 接受 &str 作为标签
- 添加了对无扩展名资产的支持
- 修复嵌入式资产路径操作
- 修复 AssetTransformer 损坏 LabeledAssets
- 将 asset_events 放在运行条件之后
- 使用资产路径扩展名进行
AssetLoader
消歧
A-Core + A-App #
A-Accessibility #
A-Transform #
A-ECS + A-Hierarchy #
A-Text #
A-Assets + A-UI #
A-Utils + A-Time #
A-Rendering + A-Assets #
A-Physics #
A-ECS + A-Editor + A-App + A-Diagnostics #
A-Reflection + A-Scenes #
A-Audio + A-Windowing #
A-Build-System + A-Meta #
A-ECS + A-Time #
A-Meta #
- 将“更新屏幕截图”添加到发布检查表中
- 从自述文件中删除对特定项目的引用
- 修复文件之间断开的链接
- [doc] 修复 CONTRIBUTING.md 中的拼写错误
- 删除未使用的命名空间声明
- 将文档链接添加到根
Cargo.toml
- 将第三方插件指南迁移到本书中
- 运行 markdownlint
- 改进
config_fast_builds.toml
- 在
config_fast_builds.toml
中使用-Z threads=0
选项 - CONTRIBUTING.md:提到拆分复杂的 PR
A-Time #
- docs:使用
read
而不是已弃用的iter
- 重命名
Time::<Fixed>::overstep_percentage()
和Time::<Fixed>::overstep_percentage_f64()
- 将
Timer::{percent,percent_left}
重命名为Timer::{fraction,fraction_remaining}
- 记录如何配置 FixedUpdate
- 将 discard_overstep 函数添加到
Time<Fixed>
A-Assets + A-Reflection #
A-Diagnostics + A-Utils #
A-Windowing + A-App #
A-ECS + A-Scenes #
A-Hierarchy #
- bevy_hierarchy:添加一些文档
- 使 bevy_app 和 reflect 成为 bevy_hierarchy 的选择退出。
- 添加
bevy_hierarchy
板条箱和插件文档 - 将“AddChild”重命名为“PushChild”
- 在 bevy_hierarchy 中内联微不足道的 method
A-ECS + A-App #
A-Transform + A-Math #
A-UI + A-Text #
A-Input #
- 使 ButtonSettings.is_pressed/released 公开
- 将
Input
重命名为ButtonInput
- 添加 method 以检查所有输入是否都被按下
- 将窗口实体添加到 TouchInput 事件中
- 使用 clear 和 reset method 扩展
Touches
- 将逻辑键数据添加到 KeyboardInput
- 为 GamepadButtonType 派生 Ord。
- 将 delta 添加到 CursorMoved 事件
A-Rendering + A-Diagnostics #
A-Rendering #
- 修复 bevy_pbr 着色器函数名称
- 为 VisibilityBundle 和 SpatialBundle 实现 Clone
- 重新导出
wgpu::Maintain
- 在压力测试中使用一致的比例因子和分辨率
- 忽略非活动相机
- 添加 shader_material_2d 示例
- 更多非活动相机检查
- 修复后处理示例以仅对具有设置组件的相机运行效果
- 确保在 camera_system 中检查添加的图像资产
- 确保 ExtendedMaterial 与反射一起工作(以启用 bevy_egui_inspector 集成)
- 显式颜色转换 method
- 重新导出 wgpu BufferAsyncError
- 改进 shader_material 示例
- 非均匀透射样本
- 解释
AmbientLight
如何插入和配置 - 将 wgpu_pass method 添加到 TrackedRenderPass
- 将
depth_bias
添加到Material2d
- 尽可能使用 as_image_copy
- impl
From<Color>
for ClearColorConfig - 确保在 prepass.wgsl 中始终使用 instance_index 推送常量。
- 绑定组布局条目
- 预处理顶点着色器始终输出世界位置
- 交换材质和网格绑定组
- 尝试插入 Aabbs
- 修复预处理绑定问题,当没有使用所有预处理绑定时会导致崩溃
- 修复 custom_material_2d.wgsl 中的绑定组
- 仅对 mikktspace 法线贴图规范化非零法线
- 光照渲染层
- 解释如何生成 RegularPolygon 网格
- 修复 webgl 上的 Mesh2d 法线
- 更新到 wgpu 0.18
- 修复
ViewVisibility
文档中的拼写错误 - 为 bevy_sprite 添加一些文档
- 修复 BindingType 导入警告
- 使用不同的填充和采样更新 texture_atlas 示例
- 当 Sprite 组件在 calculate_bounds_2d() 中改变时更新 AABB
- OrthographicProjection.scaling_mode 不仅仅用于调整大小
- 为
BloomCompositeMode
派生Debug
- 记录 compute_aabb 上的 None 条件
- 用函数调用替换计算
- 注册相机类型。
- 添加在 2D 中进行像素完美网格捕捉的示例
- 杂项清理
- 跟踪纹理第一次被清除的时间
- 修复 Mesh::ATTRIBUTE_UV_0 文档
- 不要为透射材质加载预处理法线
- 导出 tonemapping_pipeline_key (2d)、alpha_mode_pipeline_key
- 简化 examples/3d/orthographic
- 实现光照贴图。
- 为预处理关节提升顶点属性索引。
- 修复:由于持久性策略设置为
Unload
导致 Gizmos 崩溃。将其更改为Keep
- 用于 RenderTargets 和图像句柄的可用性方法
- 解释摄像机物理尺寸以像素为单位
- 更新过时的注释
- 恢复“实现最小的反射探针。(#10057)”
- 解释 OrthographicProjection.scale
Mul<f32>
用于 ScalingMode- OrthographicProjection 的 Rustdoc 示例
- 启用确定性渲染的选项
- 修复 ssao 仅采样 mip 0
- 恢复“实现最小的反射探针。(#10057)”
- 精灵切片和贴图
- 近似间接镜面遮挡
- 纹理图集重构
- 曝光设置(已采用)
- 从 GpuArrayBuffer 中移除 Vec
- 使
DynamicUniformBuffer::push
接受&T
而不是T
- 在曝光 PR 之后恢复剩余三个示例中的亮度
- 可自定义的相机主纹理使用
- 清理确定性示例
- 实现最小的反射探针(修复 macOS、iOS 和 Android)。
- 优化 batch_and_prepare_render_phase
- 将
storage_texture
选项添加到 as_bind_group 宏 - 恢复与渲染相关的关联类型名称更改
- 网格片准备
- 在创建缓存的绑定组时重用采样器
- 添加动画材质示例
- 更新到 wgpu 0.19 和 raw-window-handle 0.6
- 修复 Sprite::rect 被忽略的错误
- 添加解释流明和勒克斯之间差异的文档
- 修复由于未排干的 AssetEvent 事件导致无限资产准备
- 在使用
EnvironmentMapLight
的调试版本中的 DXC 着色器编译器中的 ICE 的解决方法 - 将 tonemapping 示例的图像查看器更新重构为两个系统
- 添加
Mesh
变换 - 修复延迟中的镜面环境贴图
- 添加
Meshable
特征并为 2D 图元实现网格化 - 优化 extract_clusters 和 prepare_clusters 系统
- RenderAssetPersistencePolicy → RenderAssetUsages
- 渲染图标签化
- 在着色器定义后面设置漫射和镜面透射
- 添加用于平移、旋转和缩放操作的帮助程序 - Mesh
- CameraProjection::compute_frustum
- 在
MeshVertexAttribute
常量的 docstrings 中添加格式 - 异步管道编译
- 按管道然后按网格对非透明通道进行排序,以实现更好的批处理
- 添加 Mesh 的 remove_indices
- 实现辐照度体积。
- 网格插入索引
- 如果没有任何视图,则不要尝试为光探针创建统一缓冲区。
- 在 Msaa 中获取管道时正确检查结果
- 当主世界被丢弃时等待渲染应用程序
- 弃用
bevy_render::mesh::shape
中的形状 - 缓存用于丢弃交换链 TextureViews 的 QueryState
- 多线程渲染命令编码
- 修复
Quad
弃用消息,该消息提到了一个不存在的类型 - 停止将网格实体提取到渲染世界。
- 停止将光探针数组复制到着色器中的堆栈。
- 添加
Mesh::merge
- 将 TextureAtlasLayout 称为布局,而不是图集
- 修复阴影批处理
- 更改光照默认值并修复光照示例
- 新的曝光和照明默认值(并校准示例)
- 将 MeshUniform::new() 更改为公开。
- 重命名核心渲染图标签
- 在 ColorAttachment 中支持可选的清除颜色。
- 辐照度:使用 textureSampleLevel 支持 WebGPU
- 添加用于在 RenderPlugin 上异步创建管道的配置
- 为 Exposure 派生 Reflect
- 添加
MeshPipelineKey::LIGHTMAPPED
,在阴影图传递过程中适用。 - 辐照度体积示例调整
- 在 WebGL 和 WebGPU 上禁用辐照度体积。
- 从
bevy_pbr
中删除naga_oil
依赖项
A-场景 #
- 在
bevy_scene
中重新导出ron
- 修复加载场景示例以使用正确的序列化格式进行旋转字段
- 在文档注释中提到 DynamicSceneBuilder
- 在场景示例中提到 DynamicSceneBuilder
- 为
SceneInstanceReady
实现 Std 特征 - 将 SceneSpawner::spawn_dynamic_sync 更改为返回 InstanceID
- 修复场景示例
- 仅对每个场景发送一次
SceneInstanceReady
A-实用程序 #
- bevy_utils:导出
generate_composite_uuid
实用程序函数 - 在
EntityHasher
中保存一条指令 - 将 SystemTime 添加到 bevy_utils
- 从 bevy_utils 重新导出 smallvec crate
- 启用克隆 EntityHashMap 和 PreHashMap
- 为
CowArc
实现Borrow
和AsRef
- 哈希稳定性保证
- 弃用 hashbrown 重新导出
- 将 ahash 更新到 0.8.7
A-UI #
- ui 材质:修复右侧边框宽度
- 将 PartialEq 添加到 Anchor
- UI 材质:每个材质应该有自己的缓冲区
- UI 材质:忽略具有
BackgroundColor
组件的实体 - 修复使用 UiMaterial 中的图像时发生的恐慌
- 使 UI 节点的剪裁区域不可交互
- 修复 resolve_outlines_system 中的拼写错误
- 通过节点自己的剪裁矩形剪裁轮廓,而不是父节点的剪裁矩形。
- 为具有
Display::None
的 UI 节点提供一个空的剪裁矩形 - 为 bevy_ui 创建序列化特征
- 使 bevy_ui 中的剩余类型反映 Default 特征,如果……
- 相机驱动的 UI
- 修复移动 UI 根节点时偶尔发生的崩溃
- 修复在没有摄像机的情况下 Text UI 发生的恐慌
- 允许用户选择默认的 ui 摄像机
- bevy_ui 中的 Rustdoc 链接
- 避免无条件地解开 Result - UI 堆栈系统
A-资产 + A-诊断 #
A-音频 + A-反射 #
A-音频 #
- 添加
VolumeLevel::ZERO
- 在 bevy_audio 中对系统进行去重
- 对
play_queued_audio_system()
进行非侵入式重构 - 文档:AnimationPlayer::play 没有 transition_duration 参数
- 删除忽略全局音量的能力
- 全局空间缩放的可选覆盖
A-任务 #
- 在单线程上下文中使 FakeTask 对外公开
- 在
bevy_tasks
中重新导出futures_lite
- 将 bevy_tasks futures-lite 提升到 2.0.1
- 修复
TaskPool::scope_with_executor_inner
中错误的转换类型 - 在 async_compute 示例中使用
std::thread::sleep
而不是自旋等待
A-ECS #
- 对
EntityMapper
使用EntityHashMap
- 允许注册 boxed 系统
- 删除调度程序中不必要的 if 语句
- 优化
Entity::eq
- 添加 'World::run_system_with_input' 函数 + 允许
World::run_system
获取系统输出 - 将
Event
发送方法更新为返回EventId
- 一些有关 IntoSystemSet 的文档
- 在
pipe
文档中链接到In
- 使用 repr 对齐和手动
PartialOrd
/Ord
优化Entity
- 允许在元组结构体上使用 #[derive(Bundle)](第 3 次)
- 为
EntityWorldMut
添加Entry
api。 - 使 RemovedSystem 的 impl 块泛型
- 追加命令
- Ref 的 Rustdoc 示例
- 从其他调度程序链接到
Main
调度程序文档 - 警告 Added/Changed 过滤器看不到延迟的更改
- 修复非功能性的非确定性系统顺序示例
- 从 PR #10718 复制
Condition
特征的文档 - 为
CommandQueue
实现Drop
- 将 WorldQuery 分裂为 WorldQueryData 和 WorldQueryFilter
- 使 IntoSystemConfigs::into_configs 成为公共 API(在文档中可见)
- 覆盖 QueryIter::fold 以将 Query::for_each 的性能提升移植到 select Iterator 组合器
- 弃用 QueryState::for_each_unchecked
- 阐明 Commands 的目的
- 在 Components 中使 ComponentId 类型化
- 减少
TableRow
的as
转换 - 添加
EntityCommands.retain
和EntityWorldMut.retain
- 删除示例中不必要的 ResMut
- 为系统类型添加一些断言
- 删除对默认调度的引用
- 通过链接到更多细节来改进
EntityWorldMut.remove
、retain
和despawn
文档 - 重新排序 SystemSchedule 中的字段
- 将
WorldQueryData
和WorldQueryFilter
重命名为QueryData
和QueryFilter
- 修复
UnsafeWorldCell
使用示例的健全性 - 在 BlobVec 测试 aligned_zst 中实际检查对齐
- 将
Q
类型参数重命名为D
,当引用WorldQueryData
时 - 允许编辑启动调度程序
- 自动插入同步点
- 简化
QueryState
方法中的生命周期 - 添加 is_resource_changed_by_id + is_resource_added_by_id
- 为了清晰起见,重新命名一些生命周期(ResMut 等)
- 将不存在的实体行为添加到 Has 文档中
- 修复 Has 文档中的拼写错误
- 将 insert_state 添加到 App 中。
- 解释 Changed、Added 不是原型过滤器
- 在
States
文档中添加缺失的冒号 - 解释 EventWriter 限制并发性
- 改进 SystemName 的文档
- 为 WorldId 实现 ExclusiveSystemParam
- 为 PhantomData 实现 ExclusiveSystemParam
- 删除 bevy_ecs 上的小警告
- 将
ArchetypeEntity::entity
重命名为ArchetypeEntity::id
- 修复 EntityMut 说明中的拼写错误
- 为 In 实现 Deref 和 DerefMut
- 为 SystemName 实现 ExclusiveSystemParam
- 打印一个警告,用于从 CommandQueue 中丢弃的未应用命令
- 为 EntityHash 实现 TypePath
- 修复 ZST 的 BlobVec::push 中的整数溢出
- 修复 BlobVec::reserve_exact 中的整数溢出
- StateTransitionEvent
- 恢复对在可能被销毁的实体上运行
fn
EntityCommands
的支持 - 删除 apply_deferred 示例
- 通过从 Resources 中删除 tick Vecs 来最小化小分配
- 将 Entity::generation 从 u32 更改为 NonZeroU32 以进行利基优化
- 修复 B0003 示例并更新日志
- 实体和关系的统一标识符
- 简化条件
- 在文档中添加使用
State
的示例 - 跳过重新哈希 TypeId
- 使
TypeId::hash
在上游 rustc 更改的情况下更加健壮 - 修复[
Schedules
]的文档以提及排除当前调度。 - 动态查询和构建器 API
- 删除 ECS 宏中的重复
#[automatically_derived]
- 获取资源的变更 Tick 方法
- 可选状态
- 当 BlobVec 满了时,将容量加倍
- 文档化 systemparam 派生所需的生存期
- 重构:简化
Commands
和相关类型的生存期 - 为
CommandQueue
实现Debug
- 修复注释中的拼写错误
- 将 Schedule::name 重命名为 Schedule::label
- 现在可以将排他系统用于一次性系统
- 添加了使用
World::get_resource_ref
从World
获取Res<T>
的功能 - bevy_ecs: 为 par_iter_mut 添加文档示例(#11311)
- 添加一个示例演示如何在同一个系统中发送和接收事件
- 为 EntityMapper 添加一个 doctest 示例
- 为了清晰起见,改写了关于
Local<T>
的注释。(已采纳) - 在基准测试中使用批量生成
- 修复事件未被丢弃的错误
- 使 Archetypes.archetype_component_count 私有
- 弃用
Query
和QueryState
中的各种组件方法 System::type_id
一致性- [
ScheduleLabel
]派生宏中的拼写错误 - 在组件/资源相关的类型文档中,提及缺失的资源
- 公开查询访问
- 添加一种方法来检测特定范围内的更改
- 修复应用命令队列时的双重间接
- 在将执行器作为任务生成之前,立即轮询一次
- 修复
BundleInfo::new
中格式错误的文档 FilteredEntityRef
转换
A-Rendering + A-Animation #
A-ECS + A-Meta #
A-Rendering + A-UI #
A-Math #
- 定义一组基本的基元
- 添加和实现基元
- 为
Triangle2d
添加绕线顺序 - 使用
Torus
基元形状的副半径和主半径 - 从方向类型中移除
From
实现 - 为方向类型实现
TryFrom
向量,并添加InvalidDirectionError
- 添加
Direction2d::from_xy
和Direction3d::from_xyz
- 为
Direction2d
和Direction3d
实现Neg
- 为
Direction2d
和Direction3d
添加常量 - 将
approx
特性添加到bevy_math
- 将
libm
特性添加到bevy_math
- 为
Direction2d
和Direction3d
添加new_and_length
方法 - 更新
glam
、encase
和hexasphere
- 实现包围盒类型
- 移除
CubicCurve
的Default
实现 - 为基元形状实现包围盒
- 改进
Rectangle
和Cuboid
的一致性 - 更改
Ellipse
的表示方式,并改进辅助函数 - 添加
Aabb2d::new
和Aabb3d::new
构造函数 - 将几何基元添加到
bevy_math::prelude
- Direction: 将
from_normalized
重命名为new_unchecked
- 实现包围盒交点
- 为
Circle
和Sphere
添加new
构造函数 - 在基元上派生 PartialEq、Serialize、Deserialize 和 Reflect
- 文档化 RegularPolygon
- 添加 RayTest2d 和 RayTest3d
- 为基元形状添加更多构造函数和数学辅助函数
- 添加
Capsule2d
基元 - 添加体积投射交点测试
- 为交点测试类型添加 Clone
- 为方向类型实现
approx
特性 - 支持通过
Quat
旋转Direction3d
- 将 RayTest 重命名为 RayCast
- 添加包围盒和交点测试的示例
- 专门的基元示例
- 在
2d_shapes
示例中取消硬编码位置和颜色
A-Build-System #
- 使用 cargo-deny 检查所有特性
- 将 actions/github-script 从 6 升级到 7
- 将 doc_markdown clippy 规则配置添加到货物工作区
- 在整个工作区中启用
clippy::undocumented_unsafe_blocks
警告 - 删除尾部空格
- 将剩余的 clippy 规则定义移动到 Cargo.toml
- 将
clippy::manual_let_else
添加到 lints,级别为 warn - 删除未使用的导入
- 重命名函数和变量以遵循代码风格
- 删除未使用的变量
- 将 libxkbcommon-x11-0 添加到默认的 Linux 依赖项中
- 在 winit 更新后修复示例展示的补丁
- 完成清理依赖项禁用作业
- 将 actions/upload-artifact 从 2 升级到 4
- 使用 GitHub Pages 构建发布 dev-docs (第二次尝试)
- 示例展示补丁:为桌面使用默认值而不是游戏模式
- 在 build-template-pages 工具中升级 toml_edit
- Miri 在最新 nightly 上失败:将 nightly 固定到最后一个已知工作版本
- 升级 dev-docs 页面操作
- 取消固定 nightly 用于 miri
- CI 中的文档:删除锁定文件
- 将 actions/cache 从 3 升级到 4
- 简化 animated_material 示例
- 示例展示:修复窗口调整大小补丁
- 在 macOS 上运行示例以验证 PR
- 反转
missing_docs
逻辑 - 将 peter-evans/create-pull-request 从 5 升级到 6
A-Gizmos #
- 修复 gizmo 着色器中的浮点精度问题
- Gizmo 箭头
- 将圆形 Gizmo 移到单独的文件中
- 将 gizmo 弧线移到单独的文件中
- 当 bevy_sprite 和 bevy_pbr 未与 bevy_gizmos 启用时发出警告
- Gizmos 的多种配置
- 修复 gizmos 应用程序新建时的 panic
- 使用 Direction3d 作为 gizmos.circle 法线
- 为 Gizmos 实现 Arc3D
- 插入 Gizmos 配置而不是 Init
- 使用 Gizmos 绘制基元
- 修复(基元):修复多边形 gizmo 渲染错误
- 修复新网格上未应用全局线框行为
- 在
insert_gizmo_group
中覆盖 gizmo 组
A-Rendering + A-Math #
A-Core #
A-Windowing #
- 对 Window 组件的一些解释
- 在 winit 中,不要在窗口创建之前运行更新
- 添加来自 winit 的新事件
WindowOccluded
- 添加关于
WindowMode
中比例因子的注释 - 重构函数
update_accessibility_nodes
- 将
Window
比例因子更改为 f32 (已采纳) - 在 bevy_winit 中重新导出 winit::platform::android::activity::*
- 将 winit 依赖项更新到 0.29
- 删除 CanvasParentResizePlugin
- 使用
WindowBuilder::with_append()
追加画布 - 修复 Web 构建的性能下降问题
- 移动设备和 webgpu:在需要时触发重绘请求,并改进窗口创建
- 删除 Wasm 上 WinitWindows 的不必要 unsafe 实现
- 修复 Reactive 和 ReactiveLowPower 更新模式
- 更改
WinitPlugin
默认值,以便在窗口不可见时限制游戏更新速率(这次是真的) - 清理 bevy winit
- 为
bevy::window::Window
添加name
- 避免在 winit 全屏处理代码中进行 unwrap
A-UI + A-Transform + A-Text #
A-Animation #
- 修复动画在重复次数后重置的问题
- 为 bevy_animation 添加 Debug、PartialEq 和 Eq 派生。
- 支持从 gltf 获取所有类型的动画插值
- 清理查找当前关键帧的代码
- 更新动画路径缓存时跳过分配
- 用通用函数替换
cubic_spline_interpolation
宏 - 用于插值和混合的 Animatable 特性
A-ECS + A-Pointers #
A-ECS + A-Utils #
A-Reflection #
- 修复
Option
序列化问题 - 修复由
clone_value
引起的insert_reflect
panic - 删除
bevy_reflect
中无意义的特性实现导出 - 修复 Reflect 派生中的嵌套泛型
- 修复动态类型的调试打印
- reflect: 最大限度地放宽
TypePath
边界 - 使用
static_assertions
检查特性实现 - 添加
ReflectFromWorld
,并将ReflectComponent
和ReflectBundle
的FromWorld
要求替换为FromReflect
- 修复
Name
组件上的反射序列化/反序列化 - 为 Wrapping/Saturating 类型添加 Reflection
- 删除 TypeUuid
- 修复 bevy_reflect 中的警告
- bevy_reflect: 类型参数边界
- bevy_reflect: 拆分
#[reflect(where)]
- reflection: 用
impl_reflect
替换impl_reflect_struct
- 添加手动创建 ParsedPaths 的功能(+ 清理)
- bevy_reflect: Reflect
&'static str
- 改进 DynamicStruct::insert
- 缺失的注册
- 添加
ReflectKind
- doc(bevy_reflect):添加关于
impl_type_path
的特性边界的注释 - bevy_reflect_derive: 清理属性逻辑
A-ECS + A-Tasks #
A-Pointers #
A-ECS + A-Reflection #
A-Reflection + A-Gizmos #
没有区域标签 #
- 修复文档内链接警告
- 修复自定义资产示例中的小问题
- 在 HttpWasmAssetReader 中将
root_path
前缀添加到元数据路径 - 支持 wasm 示例展示中的所需特性
- 示例展示:使用补丁而不是 sed 来进行 wasm 调整
- 添加[lints]表格,修复在所有地方添加
#![allow(clippy::type_complexity)]
- 将异步 crate 要求升级到最新的主版本
- 删除 0.12 中弃用的方法
- 在
benches
crate 上运行cargo fmt
- 删除不必要的路径前缀
- 修复安全性注释中的拼写错误
- 用
first()
替换get(0)
- 删除身份
map
调用 - 将 Accessibility 插件重命名为 AccessKitPlugin,位于 bevy_winit 中
- 重新排序实现,使其与特性相同
- 替换弃用的元素
- 删除不必要的括号
- 替换弃用的元素
- 简化相等断言
- 在 linux_dependencies.md 中添加 Solus 包要求
- 将 base64 要求从 0.13.0 更新到 0.21.5
- 将 sysinfo 版本更新到 0.30.0
- 删除不必要的括号
- 重新排序实现,使其与特性相同
- 修复 ci xvfb
- 替换或文档化忽略的 doctest
- 为 bevy_utils 添加静态断言,用于编译时检查
- 修复示例中遗漏的显式转换
- 删除未使用的事件监听器依赖项
- 修复 generate_custom_mesh.rs 示例中的拼写错误
- 将示例
CameraController
提取到一个模块中 - 尽可能使用 EntityHashMap
- 修复指向插件指南的链接
- [doc] 修复 CONTRIBUTING.md 中的拼写错误和格式问题
- 为 shader_material_glsl 添加一个必需的功能
- 将 ruzstd 要求从 0.4.0 更新到 0.5.0
- 调整 gamepad 查看器示例样式
- 将
.toml
扩展名添加到.cargo/config_fast_builds
- 在 benches 中添加 README
- 修复在网络上使用 argh 的示例中的 panic
- 修复循环依赖
- 启用
unsafe_op_in_unsafe_fn
lint - 将 erased-serde 要求从 0.3 更新到 0.4
- 修复示例 send_and_receive_events
- 更新 cursor.rs
- 在未聚焦时,在压力测试中使用
Continuous
更新模式 - 不要在提取计划中自动插入
- 将 tracing-tracy 要求从 0.10.4 更新到 0.11.0,并将 tracy-client 要求从 0.16.4 更新到 0.17.0
- 尽可能使用 TypeIdMap
- 修复错误文档中的几个错别字
- bevy_render: 使用来自 bevy_core 的非发送标记
- 忽略由
screenshot
示例生成的屏幕截图 - 文档反映
RemovalDetection
也生成已取消分配的实体 - bevy_dynamic_plugin: 修复
unsafe_op_in_unsafe_fn
lint - 在文档中将
crossbeam::scope
引用替换为thread::scope
- 尽可能使用问号运算符
- 修复几个 Clippy lint
- WebGPU: 修复 web-sys 版本
- 从 linting 规则中删除 map_flatten
- 修复重复的
encase_derive_impl
依赖项
A-App #
A-Diagnostics #
- 修复 tracy 版本的 Line
- bevy_diagnostic 的一些文档
- 从 LogPlugin 中的 panic 处理程序打印到 stderr
- 添加 panic 到日志示例的功能
- 确保 tracy 依赖项符合兼容性表
- 描述 bevy_diagnostic 的目的
- 添加对在 LogPlugin 中更新跟踪订阅者的支持
- 将
DiagnosticId
替换为DiagnosticPath
- 修复到 tracy 的链接
- 修复 sysinfo CPU 品牌输出