Bevy 0.7

发布于 2022 年 4 月 15 日 由 Carter Anderson ( 一只戴着猫耳、挥舞着触手的卡通人物,Octocat:GitHub 的吉祥物和标志 @cart 一只灰色的鸟飞翔的矢量图;X(原 Twitter)的旧标志 @cart_cart 一个指向右边的三角形在一个圆角矩形里;Youtube 的标志 cartdev )

感谢 **123** 位贡献者,**349** 次拉取请求,以及我们的 慷慨的赞助商,我很高兴地宣布在 crates.io 上发布 **Bevy 0.7**!

对于那些不了解 Bevy 的人来说,Bevy 是一个用 Rust 构建的,令人耳目一新的简单、数据驱动的游戏引擎。你可以查看 快速入门指南 开始使用。Bevy 也是永远免费和开源的!你可以在 GitHub 上获取完整的 源代码。查看 Bevy 资产,获取社区开发的插件、游戏和学习资源的集合。

要将现有的 Bevy 应用或插件更新到 **Bevy 0.7**,请查看我们的 0.6 到 0.7 迁移指南

与往常一样,此版本中包含大量的新功能、错误修复和易用性调整,但以下是一些亮点。

  • 骨骼动画和网格蒙皮
  • GLTF 动画导入
  • 场景中的无限* 点光源
  • 改进的聚簇前向渲染:动态/自适应聚簇,以及更快、更准确的簇分配
  • 压缩纹理支持(KTX2 / DDS / .basis):在场景中加载更多纹理,速度更快
  • 计算着色器/管道专业化:Bevy 的灵活着色器系统移植到计算着色器,使热重载、着色器定义和着色器导入成为可能
  • 渲染到纹理:相机现在可以配置为渲染到纹理而不是窗口
  • 着色器中的灵活网格顶点布局
  • ECS 改进:使用其名称排序系统、Query::many_mut、通过 ParamSets 在系统中使用冲突参数、WorldQuery 推导
  • 文档改进:更好的示例、更多文档测试和更广泛的覆盖范围
  • 更多音频控制:暂停、音量、速度和循环
  • 电源使用选项,仅在发生输入时更新 Bevy 应用

骨骼动画 #

作者:@james7132、@mockersf、@lassade、@Looooong

Bevy 终于支持 3D 骨骼动画!

场景版权:七夕之夜 - 京都风格城市场景 由 Mathias Tossens 创作,根据 知识共享署名 许可。角色模型和动画来自 Mixamo,免版税。

骨骼动画现在可以使用新的 AnimationPlayer 组件和 AnimationClip 资产进行播放、暂停、擦除、循环、反转和速度控制

#[derive(Component)]
struct Animations {
    dance: Handle<AnimationClip>,
}

fn start_dancing(mut query: Query<(&Animations, &mut AnimationPlayer)>) {
    for (animations, mut animation_player) in query.iter_mut() {
        animation_player.play(animations.dance.clone());
    }
}

AnimationPlayer 也可以用来动画任意 Transform 组件,而不仅仅是骨骼!

这个关键功能已经酝酿已久,但我们希望以一种与 新的 Bevy 渲染器 完美融合的方式来构建它,而不是仅仅“把东西硬塞进去”。这是基于我们新的 灵活的网格顶点布局着色器导入材质 系统,确保这种逻辑是灵活且可重用的,即使使用非标准网格和自定义渲染管道也是如此。

而这仅仅是开始!多轨动画混合和更高级的动画状态管理应该会在不久的将来到来。现在是开始为 Bevy 贡献动画功能的最佳时机。我们已经突破了大多数基础技术障碍,剩下的主要是高级 API 设计选择。我们已经在这些领域中开辟了几个草案 RFC:动画合成动画基元。欢迎加入讨论!

GLTF 动画导入 #

作者:@mockersf

Bevy 的 GLTF 导入器已经扩展到将 GLTF 动画导入到新的 AnimationPlayer 系统中。这同时支持“骨骼动画”和任意变换动画

struct FoxAnimations {
    walk: Handle<AnimationClip>,
}

fn setup(mut commands: Commands) {
    commands.spawn_scene(asset_server.load("models/animated/Fox.glb#Scene0"));
    commands.insert_resource(FoxAnimations {
        walk: asset_server.load("models/animated/Fox.glb#Animation0"),
    });
}

