Bevy 0.2
发表于 2020 年 9 月 19 日,作者 Carter Anderson (
@cart
@cart_cart
cartdev )
在最初的 Bevy 版本发布一个月后,感谢 **87** 位贡献者、**174** 个拉取请求以及我们慷慨的赞助商,我很高兴地宣布在crates.io上发布 **Bevy 0.2**!
对于那些不了解 Bevy 的人来说,Bevy 是一个用 Rust 构建的,令人耳目一新的简单、数据驱动的游戏引擎。您可以查看快速入门指南来开始。Bevy 永远免费且开源!您可以在 GitHub 上获取完整的源代码。
以下是此版本中的一些亮点
异步任务系统 #
Bevy 在整个引擎中使用多线程:ECS 调度、资产加载、渲染等。在此版本之前,它几乎对所有这些任务都使用Rayon。Rayon 很好,因为它通常与调用 some_list.par_iter().for_each(|x| do_something(x))
一样简单。然后 Rayon 会自动将 for_each
分解成任务,并在尽可能多的核心上运行它们。如果您想轻松地并行化代码,Rayon 是一个不错的选择,但它也有一个缺点,那就是它非常占用 CPU。
Bevy(以及许多其他使用 rayon 的 Rust 游戏引擎和 ecs 框架)收到了有关它们过度占用 CPU/使用情况与完成的“实际”工作不成比例的反馈。
我们决定通过构建一个自定义的异步友好任务系统来解决这个问题,该系统支持创建特定于上下文的任务池。例如,您可能为计算、IO、网络等拥有单独的池。这也使我们能够根据工作类型和/或优先级灵活地适当地负载平衡工作。CPU 使用率的提高非常显著
总合并 CPU 使用率百分比 - 8 核机器(越小越好) #
总合并 CPU 使用率百分比 - 32 核机器(越小越好) #
初步的 Web 平台支持 #
(一部分)Bevy 现在使用 WebAssembly/WASM 在 Web 上运行!具体来说,Bevy 应用程序可以运行 Bevy ECS 调度、响应输入事件、创建空画布(使用 winit),以及其他一些事情。这是一个巨大的第一步,但重要的是要指出,仍然缺少许多部分,例如 2D/3D 渲染、多线程和声音。
这些限制并没有阻止 @mrk-its 构建第一个 WASM Bevy 游戏!
bevy-robbo (可在此处玩) #
他们使用 Bevy 进行游戏逻辑,并巧妙地绕过了渲染限制,将 ASCII 艺术游戏状态从此 Bevy 系统传递到此 JavaScript 函数。
您可以按照此处的说明尝试一些 Bevy WASM 示例。
并行查询 #
Bevy ECS 查询是检索实体组件系统数据的灵活方法。使用查询的系统已经并行运行,但在更改之前,查询本身无法并行迭代。Bevy 0.2 添加了轻松并行迭代查询的能力
fn system(pool: Res<ComputeTaskPool>, mut query: Query<&mut Transform>) {
query.iter().par_iter(32).for_each(&pool, |mut transform| {
transform.translate(Vec3::new(1.0, 0.0, 0.0));
});
}
这提供了一个不错的函数式 API(类似于 Rayon),它运行在新的 bevy_tasks
系统之上。它将查询分解成 32 个“批次”,并将每个批次作为 bevy 任务系统中的不同任务运行。
变换系统重写 #
// old
fn system(translation: &Translation, rotation: &Rotation, scale: &Scale) {
println!("{} {} {}", translation.0, rotation.0, scale.0);
}
// new
fn system(transform: &Transform) {
println!("{} {} {}", transform.translation(), transform.rotation(), transform.scale());
}
Bevy 的旧变换系统使用单独的 Translation
、Rotation
和 Scale
组件作为“真理来源”。用户在其系统中使用这些组件进行修改,然后将它们同步到 LocalTransform
组件,该组件又会考虑到层次结构,同步到全局 Transform
组件。这在以下几个方面是不错的
- 检索
Translation
等单个组件时,缓存效率略高(因为需要访问的数据更少) - 理论上更易于并行化。仅访问
Translation
的系统不会阻塞访问Rotation
的系统。
但是,这种方法也有一些非常严重的缺点
- “单个组件”是真理来源,因此当用户系统运行时,
LocalTransform
已过期。如果需要最新的“完整变换”,则必须通过访问所有三个组件来手动构建它。 - 非常难以理解。用户需要考虑 5 个组件,它们以不同的方式相互交互。
- 将变换设置为特定的矩阵值(例如:
Mat4::look_at()
)非常麻烦,并且除非用户显式禁用组件同步,否则该值会被立即覆盖。
鉴于这些问题,我们决定将单个统一的局部到父级的 Transform
组件作为真理来源,并计算出 GlobalTransform
组件以用于世界空间变换。我们认为这个 API 将更易于使用和理解。Unity 也正在考虑对其 ECS 进行类似的变换重写,并且在Amethyst 论坛主题中对此主题进行了大量讨论。
操纵杆/游戏手柄输入 #
由于使用了gilrs 库,Bevy 输入插件现在对大多数控制器提供了跨平台支持!
fn button_system(gamepads: Res<Vec<Gamepad>>, button_input: Res<Input<GamepadButton>>) {
for gamepad in gamepads.iter() {
if button_input.just_pressed(GamepadButton(*gamepad, GamepadButtonType::RightTrigger)) {
println!("Pressed right trigger!");
}
}
}
Bevy ECS 性能改进 #
世代实体 ID #
我们将实体 ID 从随机 UUID 更改为递增的世代索引。随机 UUID 很好,因为它们可以在任何地方创建,在游戏运行期间是唯一的,并且可以安全地持久保存到文件或跨网络重用。我真的很希望我们能让它们发挥作用,但它们最终比其他方法慢太多。随机性会带来可衡量的成本,并且必须使用哈希表查找实体位置。
通过迁移到世代索引(我们使用 hecs 实现),我们可以直接使用实体 ID 作为数组索引,这使得实体位置查找非常快。
只读查询 #
我为不会修改任何内容的查询实现了“只读”特性。这使我们能够保证查询不会修改任何内容。
从 World API 中删除锁定 #
这让我们获得了非常不错的速度提升。由于结合了新的“只读查询”并将 World 修改 API 更改为可变的 World 借用,我们可以安全地做到这一点。
这尚未在系统中的 Queries
中启用,因为一个系统可能有多个 Queries
,这些 Queries
可以以一种不使可变访问唯一的方式同时访问。我认为这是一个可以解决的问题,但它需要更多工作。幸运的是,“for-each”系统没有任何碰撞风险,因此我们现在在其中使用了无锁查询。
直接组件查找(以纳秒计,越小越好) #
由于这些优化,直接组件查找比以前快得多
请注意,此基准测试使用了 world.get::<T>(entity)
。query.get::<T>(entity)
应该具有类似于 hecs
结果的结果,因为它仍然使用锁。最终我希望我们也能从系统查询中删除锁。
变更日志 #
新增 #
- Bevy 的任务系统
- 用自定义设计的任务系统替换 rayon,该系统由多个“TaskPools”组成。
- 在
bevy_tasks
crate 中导出IOTaskPool
、ComputePool
和AsyncComputePool
。
- 用于通过
ParallelIterator
特性在多个任务之间分配工作的并行查询。- 例如:
query.iter().par_iter(batch_size).for_each(/* ... */)
- 例如:
- 使用 Gilrs 添加了游戏手柄支持
- 为 bevy_winit 实现 WASM 支持
- 在 WebAssembly 下创建 winit 画布
- 为 WebAssembly 实现单线程任务调度程序
- 支持二进制 glTF(.glb)。
- 支持 ECS 查询中的
Or
。 - 在
SceneSpawner
上添加了unload()
和unload_sync()
方法,用于卸载场景。. - 自定义 rodio 源用于音频。
AudioOuput
现在可以播放任何Decodable
内容。
Color::hex
用于从字符串十六进制值创建Color
。- 接受 RGB、RGBA、RRGGBB 和 RRGGBBAA 格式。
Color::rgb_u8
和Color::rgba_u8
。- 在 prelude 中添加了
bevy_render::pass::ClearColor
。 SpriteResizeMode
可以选择如何处理Sprite
调整大小。默认情况下为Automatic
。- 在
Input<T>
上添加了方法,用于迭代访问键。get_pressed()
、get_just_pressed()
、get_just_released()
- 为
MouseScrollUnit
派生Copy
。 - 为 UI 组件包派生
Clone
。 - 一些文档示例
- 更新了已更新、已更改和已修改的文档
- 在 macOS 上更快构建的技巧:#312、#314、#433
- 添加和记录了 cargo 功能
- 为 Linux 依赖项添加了更多说明
- 提供 shell.nix 以更轻松地使用 nix-shell 编译
- 添加
AppBuilder::add_startup_stage_|before/after
更改 #
- 变换重写
- 使用代际实体 ID 和其他优化
- 优化转换系统,使其仅在发生更改时运行。
- 使用 `get_id_mut` 修改时发送 AssetEvent
- 重命名 `Assets::get_id_mut` -> `Assets::get_with_id_mut`
- 支持 `DrawableText` 中的多行文本
- iOS:使用 shaderc-rs 将 glsl 编译为 spirv
- 将默认节点大小更改为自动,而不是未定义,以匹配 Stretch 的实现。
- 直接加载时从根路径加载资产
- 添加 `render` 功能,这使得整个渲染管道可选。
修复了 #
- 在 RenderResourcesNode 中正确跟踪添加和删除的 RenderResources。
- 修复了当新的实体被生成/销毁时,实体消失或颜色改变的问题。
- 修复了相同深度下精灵的裁剪问题
- 透明精灵不再裁剪。
- 检查资产路径是否存在
- 修复了热资产重新加载中的死锁问题
- 修复了 Windows 上的热资产重新加载问题
- 允许加载没有 uv 和法线的 glTF
- 修复了为系统错误更新 archetypes_generation 的问题
- 当子实体被销毁时,将其从父实体中删除
- 在运行应用程序时初始化 App.schedule 系统
- 修复了同步加载时缺少资产信息路径的问题
- 修复字体图集溢出
- 不要假设字体句柄存在于资产中
内部改进 #
Bevy CI 的许多改进:#325、#349、#357、#373、#423.
贡献者 #
非常感谢 **87 位贡献者** 使这次发布(以及相关的文档)成为可能!
- 0x22fe
- 8bit-pudding
- aarongeorge
- ablakey
- aclysma
- adekau
- aevyrie
- AmionSky
- andreheringer
- AngelOnFira
- ashneverdawn
- BafDyce
- BimDav
- bitshifter
- Bobox214
- Boiethios
- caelunshun
- cart
- CleanCut
- dallenng
- DGriffin91
- Dispersia
- DJMcNab
- eliaspekkala
- EllenNyan
- eXodiquas
- figsoda
- Fishrock123
- FSMaxB
- GabLotus
- GrantMoyer
- guimcaballero
- Halfwhit
- hannobraun
- IceSentry
- ifletsomeclaire
- Incipium
- io12
- jakerr
- jamadazi
- joejoepie
- JohnDoneth
- julhe
- kaflu
- karroffel
- lachlansneff
- lberrymage
- logannc
- Lowentwickler
- MarekLg
- MatteoGgl
- memoryruins
- mfrancis107
- MGlolenstine
- MichaelHills
- MilanVasko
- Moxinilian
- mrk-its
- mtsr
- multun
- naithar
- ncallaway
- ndarilek
- OptimisticPeach
- PrototypeNM1
- reidbhuntley
- RobDavenport
- saicu
- simpuid
- SmiteWindows
- smokku
- StarArawn
- stefee
- tarkah
- TehPers
- Telzhaak
- TheNeikos
- thirdsgames
- TomBebb
- tristanpemble
- verzuz
- VitalyAnkh
- w1th0utnam3
- Waridley
- wyhaya
- Xavientois
- zicklag