fn play_on_load(
    animations: Res<FoxAnimations>,
    mut players: Query<&mut AnimationPlayer, Added<AnimationPlayer>>,
) {
    for mut player in players.iter_mut() {
        player.play(animations.walk.clone()).repeat();
    }
}

无限* 点光源 #

作者:Rob Swain (@superdump)、@robtfm

Bevy 现在可以渲染具有任意数量点光源的场景,前提是平台支持存储缓冲区(基本上除了 WebGL 之外所有平台都支持)。在之前的 Bevy 版本(0.6)中,我们添加了 聚簇前向渲染,这是一种渲染技术,通过将光源分配到可见体积的子体积(称为“簇”)来优化每个片段的光源计算成本。然而,出于平台兼容性的考虑(WebGL),我们最初将自己限制在 256 个光源,因为这是 uniform 缓冲区绑定中可以容纳的数量。

在 **Bevy 0.7** 中,我们添加了在支持它们的平台上自动“升级”到使用无界存储缓冲区进行聚簇前向渲染的功能,从而实现了无限* 个点光源。这里有一个星号,因为实际上这是由内存和硬件限制所决定的。

光簇功能和优化 #

作者:Rob Swain (@superdump)、@robtfm、@dataphract、@cart

随着 256 个点光源的上限被移除,光源的唯一限制是硬件可以支持的范围以及我们算法中的瓶颈。为了增加光源的数量,我们对我们的聚簇算法进行了一系列优化。

  • 动态光簇
    • 默认情况下,簇 x/y 切片现在根据场景中的光源进行动态配置,这可以在某些场景中显著提高性能。
    • 聚簇行为现在也是用户可配置的,可以是 FixedZ(新的默认动态 x/y 行为,固定 z 切片的数量)、自定义的固定 x/y/z 切片值、单簇和“无簇”,让你在知道某种簇配置可以更好地执行时进行控制。
    • 此外,在 0.6 中,所有簇覆盖的可见体积基本上与视锥体的完整可见体积相匹配。这意味着如果所有点光源都在前景,那么光源之外的所有簇都是浪费的空间。在 0.7 中,可以将远边界限制为比相机远边界更近,这意味着光源可以分布在更多簇中,这可以显著提高渲染性能。
  • 迭代球体细化:Bevy 现在使用 Just Cause 3 迭代球体细化方法进行簇分配,这使得我们在某些基准测试中获得了约 10% 的性能提升,并且实现了更准确的聚簇(这也可能提高渲染性能)。
  • 光源视锥体变化检测:我们现在使用 Bevy ECS 的变化检测功能,只重新计算已更改的光源的视锥体。
  • 簇分配优化:簇分配数据访问模式和数据结构进行了一系列调整,从而提高了性能。

以下视频展示了从旧的 256 个点光源限制到 25,000 个点光源(以 60fps 运行)的演变过程!

(请注意,25,000 个光源示例禁用了调试光源球体,以确保光源计算是瓶颈)

我们还有 更多聚簇优化正在进行中!

可配置的光源可见性 #

作者:@robtfm

光源现在可以使用 Bevy 的标准 Visibility 组件打开和关闭。

commands.spawn(PointLightBundle {
    visibility: Visibility {
        is_visible: false,
    },
    ..default()
});

压缩 GPU 纹理 #

作者:Rob Swain (@superdump)

随着场景变得越来越大,其资产也随之增多。压缩这些资产是节省空间的好方法。下面展示的亚马逊餐厅场景中包含超过 1GB 的压缩纹理。

PNG 是一种流行的压缩格式,但它必须先解压缩才能被 GPU 使用。对于大型场景来说,这可能是一个缓慢的过程。然后,这些纹理以其未压缩的形式使用,占用大量有限的内存。压缩 GPU 纹理可以直接以其压缩形式被 GPU 使用,并且可以加载而无需任何额外的处理。这显著减少了加载时间。由于它们保持压缩状态,这也显著减少了 RAM 使用量。

餐厅场景使用 PNG 纹理总共需要 12.9 秒加载,但使用压缩纹理仅需 1.5 秒,大约是加载时间的十分之一!使用未压缩纹理的总 RAM 使用量约为 12GB,使用压缩纹理的 RAM 使用量为 5GB,不到一半!

此外,优势不止于此 - 由于纹理被压缩,并且 GPU 可以以这种格式使用它们,因此读取它们会使用更少的内存带宽,这可以带来性能优势。餐厅场景从使用压缩纹理中获得了大约 10% 的帧率提升。

bistro compressed

另一个好处是支持 mipmap,这使得纹理更平滑,噪点更少。Bevy 目前不支持自动为未压缩纹理生成 mipmap,因此使用压缩纹理是在现在使用 mipmap 的一种好方法!

总之,Bevy 现在支持从 .dds.ktx2.basis 文件加载压缩纹理。这包括对标准 ASTC、BCn 和 ETC2 格式的支持,以及像 ETC1S 和 UASTC 这样的“通用”格式,这些格式可以在运行时被转码为特定系统支持的标准格式。glTF 加载器也得到了扩展,以支持加载这些格式。

这些功能可以使用 ddsktx2basis-universal Cargo 特性启用。

渲染到纹理 #

作者:@HackerFoo

Bevy 现在通过配置 Camera 上的 render_target 字段,对渲染到纹理提供了初步支持。这使镜面反射、分屏、3D 空间中的 2D UI、传送门等场景成为可能。

请注意,当前的实现比较底层。它通常需要与 Bevy 的渲染图交互并定义新的相机类型。如果您想现在使用此功能,render_to_texture 示例 说明了所需的步骤。我们计划为 "高级渲染目标" 提供支持,这将使在几行代码内就可以将渲染结果输出到纹理。敬请期待更新!

Bevy 原生计算着色器 #

作者:@Ku95

Bevy 灵活的基于资产的着色器系统已移植到计算着色器/管道,支持热重载,着色器定义着色器导入管道专门化(基于用户可配置的键)。

#import "shaders/game_of_life_texture_bind_group.wgsl"

[[stage(compute), workgroup_size(8, 8, 1)]]
fn game_of_life_update([[builtin(global_invocation_id)]] invocation_id: vec3<u32>) {
    let location = vec2<i32>(i32(invocation_id.x), i32(invocation_id.y));
    let alive = is_location_alive(location);

    // shader defs are configurable at runtime, prompting new variants of the shader to be compiled
#ifdef WRITE_OUTPUT
    storageBarrier();
    textureStore(texture, location, vec4<f32>(f32(alive)));
#endif
}

灵活的网格顶点布局 #

作者:@cart,@parasyte

在 **Bevy 0.7** 中,现在可以轻松地使着色器支持任何网格顶点布局和任意顶点属性。Bevy 的“着色器管道专门化”系统已扩展以支持“根据网格顶点布局专门化”。

对于大多数 Bevy 用户来说,这意味着 材质(包括内置的 StandardMaterial 和自定义着色器材质)现在自动支持任意网格,前提是这些网格具有材质着色器所需的顶点属性。这也意味着,如果您的网格缺少材质所需的任何属性,则渲染可能会正常失败。

我们还利用此系统为我们新的 骨骼动画 实现实现了关节权重和索引。

对于喜欢编写底层图形管道的 Bevy 用户来说,此功能使您能够根据网格顶点布局轻松高效地专门化您的管道。

impl SpecializedMeshPipeline for SomeCustomPipeline {
    type Key = SomeCustomKey;

    fn specialize(
        &self,
        key: Self::Key,
        layout: &MeshVertexBufferLayout,
    ) -> Result<RenderPipelineDescriptor, SpecializedMeshPipelineError> {
        // this is a layout that matches the requirements requested,
        // but catered to whatever mesh is currently being rendered
        let vertex_buffer_layout = layout.get_layout(&[
            Mesh::ATTRIBUTE_POSITION.at_shader_location(0),
            Mesh::ATTRIBUTE_NORMAL.at_shader_location(1),
            Mesh::ATTRIBUTE_UV_0.at_shader_location(2),
        ])?;

        Ok(RenderPipelineDescriptor {
            vertex: VertexState {
                buffers: vec![vertex_buffer_layout],
                /* define the rest of the vertex state here */
            },
            /* define the rest of the mesh pipeline here */
        })
    }

相机标记组件 #

作者:@jakobhellermann

在 **Bevy 0.7** 中,相机现在使用“标记组件”模式来确定“相机类型”(例如:3D、2D、UI),而不是使用字符串名称。

这意味着现在可以更便宜、更轻松地选择特定类型的相机。

fn move_3d_camera_system(transforms: Query<&mut Transform, With<Camera3d>>) {
    for mut camera in transforms.iter_mut() {
        // move camera here
    }
}

符合人体工程学的系统排序 #

作者:@cart,@aevyrie,@alice-i-cecile,@DJMcNab

Bevy 使用“标签”来定义其 ECS 系统在并行运行时的排序约束。在 Bevy 的早期版本中,唯一的方法是定义自定义标签。

#[derive(SystemLabel, Clone, Hash, Debug, PartialEq, Eq)]
struct UpdateVelocity;

app
  .add_system(update_velocity.label(UpdateVelocity))
  .add_system(movement.after(UpdateVelocity))

在 **Bevy 0.7** 中,不再需要手动定义标签。您可以使用函数对系统进行排序,就像您在添加系统时所做的那样!

app
  .add_system(update_velocity)
  .add_system(movement.after(update_velocity))

这是通过使用其 TypeId(标签类型为 SystemTypeIdLabel)自动标记系统来实现的。在内部,排序仍然使用标签。

Bevy ECS 标签系统功能强大,自定义标签仍然存在合理的使用场景(例如,用同一个标签标记多个系统并作为插件作者导出稳定的公共 API)。但大多数常见用例可以利用符合人体工程学的自动标记功能。

默认简写 #

作者:@cart

Bevy 在初始化实体时大量使用了 Rust 的 结构体更新模式,并结合了 Default 特性。这通过允许开发人员只填写他们想要更改的字段来显著减少了所需的输入。

执行此操作的标准方法是写出 ..Default::default()

commands.spawn_bundle(SpriteBundle {
    texture: some_texture,
    ..Default::default()
})

这比手动填写每个字段的组件好得多。

commands.spawn(SpriteBundle {
    texture: some_texture,
    sprite: Default::default(),
    transform: Default::default(),
    global_transform: Default::default(),
    visibility: Default::default(),
});

但是,当您对数十个或数百个实体执行此操作时,这可能会让人感觉重复。我们添加了一种方法来使这变得更加容易,而无需使用宏。

commands.spawn_bundle(SpriteBundle {
    texture: some_texture,
    ..default()
})

这在功能上等同于 ..Default::default(),只是更紧凑。您仍然可以使用更长的形式,如果您更喜欢的话。default() 函数包含在 Bevy 的前导文件中,因此您不需要手动导入它。符合人体工程学大获全胜!

Query::many #

作者:@alice-i-cecile

Bevy ECS 解决了一个难题:在并行情况下提供对数据的简单快捷访问,同时仍然尊重 Rust 的严格可变性和所有权规则。自我们首次发布以来,我们一直支持在 ECS 查询中有效地访问特定实体。

struct SomeEntities {
    a: Entity,
    b: Entity,
}

fn system(mut query: Query<&mut Transform>, entities: Res<SomeEntities>) {
    let a_transform = query.get_mut(entities.a).unwrap();
}

但是,为了尊重 Rust 的可变性规则,我们需要禁止可能产生“别名可变性”的 API。经验丰富的 Bevy 用户可能会认出这个 Rust 借用检查器错误。

fn system(mut query: Query<&mut Transform>, entities: Res<SomeEntities>) {
    let a_transform = query.get_mut(entities.a).unwrap();
    // This line fails to compile because `query` is already mutably borrowed above
    let b_transform = query.get_mut(entities.b).unwrap();
}

知道实体 A 和实体 B 在运行时是不同的实体。但是 Rust 的借用检查器在编译时无法知道这一点!我敢肯定您可以想象出游戏开发场景,这些场景将从同时对多个组件进行可变访问中获益。这个借用检查器限制是一个常见的痛点,解决方法……并不有趣(使用作用域来确保冲突访问被删除、复制数据、重新查询事物,等等)。

幸运的是,**Bevy 0.7** 引入了一套全新的 API 来拯救世界!

fn system(mut query: Query<&mut Transform>, entities: Res<SomeEntities>) {
    // Takes an array of entities and returns an array of mutable Query results
    // This will panic if there are entity conflicts or the entities do not exist
    let [a_transform, b_transform] = query.many_mut([entities.a, entities.b]);
}

有很多变体。

// Same as many_mut, but returns a Result instead of panicking
if let Ok([a_transform, b_transform]) = query.get_many_mut([entities.a, entities.b]) {
}

// There are also immutable/read-only variants
let [a_transform, b_transform] = query.many([entities.a, entities.b]);
if let Ok([a_transform, b_transform]) = query.get_many([entities.a, entities.b]) {
}

它们都支持任意数量的实体。

let [a, b, c] = query.many([entity_a, entity_b, entity_c]);

ParamSets #

作者:@bilsen

为了防止别名可变性,Bevy ECS 不允许具有相互冲突的参数的系统。例如,如果两个查询都请求对同一“原型”中的同一组件进行写入访问,这可能会导致别名可变访问,因此 Bevy 不允许该系统并出错。

Bevy 的早期版本使用 QuerySets 支持同一个系统中的冲突查询,QuerySets 每次只允许访问集合中的一个查询。

// These queries could each return a mutable A component for the same entity, so they must be put in a set to be considered a valid system. 
fn system(mut set: QuerySet<(QueryState<(&mut A, &B)>, QueryState<(&mut A, &C)>)>) {
    for (a, b) in set.q0().iter_mut() {
    }
}

**Bevy 0.7** 使用 ParamSet 替换了 QuerySetParamSet 将 QuerySet 模式推广到任何系统参数。

fn system(mut set: ParamSet<(Query<(&mut A, &B)>, Query<(&mut A, &C)>)>) {
    for (a, b) in set.p0().iter_mut() {
    }
}

但是 ParamSets 不仅限于查询!考虑这个示例,其中 EventWriter<Jump> 参数(它在内部访问 Events<Jump> 资源)与对该资源的原始访问冲突。以前,表达这一点是不可能的。但是使用 ParamSets,就可以做到!

fn system(mut set: ParamSet<(EventWriter<Jump>, ResMut<Events<Jump>>)>) {
    for jump_event in set.p1().drain() {
    }
}

我们仍然建议尽可能避免使用 ParamSets,以保证清晰度。但它们有时是必要的、有用的工具!

Deref / DerefMut 派生 #

作者:@MrGVSV

Rust 鼓励在使用新功能或含义扩展类型时使用 newtype 模式。这在 Bevy 中也是一个有用的工具。

#[derive(Component)]
struct Items(Vec<Item>);

fn give_sword(mut query: Query<&mut Items>) { 
    for mut items in query.iter_mut() {
        items.0.push(Item::new("Flaming Poisoning Raging Sword of Doom"));
    }
}

这工作得很好,但是 items.0 末尾的 0 就像一根刺,让人很不舒服。Bevy 组织中的许多人认为 .0 在公共 API 中没有立足之地。但是 newtype 模式仍然有用!理想情况下,Rust 会提供一种方法来表达 Items 是一个新类型,同时透明地提供对其中存储的 Vec<Item> 的访问。Rust 团队正在讨论设计,但我们不想坐等好消息!

幸运的是,std 中的 Deref / DerefMut 特性提供了我们想要的行为。用户已经可以手动实现这些特性,但是对于如此常见的模式,我们决定提供我们自己的特性派生是值得的。在 **Bevy 0.7** 中,您现在可以派生 Deref 和 DerefMut,从而实现更漂亮的公共 API。

#[derive(Component, Deref, DerefMut)]
struct Items(Vec<Item>);

fn give_sword(mut query: Query<&mut Items>) { 
    for mut items in query.iter_mut() {
        // No more .0!
        items.push(Item::new("Flaming Poisoning Raging Sword of Doom"));
    }
}

敏锐的 std 文档读者可能会注意到,Rust 团队 建议只对智能指针使用 Deref/DerefMut,以避免混淆。像 Items 这样的组件不是智能指针。我们选择忽略这个建议,因为这种模式有效,已经在 Rust 生态系统中被广泛使用,并且良好的用户体验至上。

WorldQuery 派生 #

作者:@mvlabat

有时在构建 Bevy 应用程序时,您可能会发现自己在查询中反复使用相同的组件集。

fn move_players(mut players: Query<(&mut Transform, &mut Velocity, &mut PlayerStats)>) {
    for (transform, velocity, stats) in players.iter_mut() {
    }
}

fn player_gravity(mut players: Query<(Entity, &mut Transform, &mut Velocity, &mut PlayerStats)>) {
    for (entity, transform, velocity, stats) in players.iter_mut() {
    }
}

也许您厌倦了反复输入相同的组件。在 **Bevy 0.7** 中,您现在可以使用 WorldQuery 派生轻松地创建您自己的自定义 WorldQuery 特性实现。

#[derive(WorldQuery)]
#[world_query(mutable)]
struct PlayerMovementQuery<'w> {
    transform: &'w mut Transform,
    velocity: &'w mut Velocity,
    stats: &'w mut PlayerStats,
}

fn move_players(mut players: Query<PlayerMovementQuery>) {
    for player in players.iter_mut() {
    }
}

fn player_gravity(mut players: Query<(Entity, PlayerMovementQuery)>) {
    for (entity, player) in players.iter_mut() {
    }
}

World::resource #

作者:@alice-i-cecile

我们注意到,大多数直接对 World 资源进行的访问立即解包了 get_resource 的结果。

let time = world.get_resource::<Time>().unwrap();

在 **Bevy 0.7** 中,我们添加了一个符合人体工程学的变体,它在内部会产生恐慌。

let time = world.resource::<Time>();

还有一个可变的变体。

let mut time = world.resource_mut::<Time>();

get_resource 变体仍然适用于用户希望手动处理返回的 Option 的情况。

AnyOf 查询 #

作者:@TheRawMeatball

Bevy ECS 查询现在支持 AnyOf,它将返回与给定组件查询中的“任何一个”匹配的实体的结果。

fn system(query: Query<AnyOf<(&A, &B)>>) {
    for (a, b) in query.iter() {
        // Either A or B is guaranteed to be Some
        assert!(a.is_some() || b.is_some())
    }
}

对于上面的示例,AnyOf 将返回具有 A 而不是 B、B 而不是 A 以及 A 和 B 两个的实体。

&World 系统参数 #

作者:@bilsen

现在,“普通系统”可以具有 &World 系统参数,这将提供对整个 World 的完整只读访问。

fn system(world: &World, transforms: Query<&Transform>) {
}

请记住,&World 会与任何可变查询冲突。

This code is invalid
fn invalid_system(world: &World, transforms: Query<&mut Transform>) {
}

在这些情况下,请考虑使用我们新的 ParamSets 来解决冲突。

fn valid_system(set: ParamSet<(&World, Query<&mut Transform>)>) {
}

ECS 健壮性/正确性改进 #

作者:@BoxyUwU,@TheRawMeatball,@bjorn3

Bevy ECS 在此版本中修复了许多健壮性和正确性错误。

  • 删除了 EntityMutQuery 上不安全的生命周期注释,这些注释可能在某些情况下被用来获得别名可变性。
  • World::entities_mut 标记为不安全(因为手动修改实体元数据可能会使安全假设失效)。
  • 删除了不安全的 World::components_mut(它允许替换组件元数据,从而使 World 中其他地方所做的假设失效)。
  • 修复了 World::resource_scope 健壮性错误。
  • 在资源 ID 初始化中使用了 ManuallyDrop 而不是 forget(),以避免在数据指针被使用之前使它失效。

我们现在还在 CI 中对 Bevy ECS 运行 miri 解释器,以帮助检测和防止未来的健壮性/正确性问题。

随着 Bevy ECS 的成熟,我们对不安全代码块和健壮性的要求也必须提高。Bevy ECS 可能永远无法完全摆脱不安全代码块,因为我们正在模拟 Rust 无法在没有我们帮助的情况下进行推理的并行数据访问。但我们致力于尽可能地删除不安全的代码,并提高我们不安全代码的质量和范围。

音频控制 #

作者:@mockersf

Bevy 的音频系统从我们首次发布以来一直处于... 最简状态。到目前为止,它只支持对音频资源按下“播放”。第三方插件,如 bevy_kira_audio 已经用更灵活的音频解决方案填补了空白。

Bevy 0.7 中,我们开始扩展内置音频插件的功能。现在可以使用 AudioSink 资源来暂停、调整音量和设置播放速度。

播放音频现在会返回一个 Handle<AudioSink>,可以用来播放/暂停/设置速度/设置音量

struct BeautifulMusic(Handle<AudioSink>);

fn setup_audio(
    asset_server: Res<AssetServer>,
    audio: Res<Audio>,
    audio_sinks: Res<Assets<AudioSink>>,
) {
    let music = asset_server.load("BeautifulMusic.ogg");
    // play audio and upgrade to a strong handle
    let sink_handle = audio_sinks.get_handle(audio.play(music));
    commands.insert_resource(BeautifulMusic(sink_handle));
}

// later in another system
fn adjust_audio(music: Res<BeautifulMusic>, mut audio_sinks: ResMut<Assets<AudioSink>>) {
    if let Some(sink) = audio_sinks.get(music.0) {
        // pause playback
        sink.pause();
        // start playback again
        sink.play();
        // increase the volume
        sink.set_volume(sink.volume() + 0.1);
        // slow down playback
        sink.set_speed(0.5);
    }
}

现在还可以循环播放音频。

audio.play_with_settings(music, PlaybackSettings::LOOP.with_volume(0.75));

我们计划继续迭代这些 API,加入更多功能和可用性改进!

精灵锚点 #

作者:@mockersf

Sprite 组件现在可以定义一个 Anchor 点(也称为“枢轴”点),它决定了精灵的“原点”。精灵仍然默认使用“中心”原点,但这现在可以配置。

commands.spawn_bundle(SpriteBundle {
    texture: asset_server.load("bevy_logo.png"),
    sprite: Sprite {
        anchor: Anchor::TopRight,
        ..default()
    },
    ..default()
});

事件循环节能模式 #

作者:@aevyrie

默认情况下,Bevy 会“尽可能快地”运行更新(受屏幕刷新率限制)。这对于大多数游戏来说很棒,但某些应用程序类型(如 GUI 应用程序)需要优先考虑 CPU 和 GPU 的功耗。

Bevy 0.7 添加了在 [WinitConfig] 中配置 [UpdateMode] 的功能,以配置 Bevy 应用程序如何运行更新

  • 连续:始终“尽快”更新(尊重 vsync 配置)
  • 响应式:仅在有窗口事件、请求重绘或配置的等待时间已过时更新
  • 响应式低功耗:仅在有用户输入(鼠标移动、键盘输入等)、请求重绘或配置的等待时间已过时更新

这些设置可以分别为焦点窗口和非焦点窗口配置(使您能够在窗口失去焦点时节省功耗)。

响应式低功耗 可以显着降低功耗/资源使用,但它并不适合每种应用程序类型,因为某些应用程序需要假设它们始终以尽可能快的速度更新。因此,这些设置是可选的。

此应用程序演示了可用的各种模式。请注意,游戏模式已配置为在失去焦点时降低其滴答率,这与默认设置不同。

文档改进 #

作者:@alice-i-cecile 和更多人

优秀的文档可以让学习、使用和构建 Bevy 变得更好。但作为一个年轻的引擎,它们仍在不断完善中。

拒绝缺少文档 #

我们的文档团队(由 @alice-i-cecile 领导)已经开始 系统地解决这个问题,借助 Rust 的 #[warn(missing_docs)] lint。从 0.6 版开始,我们已经完整地记录了(并防止了文档回归)

  • bevy_tasks@james7132 提供
  • bevy_app@dbearden 提供
  • bevy_dylib@KDecay 提供
  • bevy_internal@sheepyhead 提供

在此期间,还进行了 许多其他文档改进,包括添加许多有用的 文档测试,我们对新代码中文档的要求也不断提高。衷心感谢所有帮助改进 Bevy 文档的人。

新贡献者 #

如果您 有兴趣贡献代码,文档团队随时准备帮助新贡献者尽快将他们的第一个 Bevy PR 合并。有很多新贡献者帮助完善了文档,无论是作为作者还是审阅者。如果您是其中之一:感谢您!

更好的示例 #

对于许多人来说,学习工具的最佳方法是看看它在实际中的应用。我们一直在不断完善我们的 示例,加入更好的解释、更多覆盖范围和更高的代码质量。如果您是 Bevy 的新手,请查看经过大幅改进的 Breakout 示例

开发者文档 #

作者:@james7132、@mockersf、@aevyrie

我们现在会在每次合并更改时自动将 Bevy 的 main 开发分支部署到 https://dev-docs.bevyengine.org。这将帮助 Bevy 文档作者轻松验证他们的更改。而“最前沿”的 Bevy 用户可以了解我们正在进行的 API 更改。

dev docs

网站改进 #

作者:@doup

Bevy 手册现在拥有一个更漂亮的分页小部件,它会显示上一节/下一节的名称。

pager

我们还添加了一个“改进此页面”页脚链接,使 Bevy 手册读者更容易贡献更改。

侧边栏进行了全面改造,提高了清晰度,并使您无需单击即可打开/关闭部分。

sidebar

网站的响应能力也得到了提高,某些部分在移动设备上的布局更好。

场景查看器工具 #

作者:Rob Swain (@superdump)、@mockersf、@IceSentry、@jakobhellermann

Bevy 现在拥有一个专用的场景查看器工具,可以加载任意 GLTF 场景文件。如果您查看主 Bevy 仓库,可以通过运行以下命令试用它

cargo run --release --example scene_viewer /some/path/castle.gltf

它有一个内置的“飞行相机”,并有工具可以播放动画以及切换灯光和阴影。

scene viewer

支持 Bevy #

赞助有助于让我的 Bevy 全职工作可持续发展。如果您相信 Bevy 的使命,请考虑赞助我 (@cart) ... 每一点帮助都很重要!

捐赠 爱心图标

贡献者 #

衷心感谢 123 位贡献者,他们的贡献使这次发布(以及相关的文档)成为可能!随机顺序

  • @mfdorst
  • @devjobe
  • @PrototypeNM1
  • @devil-ira
  • @IsseW
  • @SuperSamus
  • @alice-i-cecile
  • @bilsen
  • @CooCooCaCha
  • @lufog
  • @chescock
  • @dbearden
  • @doup
  • @MiniaczQ
  • @C-BJ
  • @TedDriggs
  • @idanarye
  • @plof27
  • @robtfm
  • @ickk
  • @rsk700
  • @dataphract
  • @Looooong
  • @mdickopp
  • @bjorn3
  • @aloucks
  • @EpsilonDelphine
  • @rparrett
  • @CptPotato
  • @horvbalint
  • @jakobhellermann
  • @light4
  • @Lindenk
  • @rib
  • @ItsDoot
  • @KDecay
  • @p3rsik
  • @danieleades
  • @parasyte
  • @Sorck
  • @chenzhekl
  • @jch-13
  • @SecretPocketCat
  • @DJMcNab
  • @jak6jak
  • @ShadowCurse
  • @MrGVSV
  • @NiklasEi
  • @ZainlessBrombie
  • @lwansbrough
  • @ramon-bernardo
  • @aevyrie
  • @OptimisticPeach
  • @bitshifter
  • @glfmn
  • @rezural
  • @joshuataylor
  • @nakedible
  • @GarettCooper
  • @rand0m-cloud
  • @kirusfg
  • @oceantume
  • @l4desu-mizu
  • @fgiordana-netflix
  • @sarkahn
  • @Weasy666
  • @bytemuck
  • @james7132
  • @sseemayer
  • @pubrrr
  • @Sliman4
  • @geckoxx
  • @Bobox214
  • @mirkoRainer
  • @Veykril
  • @Gingeh
  • @pocket7878
  • @cart
  • @infmagic2047
  • @MinerSebas
  • @ramirezmike
  • @cjglo
  • @Sheepyhead
  • @L-french
  • @JoJoJet
  • @Wcubed
  • @Shatur
  • @reinismu
  • @boguscoder
  • @tversteeg
  • @TheRawMeatball
  • @mcobzarenco
  • @ezekg
  • @hlb8122
  • @B-Janson
  • @emersonmx
  • @mvlabat
  • @Nilirad
  • @jamesbeilby
  • @ryo33
  • @cdbfoster
  • @blaind
  • @fgiordana
  • @josh65536
  • @CleanCut
  • @tornewuff
  • @Ku95
  • @kcking
  • @luke-biel
  • @SUPERCILEX
  • @BoxyUwU
  • @TheAndrewJackson
  • @HackerFoo
  • @andresovela
  • @IceSentry
  • @RedlineTriad
  • @killercup
  • @Azorlogh
  • @superdump
  • @nebkor
  • @mockersf
  • @Gordon-F
  • @Jupp56

完整变更日志 #

添加了 #

更改了 #

修复了#