迁移指南

迁移指南:0.12 到 0.13

Bevy 严重依赖 Rust 语言和编译器的改进。因此,最低支持的 Rust 版本 (MSRV) 是 Rust 的“最新稳定版本”。

支持来自 gltf 的所有类型的动画插值 #

动画

手动指定动画 VariableCurve 时,必须指定插值类型

// 0.12
VariableCurve {
    keyframe_timestamps: vec![0.0, 1.0, 2.0, 3.0, 4.0],
    keyframes: Keyframes::Rotation(vec![
        Quat::IDENTITY,
        Quat::from_axis_angle(Vec3::Y, PI / 2.),
        Quat::from_axis_angle(Vec3::Y, PI / 2. * 2.),
        Quat::from_axis_angle(Vec3::Y, PI / 2. * 3.),
        Quat::IDENTITY,
    ]),
},

// 0.13
VariableCurve {
    keyframe_timestamps: vec![0.0, 1.0, 2.0, 3.0, 4.0],
    keyframes: Keyframes::Rotation(vec![
        Quat::IDENTITY,
        Quat::from_axis_angle(Vec3::Y, PI / 2.),
        Quat::from_axis_angle(Vec3::Y, PI / 2. * 2.),
        Quat::from_axis_angle(Vec3::Y, PI / 2. * 3.),
        Quat::IDENTITY,
    ]),
    interpolation: Interpolation::Linear,
},

ReadAssetBytesError::Io 公开失败路径 #

资产

ReadAssetBytesError::Io 变体现在包含两个命名字段,而不是从 std::io::Error 转换。

  • path:请求的(失败的)路径(PathBuf
  • source:源 std::io::Error

确保未类型化/类型化 AssetIdHandle 之间的一致性 #

资产

如果您依赖任何崩溃的 From<Untyped...> 实现,只需调用现有的 typed 方法即可。或者,可以使用新的 TryFrom 实现来直接公开可能的错误。

Assets::add 使用 impl Into<A> #

资产

由于新的特性边界,之前有效的某些 into 调用现在可能已损坏。您需要删除 into 或使用 from 显式执行转换

// 0.12
let mesh_handle = meshes.add(shape::Cube { size: 1.0 }.into()),

// 0.13
let mesh_handle = meshes.add(shape::Cube { size: 1.0 }),
let mesh_handle = meshes.add(Mesh::from(shape::Cube { size: 1.0 })),

GLTF 扩展支持 #

资产

这将导致“资产迁移”问题,因为目前没有方法可以迁移 .meta 文件。尝试在没有新标志的情况下迁移 .meta 文件将导致以下错误

bevy_asset::server: Failed to deserialize meta for asset test_platform.gltf: Failed to deserialize asset meta: SpannedError { code: MissingStructField { field: "include_source", outer: Some("GltfLoaderSettings") }, position: Position { line: 9, col: 9 } }

这意味着想要迁移 .meta 文件的用户必须手动将其 .meta 文件中添加 include_source: true, 设置。

允许 AssetLoader 中的 TextureAtlasBuilder #

资产
  • 对于 add_texture,您需要将您的 AssetId 包裹在 Some
  • finish 现在直接返回图集纹理图像,而不是句柄。将图集纹理提供给 Assets 上的 add以获取 Handle

    删除忽略全局音量的能力 #

    音频

    使用 Volume::Absolute 忽略全局音量的选项已被删除,Volume 现在直接存储音量级别,从而无需 VolumeLevel 结构。

    Volume::new_absoluteVolume::new_relative 已被删除。使用 Volume::new(0.5)

    全局空间比例的可选覆盖 #

    音频

    AudioPlugin::spatial_scale 已被重命名为 default_spatial_scale,并且现在可以使用 PlaybackSettings::spatial_scale 在各个音频源上覆盖默认空间比例。

    如果您在运行时修改或读取 SpatialScale,请改用 DefaultSpatialScale

    // 0.12
    app.add_plugins(DefaultPlugins.set(AudioPlugin {
        spatial_scale: SpatialScale::new(AUDIO_SCALE),
        ..default()
    }));
    
    // 0.13
    app.add_plugins(DefaultPlugins.set(AudioPlugin {
        default_spatial_scale: SpatialScale::new(AUDIO_SCALE),
        ..default()
    }));
    

    添加对 LogPlugin 中更新跟踪订阅者的支持 #

    诊断

    LogPlugin 有一个新的可选 update_subscriber 字段。使用 None..default() 以匹配之前的行为。

    DiagnosticPath 替换 DiagnosticId #

    诊断
    - const UNIQUE_DIAG_ID: DiagnosticId = DiagnosticId::from_u128(42);
    + const UNIQUE_DIAG_PATH: DiagnosticPath = DiagnosticPath::const_new("foo/bar");
    
    - Diagnostic::new(UNIQUE_DIAG_ID, "example", 10)
    + Diagnostic::new(UNIQUE_DIAG_PATH).with_max_history_length(10)
    
    - diagnostics.add_measurement(UNIQUE_DIAG_ID, || 42);
    + diagnostics.add_measurement(&UNIQUE_DIAG_ID, || 42);
    

    EntityMapper 使用 EntityHashMap #

    ECS

    如果您正在使用以下类型,请更新其列出的方法以使用新的 EntityHashMapEntityHashMap 与正常的 HashMap 具有相同的方法,因此您只需要替换名称即可。

    EntityMapper

    • get_map
    • get_mut_map
    • new
    • world_scope

    ReflectMapEntities

    • map_all_entities
    • map_entities
    • write_to_world

    InstanceInfo

    • entity_map
      • 这是一个属性,而不是方法。

    更新 Event 发送方法以返回 EventId #

    ECS

    send / send_default / send_batch

    对于以下方法

    • Events::send
    • Events::send_default
    • Events::send_batch
    • EventWriter::send
    • EventWriter::send_default
    • EventWriter::send_batch
    • World::send_event
    • World::send_event_default
    • World::send_event_batch

    确保对这些方法的调用要么处理返回值,要么使用 ; 抑制结果。

    // 0.12
    fn send_my_event(mut events: EventWriter<MyEvent>) {
        events.send_default()
    }
    
    // 0.13
    fn send_my_event(mut events: EventWriter<MyEvent>) {
        events.send_default();
    }
    

    这很可能在 match 语句中被注意到

    // 0.12
    match is_pressed {
        true => events.send(PlayerAction::Fire),
    //                 ^--^ No longer returns ()
        false => {}
    }
    
    // 0.13
    match is_pressed {
        true => {
            events.send(PlayerAction::Fire);
        },
        false => {}
    }
    

    使用 repr align 和手动 PartialOrd/Ord 优化 Entity #

    ECS

    任何依赖 Entity 字段顺序或足够邪恶的把戏的 unsafe 代码都应该更改以反映 Entity 的不同内部表示和对齐要求。

    将 WorldQuery 拆分为 QueryData 和 QueryFilter #

    ECS

    查看 #9918#10799 以了解更多信息。

    • 重命名以下特性类型用法
      • 特性的 ExtractComponent 类型 QueryData
      • 特性的 GetBatchData 类型 QueryData
      • 特性的 ExtractInstance 类型 QueryData
    • ReadOnlyWorldQuery 重命名为 QueryFilter,将 WorldQuery 重命名为 QueryData
    • 您需要更新相关的派生
    // 0.12
    #[derive(WorldQuery)]
    #[world_query(mutable, derive(Debug))]
    struct CustomQuery {
        entity: Entity,
        a: &'static mut ComponentA
    }
    
    #[derive(WorldQuery)]
    struct QueryFilter {
        _c: With<ComponentC>
    }
    
    // 0.13
    #[derive(QueryData)]
    #[query_data(mutable, derive(Debug))]
    struct CustomQuery {
        entity: Entity,
        a: &'static mut ComponentA,
    }
    
    #[derive(QueryFilter)]
    struct QueryFilter {
        _c: With<ComponentC>
    }
    
    • Option<With<T>> 替换为 Has<T>
    // 0.12
    fn my_system(query: Query<(Entity, Option<With<ComponentA>>)>) {
      for (entity, has_a_option) in query.iter(){
        let has_a:bool = has_a_option.is_some();
        //todo!()
      }
    }
    
    // 0.13
    fn my_system(query: Query<(Entity, Has<ComponentA>)>) {
      for (entity, has_a) in query.iter(){
        //todo!()
      }
    }
    
    • 修复在数据位置或反之亦然的过滤器查询。
    // 0.12
    fn my_system(query: Query<(Entity, With<ComponentA>)>) {
      for (entity, _) in query.iter(){
      //todo!()
      }
    }
    
    // 0.13
    fn my_system(query: Query<Entity, With<ComponentA>>) {
      for entity in query.iter(){
      //todo!()
      }
    }
    
    // 0.12
    fn my_system(query: Query<AnyOf<(&ComponentA, With<ComponentB>)>>) {
      for (entity, _) in query.iter(){
      //todo!()
      }
    }
    
    // 0.13
    fn my_system(query: Query<Option<&ComponentA>, Or<(With<ComponentA>, With<ComponentB>)>>) {
      for entity in query.iter(){
      //todo!()
      }
    }
    
    

    减少 TableRow as 转换 #

    ECS
    • TableRow::new -> TableRow::from_usize
    • TableRow::index -> TableRow::as_usize
    • TableId::new -> TableId::from_usize
    • TableId::index -> TableId::as_usize

    允许编辑启动计划 #

    ECS
    • MainScheduleOrder 中添加了一个新字段 startup_labels,用于编辑启动计划顺序。

    自动插入同步点 #

    ECS
    • 当与具有延迟参数(如 Commands)的系统存在排序关系时,会自动添加 apply_deferred 点。如果您想选择退出,可以将 afterbeforechain 切换到相应的 ignore_deferred API,after_ignore_deferredbefore_ignore_deferredchain_ignore_deferred,以用于您的系统/集合排序。
    • 如果您想对整个计划执行此操作,还可以将 ScheduleBuildSettings::auto_insert_sync_points 设置为 false。请注意,在此模式下,您仍然可以手动添加 apply_deferred 点。
    • 对于大多数 apply_deferred 的手动插入,您应该将其删除,因为它们不能与自动插入的点合并,并且可能会降低系统图的并行性。
    • 如果您手动派生 SystemParam,则需要在使用 SystemParam::apply 且希望在使用 SystemParam 后自动插入同步点时添加 system_meta.set_has_deferred

    向 App 添加 insert_state。 #

    ECS

    App::add_state 重命名为 init_state

    ArchetypeEntity::entity 重命名为 ArchetypeEntity::id #

    ECS

    方法 ArchetypeEntity::entity 已重命名为 ArchetypeEntity::id

    恢复对在可能已被销毁的实体上运行 fn EntityCommands 的支持 #

    ECS

    bevy_ecs 中的所有 Command 类型,例如 SpawnSpawnBatchInsert 等,都已设为私有。请改用 CommandsEntityCommands 上的等效方法。

    如果您正在使用 ChildBuilder,请使用闭包重新创建这些命令。例如,您可以迁移一个命令来插入组件,例如

    // 0.12
    parent.add_command(Insert {
        entity: ent_text,
        bundle: Capitalizable,
    });
    
    // 0.13
    parent.add_command(move |world: &mut World| {
        world.entity_mut(ent_text).insert(Capitalizable);
    });
    

    简化条件 #

    ECS

    一些以前是闭包并且需要被调用的常见运行条件现在只是系统了。请删除括号。

    • resource_exists<T>() -> resource_exists<T>
    • resource_added<T>() -> resource_added<T>
    • resource_changed<T>() -> resource_changed<T>
    • resource_exists_and_changed<T>() -> resource_exists_and_changed<T>
    • state_exists<S: States>() -> state_exists<S: States>
    • state_changed<S: States>() -> state_changed<S: States>
    • any_with_component<T: Component>() -> any_with_component<T: Component>
    ECS

    EntityCommands 的生命周期已简化。

    // 0.12 (Bevy 0.12)
    struct MyStruct<'w, 's, 'a> {
         commands: EntityCommands<'w, 's, 'a>,
    }
    
    // 0.13 (Bevy 0.13)
    struct MyStruct<'a> {
        commands: EntityCommands<'a>,
    }
    

    方法 EntityCommands::commands 现在返回 Commands 而不是 &mut Commands

    // 0.12 (Bevy 0.12)
    let commands = entity_commands.commands();
    commands.spawn(...);
    
    // 0.13 (Bevy 0.13)
    let mut commands = entity_commands.commands();
    commands.spawn(...);
    

    弃用 QueryQueryState 中的各种组件方法 #

    ECS

    QueryState::get_component_unchecked_mut

    请使用 QueryState::get_unchecked_manual 并根据所需精确查询的结构选择精确的组件。

    Query::(get_)component(_unchecked)(_mut)

    请使用 Query::get 并根据所需精确查询的结构选择精确的组件。

    • 对于可变访问(_mut),请使用 Query::get_mut
    • 对于未经检查的访问(_unchecked),请使用 Query::get_unchecked
    • 对于 panic 变体(非 get_),请添加 .unwrap()

    例如

    fn system(query: Query<(&A, &B, &C)>) {
        // 0.12
        let b = query.get_component::<B>(entity).unwrap();
    
        // Alternative 1 (using tuple destructuring)
        let (_, b, _) = query.get(entity).unwrap();
    
        // Alternative 2 (using tuple item indexing)
        let b = query.get(entity).unwrap().1;
    }
    

    System::type_id 一致性 #

    ECS

    如果您在函数系统(独占或非独占)上使用 System::type_id(),请确保将它的值与其他 System::type_id() 调用或 IntoSystem::system_type_id() 进行比较。

    此代码不需要任何更改,因为 IntoSystem 会直接相互比较。

    fn test_system() {}
    
    let type_id = test_system.type_id();
    
    // ...
    
    // No change required
    assert_eq!(test_system.type_id(), type_id);
    

    同样,此代码也不需要,因为 System 会直接相互比较。

    fn test_system() {}
    
    let type_id = IntoSystem::into_system(test_system).type_id();
    
    // ...
    
    // No change required
    assert_eq!(IntoSystem::into_system(test_system).type_id(), type_id);
    

    下面的代码 _确实_ 需要更改,因为您正在将 System 类型与 IntoSystem 类型进行比较。

    fn test_system() {}
    
    // 0.12
    assert_eq!(test_system.type_id(), IntoSystem::into_system(test_system).type_id());
    
    // 0.13
    assert_eq!(test_system.system_type_id(), IntoSystem::into_system(test_system).type_id());
    

    系统步进作为资源实现 #

    ECS
    编辑器
    App
    诊断

    对于任何自定义 Schedule,请添加对 Schedule::set_label() 的调用。这仅在 Schedule 将被步进时才需要。

    使 MapEntities 特性对 Mappers 进行泛型化,并添加一个更简单的 EntityMapper #

    ECS
    场景
    • 现有的 EntityMapper(特别用于在不同的 World 之间复制 Scenes)已重命名为 SceneEntityMapper
    • MapEntities 特性现在使用泛型 EntityMapper 而不是特定结构 EntityMapper。对 fn map_entities(&mut self, entity_mapper: &mut EntityMapper) 的调用需要更新为 fn map_entities<M: EntityMapper>(&mut self, entity_mapper: &mut M)
    • 新的特性 EntityMapper 已添加到 prelude 中

    异步通道 v2 #

    ECS
    任务

    PipelinedRendering 插件不再在 wasm 上导出。如果您在 wasm 构建中包含它,则应将其删除。

    #[cfg(not(target_arch = "wasm32"))]
    app.add_plugins(bevy_render::pipelined_rendering::PipelinedRenderingPlugin);
    

    向固定时间步长添加 First/Pre/Post/Last 调度程序 #

    ECS
    时间

    RunFixedUpdateLoop 的使用应重命名为 RunFixedMainLoop

    ECS
    实用程序
    • bevy::utils::{EntityHash, EntityHasher, EntityHashMap, EntityHashSet} 的使用现在需要从 bevy::ecs::entity::hash 中导入。
    • EntityHashMap 的使用不再需要指定第一个泛型参数。它现在被硬编码为始终为 Entity

    将圆形 Gizmo 移至其自己的文件 #

    Gizmo
    • gizmos::CircleBuilder 更改为 gizmos::circles::Circle2dBuilder
    • gizmos::Circle2dBuilder 更改为 gizmos::circles::Circle2dBuilder

    将 Gizmo 弧移至其自己的文件 #

    Gizmo

    gizmos::Arc2dBuilder -> gizmos::arcs::Arc2dBuilder

    Gizmo 的多个配置 #

    Gizmo

    GizmoConfig 不再是资源,必须通过 GizmoConfigStore 资源进行访问。默认配置组是 DefaultGizmoConfigGroup,但在适用时请考虑使用您自己的自定义配置组。

    将 Direction3d 用于 gizmos.circle 法线 #

    Gizmo

    为 gizmos.circle 法线传递一个 Direction3d,例如 Direction3d::new(vec).unwrap_or(default) 或如果您知道您的 vec 绝对已标准化,则可能是 Direction3d::new_unchecked(vec)

    将 "AddChild" 重命名为 "PushChild" #

    层次结构

    结构 AddChild 已重命名为 PushChild,结构 AddChildInPlace 已重命名为 PushChildInPlace

    Input 重命名为 ButtonInput #

    输入

    用户需要在其项目中将 Input 重命名为 ButtonInput

    将窗口实体添加到 TouchInput 事件 #

    输入

    在构造或解构 TouchInput 结构时添加 window 字段。

    将 delta 添加到 CursorMoved 事件 #

    输入

    您需要将 delta 添加到任何手动创建的 CursorMoved 结构中。

    删除 CubicCurveDefault 实现 #

    数学
    • 从实现 Default 的任何结构中删除 CubicCurve
    • CubicCurve 包裹在一个新类型中并提供您自己的默认值。
    #[derive(Deref)]
    struct MyCubicCurve<P: Point>(pub CubicCurve<P>);
    
    impl Default for MyCubicCurve<Vec2> {
        fn default() -> Self {
            let points = [[
                vec2(-1.0, -20.0),
                vec2(3.0, 2.0),
                vec2(5.0, 3.0),
                vec2(9.0, 8.0),
            ]];
    
            Self(CubicBezier::new(points).to_curve())
        }
    }
    

    Direction:将 from_normalized 重命名为 new_unchecked #

    数学

    Direction2d::from_normalizedDirection3d::from_normalized 重命名为 new_unchecked

    添加 Capsule2d 原语 #

    数学

    Capsule 现在是 Capsule3d。如果您在 2d 中使用它,您需要使用 Capsule2d

    将 RayTest 重命名为 RayCast #

    数学

    RayTest2d 和 RayTest3d 已重命名为 RayCast2d 和 RayCast3d

    在 breakout 示例碰撞中使用 IntersectsVolume #

    物理

    sprite::collide_aabb::collidesprite::collide_aabb::Collision 已删除。

    // 0.12
    let collision = bevy::sprite::collide_aabb::collide(a_pos, a_size, b_pos, b_size);
    if collision.is_some() {
        // ...
    }
    
    // 0.13
    let collision = Aabb2d::new(a_pos.truncate(), a_size / 2.)
        .intersects(&Aabb2d::new(b_pos.truncate(), b_size / 2.));
    if collision {
        // ...
    }
    

    如果您正在使用 collide_aabb::Collision,请查看 breakout 示例 中的新 collide_with_side 函数。

    添加 ReflectFromWorld 并将 ReflectComponentReflectBundle 上的 FromWorld 要求替换为 FromReflect #

    反射
    • ReflectComponent::from_worldReflectBundle::from_world 的现有使用需要更改为 ReflectFromWorld::from_world
    • #[reflect(Component)]#[reflect(Bundle)] 的用户还需要实现/派生 FromReflect
    • #[reflect(Component)]#[reflect(Bundle)] 的用户现在可能还希望将 FromWorld 添加到反射特性的列表中,以防他们的 FromReflect 实现可能失败。
    • ReflectComponent 的用户现在需要向其 insertapply_or_insertcopy 方法传递一个 &TypeRegistry

    删除 TypeUuid #

    反射

    将任何使用 #[derive(TypeUuid)] 的地方转换为 #[derive(TypePath],对于更复杂的用途,请查看相关的 文档 以获取更多信息。

    显式颜色转换方法 #

    渲染

    Color::from(Vec4) 现在是 Color::rgba_from_array(impl Into<[f32; 4]>) Vec4::from(Color) 现在是 Color::rgba_to_vec4(&self)

    // 0.12
    let color_vec4 = Vec4::new(0.5, 0.5, 0.5);
    let color_from_vec4 = Color::from(color_vec4);
    
    let color_array = [0.5, 0.5, 0.5];
    let color_from_array = Color::from(color_array);
    
    // 0.13
    let color_vec4 = Vec4::new(0.5, 0.5, 0.5);
    let color_from_vec4 = Color::rgba_from_array(color_vec4);
    
    let color_array = [0.5, 0.5, 0.5];
    let color_from_array = Color::rgba_from_array(color_array);
    

    Material2d 添加一个 depth_bias #

    渲染

    PreparedMaterial2d 有一个新的 depth_bias 字段。可以使用 0.0 的值来获得之前的行为。

    绑定组布局条目 #

    渲染

    RenderDevice::create_bind_group_layout() 不再接受 BindGroupLayoutDescriptor。您需要分别提供参数

    // 0.12
    let layout = render_device.create_bind_group_layout(&BindGroupLayoutDescriptor {
        label: Some("post_process_bind_group_layout"),
        entries: &[
            BindGroupLayoutEntry {
                // ...
            },
        ],
    });
    
    // 0.13
    let layout = render_device.create_bind_group_layout(
     "post_process_bind_group_layout",
        &[
            BindGroupLayoutEntry {
                // ...
            },
        ],
    );
    

    交换材质和网格绑定组 #

    渲染
    • 自定义 2d 和 3d 网格/材质着色器现在应该在其绑定资源中使用绑定组 2 @group(2) @binding(x),而不是绑定组 1。
    • 渲染代码的许多内部部分都已更改,因此网格数据现在位于绑定组 1 中,而材质数据现在位于绑定组 2 中。半自定义渲染设置(不使用 Material 或 Material2d API)应该适应这些更改。

    光线渲染层 #

    渲染

    默认情况下,灯光不再影响所有RenderLayers,现在与摄像机和网格一样,它们默认设置为RenderLayers::layer(0)。 要恢复以前的行为并让所有灯光影响所有视图,请向灯光实体添加RenderLayers::all()组件。

    更新到 wgpu 0.18 #

    渲染
    • RenderPassDescriptorcolor_attachments(以及RenderPassColorAttachmentRenderPassDepthStencilAttachment)现在使用StoreOp::StoreStoreOp::Discard代替boolean来声明是否应该存储它们。
    • RenderPassDescriptor现在具有timestamp_writesocclusion_query_set字段。 这些可以安全地设置为None
    • ComputePassDescriptor现在具有timestamp_writes字段。 目前可以将其设置为None
    • 有关更多详细信息,请参见wgpu 变更日志

    跟踪纹理首次清除的时间 #

    渲染
    • 删除ViewTarget::get_color_attachment()ViewTarget::get_unsampled_color_attachment()的参数。
    • Camera上配置清除颜色,而不是在Camera3dCamera2d上配置。
    • ClearColorClearColorConfigbevy::core_pipeline::clear_color移动到bevy::render::camera
    • ViewDepthTexture现在必须通过new()方法创建

    近似间接镜面遮挡 #

    渲染

    PbrInput::occlusion重命名为diffuse_occlusion,并添加了specular_occlusion

    纹理图集重做 #

    渲染

    之前包含图集布局和图像句柄的TextureAtlas资产已重命名为TextureAtlasLayout,图像句柄部分已移动到一个单独的Handle<Image>,可从SpriteSheetBundle::textureAtlasImageBundle::image获取。

    TextureAtlasSprite已被删除,并被一个新的组件TextureAtlas替换,该组件现在保存图集索引。 Sprite组件可用于flip_xflip_ycustom_sizeanchorcolor

    SpriteSheetBundle现在使用Sprite而不是TextureAtlasSprite组件,使用TextureAtlas组件而不是Handle<TextureAtlaslayout>

    DynamicTextureAtlasBuilder::add_texture接收一个额外的&Handle<Image>参数。

    TextureAtlasLayout::from_grid不再接收Handle<Image>参数。

    TextureAtlasBuilder::finish现在返回Result<(TextureAtlasLayout, Image), TextureAtlasBuilderError>

    UiTextureAtlasImage已被删除。 AtlasImageBundle现在与ImageBundle相同,并附带一个额外的TextureAtlas

    • 精灵
    fn my_system(
      mut commands: Commands,
    -  mut atlases: ResMut<Assets<TextureAtlas>>,
    +  mut atlases: ResMut<Assets<TextureAtlasLayout>>,
      asset_server: Res<AssetServer>
    ) {
        let texture_handle = asset_server.load("my_texture.png");
    -   let layout = TextureAtlas::from_grid(texture_handle, Vec2::new(25.0, 25.0), 5, 5, None, None);
    +   let layout = TextureAtlasLayout::from_grid(Vec2::new(25.0, 25.0), 5, 5, None, None);
        let layout_handle = atlases.add(layout);
        commands.spawn(SpriteSheetBundle {
    -      sprite: TextureAtlasSprite::new(0),
    -      texture_atlas: atlas_handle,
           // the new sprite initialization is covered by the `..default()` expression; however, it is added to showcase migration
    +      sprite: Sprite::default(),
    +      atlas: TextureAtlas {
    +         layout: layout_handle,
    +         index: 0
    +      },
    +      texture: texture_handle,
           ..default()
         });
    }
    
    • UI
    fn my_system(
      mut images: ResMut<Assets<Image>>,
    -  mut atlases: ResMut<Assets<TextureAtlas>>,
    +  mut atlases: ResMut<Assets<TextureAtlasLayout>>,
      asset_server: Res<AssetServer>
    ) {
        let texture_handle = asset_server.load("my_texture.png");
    -   let layout = TextureAtlas::from_grid(texture_handle, Vec2::new(25.0, 25.0), 5, 5, None, None);
    +   let layout = TextureAtlasLayout::from_grid(Vec2::new(25.0, 25.0), 5, 5, None, None);
        let layout_handle = atlases.add(layout);
        commands.spawn(AtlasImageBundle {
    -      texture_atlas_image: UiTextureAtlasImage {
    -           index: 0,
    -           flip_x: false,
    -           flip_y: false,
    -       },
    -      texture_atlas: atlas_handle,
    +      texture_atlas: TextureAtlas {
    +         layout: layout_handle,
    +         index: 0
    +      },
    +      image: UiImage {
    +           texture: texture_handle,
    +           flip_x: false,
    +           flip_y: false,
    +       },
           ..default()
         });
    }
    
    fn animate_sprite(
        time: Res<Time>,
        mut query: Query<(
            &AnimationIndices,
            &mut AnimationTimer,
    -       &mut TextureAtlasSprite)>,
    +       &mut TextureAtlas)>,
    ) {
    -   for (indices, mut timer, mut sprite) in &mut query {
    +   for (indices, mut timer, mut atlas) in &mut query {
            timer.tick(time.delta());
            if timer.just_finished() {
    -           sprite.index = if sprite.index == indices.last {
    +           atlas.index = if atlas.index == indices.last {
                    indices.first
                } else {
    -               sprite.index + 1
    +               atlas.index + 1
                };
            }
        }
    }
    

    曝光设置(采用) #

    渲染
    • 如果使用SkyboxEnvironmentMapLight,请使用新的brightnessintensity控件调整其强度。
    • 所有 3D 场景现在将具有不同的表观亮度,因为 Bevy 实现了正确的曝光控制。 您将必须调整灯光的强度和/或通过新的Exposure组件进行补偿。

    使DynamicUniformBuffer::push接受&T而不是T #

    渲染

    DynamicUniformBuffer::push的用户现在需要向DynamicUniformBuffer::push传递引用(例如,现有的uniforms.push(value)现在将变为uniforms.push(&value)

    可定制的摄像机主纹理使用 #

    渲染

    main_texture_usages: Default::default()添加到您的相机捆绑包中。

    优化 batch_and_prepare_render_phase #

    渲染

    特质GetBatchData不再包含关联类型DataFilter get_batch_data query_item类型从Self::DataEntity,并返回Option<(Self::BufferData, Option<Self::CompareData>)> batch_and_prepare_render_phase不应有查询

    更新到 wgpu 0.19 和 raw-window-handle 0.6 #

    渲染
    • bevy_render::instance_index::get_instance_index()已被删除,因为 webgl2 解决方案不再需要,因为它已在 wgpu 上游修复。 BASE_INSTANCE_WORKAROUND 着色器定义也被删除。

    • WebGPU 现在需要启用新的webgpu功能。 webgpu功能当前会覆盖webgl2功能,因此您不再需要禁用所有默认功能并在针对webgpu时重新添加它们,但是使用webgpuwebgl2功能构建的二进制文件将只针对 webgpu 后端,并且只会在支持 WebGPU 的浏览器上运行。

      • 您根据 webgl2 条件编译内容的地方需要更新,例如
        • #[cfg(any(not(feature = "webgl"), not(target_arch = "wasm32")))] 变为 #[cfg(any(not(feature = "webgl") ,not(target_arch = "wasm32"), feature = "webgpu"))]
        • #[cfg(all(feature = "webgl", target_arch = "wasm32"))] 变为 #[cfg(all(feature = "webgl", target_arch = "wasm32", not(feature = "webgpu")))]
        • if cfg!(all(feature = "webgl", target_arch = "wasm32")) 变为 if cfg!(all(feature = "webgl", target_arch = "wasm32", not(feature = "webgpu")))
    • create_texture_with_data现在还接收TextureDataOrder。 您可能只需将其设置为TextureDataOrder::default()

    • TextureFormatblock_size已重命名为block_copy_size

    • 有关我可能遗漏的内容,请参阅wgpu变更日志:https://github.com/gfx-rs/wgpu/blob/trunk/CHANGELOG.md

    • wgpu现在在实例创建时显示错误,这可能会导致您遇到此错误(我们也曾在 EOSOverlay 而不是 nsight 中看到过它)。

    2024-01-27T02:11:58.491767Z ERROR wgpu_hal::vulkan::instance: GENERAL [Loader Message (0x0)]
            loader_get_json: Failed to open JSON file C:\Program Files (x86)\Epic Games\Launcher\Portal\Extras\Overlay\EOSOverlayVkLayer-Win32.json
    2024-01-27T02:11:58.492046Z ERROR wgpu_hal::vulkan::instance:   objects: (type: INSTANCE, hndl: 0x1fbe55dc070, name: ?)
    2024-01-27T02:11:58.492282Z ERROR wgpu_hal::vulkan::instance: GENERAL [Loader Message (0x0)]
            loader_get_json: Failed to open JSON file C:\Program Files (x86)\Epic Games\Launcher\Portal\Extras\Overlay\EOSOverlayVkLayer-Win64.json
    2024-01-27T02:11:58.492525Z ERROR wgpu_hal::vulkan::instance:   objects: (type: INSTANCE, hndl: 0x1fbe55dc070, name: ?)
    

    这仅仅意味着程序在更新/卸载时没有正确清理其注册表项,并且 vulkan 使用这些项来加载验证层。 解决方法是备份您的注册表,然后删除"Computer\HKEY_LOCAL_MACHINE\SOFTWARE\Khronos\Vulkan\ImplicitLayers"中的相关项。

    渲染图标签化 #

    渲染

    对于节点和子图,现在您不再使用硬编码字符串,而是传递标签,这些标签可以使用结构体和枚举来推导。

    // 0.12
    #[derive(Default)]
    struct MyRenderNode;
    impl MyRenderNode {
        pub const NAME: &'static str = "my_render_node"
    }
    
    render_app
        .add_render_graph_node::<ViewNodeRunner<MyRenderNode>>(
            core_3d::graph::NAME,
            MyRenderNode::NAME,
        )
        .add_render_graph_edges(
            core_3d::graph::NAME,
            &[
                core_3d::graph::node::TONEMAPPING,
                MyRenderNode::NAME,
                core_3d::graph::node::END_MAIN_PASS_POST_PROCESSING,
            ],
        );
    
    // 0.13
    use bevy::core_pipeline::core_3d::graph::{Node3d, Core3d};
    
    #[derive(Debug, Hash, PartialEq, Eq, Clone, RenderLabel)]
    pub struct MyRenderLabel;
    
    #[derive(Default)]
    struct MyRenderNode;
    
    render_app
        .add_render_graph_node::<ViewNodeRunner<MyRenderNode>>(
            Core3d,
            MyRenderLabel,
        )
        .add_render_graph_edges(
            Core3d,
            (
                Node3d::Tonemapping,
                MyRenderLabel,
                Node3d::EndMainPassPostProcessing,
            ),
        );
    

    如果您仍然想使用动态标签,您可以轻松地使用元组结构体创建它们。

    #[derive(Debug, Hash, PartialEq, Eq, Clone, RenderLabel)]
    pub struct MyDynamicLabel(&'static str);
    

    子图 #

    • bevy_core_pipeline::core_2d::graph: NAME -> Core2d
    • bevy_core_pipeline::core_3d::graph:NAME -> Core3d
    • bevy_ui::render: draw_ui_graph::NAME -> graph::SubGraphUi

    节点 #

    • bevy_core_pipeline::core_2d::graph:
      • node::MSAA_WRITEBACK -> Node2d::MsaaWriteback node::MAIN_PASS ->Node2d::MainPass node::BLOOM -> Node2d::Bloom node::TONEMAPPING -> Node2d::Tonemapping node::FXAA -> Node2d::Fxaa node::UPSCALING -> Node2d::Upscaling node::CONTRAST_ADAPTIVE_SHARPENING -> Node2d::ContrastAdaptiveSharpening node::END_MAIN_PASS_POST_PROCESSING -> Node2d::EndMainPassPostProcessing
    • bevy_core_pipeline::core_3d::graph:
      • node::MSAA_WRITEBACK -> Node3d::MsaaWriteback node::PREPASS -> Node3d::Prepass node::DEFERRED_PREPASS -> Node3d::DeferredPrepass node::COPY_DEFERRED_LIGHTING_ID -> Node3d::CopyDeferredLightingId node::END_PREPASSES -> Node3d::EndPrepasses node::START_MAIN_PASS -> Node3d::StartMainPass node::MAIN_OPAQUE_PASS -> Node3d::MainOpaquePass node::MAIN_TRANSMISSIVE_PASS -> Node3d::MainTransmissivePass node::MAIN_TRANSPARENT_PASS -> Node3d::MainTransparentPass node::END_MAIN_PASS -> Node3d::EndMainPass node::BLOOM -> Node3d::Bloom node::TONEMAPPING -> Node3d::Tonemapping node::FXAA -> Node3d::Fxaa node::UPSCALING -> Node3d::Upscaling node::CONTRAST_ADAPTIVE_SHARPENING -> Node3d::ContrastAdaptiveSharpening node::END_MAIN_PASS_POST_PROCESSING -> Node3d::EndMainPassPostProcessing
    • bevy_core_pipeline: taa::draw_3d_graph::node::TAA -> Node3d::Taa
    • bevy_pbr: draw_3d_graph::node::SHADOW_PASS -> NodePbr::ShadowPass ssao::draw_3d_graph::node::SCREEN_SPACE_AMBIENT_OCCLUSION -> NodePbr::ScreenSpaceAmbientOcclusion deferred::DEFERRED_LIGHTING_PASS -> NodePbr::DeferredLightingPass
    • bevy_render: main_graph::node::CAMERA_DRIVER -> graph::CameraDriverLabel
    • bevy_ui::render: draw_ui_graph::node::UI_PASS -> graph::Nodeui::UiPass

    将漫射和镜面透射放在着色器定义后面 #

    渲染

    如果您在着色器代码中使用的是#ifdef STANDARDMATERIAL_NORMAL_MAP,请确保将其更新为STANDARD_MATERIAL_NORMAL_MAP;(在STANDARDMATERIAL之间使用下划线)

    异步管道编译 #

    渲染

    CachedPipelineState的穷举匹配,匹配新的Creating变体

    网格插入索引 #

    渲染
    • 使用Mesh::insert_indicesMesh::with_inserted_indices而不是Mesh::set_indices / Mesh::with_indices
    • 如果您已将None传递给Mesh::set_indicesMesh::with_indices,则应使用Mesh::remove_indicesMesh::with_removed_indices

    在主世界被丢弃时等待渲染应用程序 #

    渲染

    如果您使用的是流水线渲染通道MainToRenderAppSenderRenderToMainAppReceiver,它们已合并到单个资源RenderAppChannels中。

    弃用 bevy_render::mesh::shape 中的形状 #

    渲染

    Bevy 以前使用过渲染特定的类型,例如UVSphereQuad,用于表示原始网格形状。 这些现在已被弃用,转而使用 0.13 版本中新引入的几何基元。

    • bevy_render::mesh::Capsule 已被弃用,请改用 bevy_math::primitives::dim3::Capsule3d
    • bevy_render::mesh::Cylinder 已被弃用,请改用 bevy_math::primitives::dim3::Cylinder
    • bevy_render::mesh::Icosphere 已被弃用,请改用 bevy_math::primitives::dim3::Sphere
    • bevy_render::mesh::Cube 已被弃用,请改用 bevy_math::primitives::dim3::Cuboid
    • bevy_render::mesh::Box 已被弃用,请改用 bevy_math::primitives::dim3::Cuboid
    • bevy_render::mesh::Quad 已被弃用,请改用 bevy_math::primitives::dim2::Rectangle
    • bevy_render::mesh::Plane 已被弃用,请改用 bevy_math::primitives::dim2::Plane2dbevy_math::primitives::dim3::Plane3d
    • bevy_render::mesh::RegularPolygon 已被弃用,请改用 bevy_math::primitives::dim2::RegularPolygon
    • bevy_render::mesh::Circle 已被弃用,请改用 bevy_math::primitives::dim2::Circle
    • bevy_render::mesh::Torus 已被弃用,请改用 bevy_math::primitives::dim3::Torus
    • bevy_render::mesh::UVSphere 已被弃用,请改用 bevy_math::primitives::dim3::Sphere

    一些示例

    let before = meshes.add(shape::Box::new(5.0, 0.15, 5.0));
    let after = meshes.add(Cuboid::new(5.0, 0.15, 5.0));
    
    let before = meshes.add(shape::Quad::default());
    let after = meshes.add(Rectangle::default());
    
    let before = meshes.add(shape::Plane::from_size(5.0));
    // The surface normal can now also be specified when using `new`
    let after = meshes.add(Plane3d::default().mesh().size(5.0, 5.0));
    
    let before = meshes.add(
        Mesh::try_from(shape::Icosphere {
            radius: 0.5,
            subdivisions: 5,
        })
        .unwrap(),
    );
    let after = meshes.add(Sphere::new(0.5).mesh().ico(5).unwrap());
    

    多线程渲染命令编码 #

    渲染

    RenderContext::new()现在接收适配器信息

    一些渲染图和节点相关类型和方法现在具有额外的生命周期约束。

    停止将网格实体提取到渲染世界。 #

    渲染

    出于效率考虑,渲染世界中的一些网格可能不再具有对应的 `Entity` ID。因此,`RenderCommand::render()` 的 `entity` 参数现在被包装在一个 `Option` 中。自定义渲染代码可能需要更新以处理为要渲染的对象不存在 `Entity` 的情况。

    新的曝光和灯光默认值(以及校准示例) #

    渲染

    `Exposure::ev100` 的增加意味着所有现有的 3D 照明都需要调整以匹配(方向光、点光、聚光灯、环境贴图灯等)。或者,您可以调整相机上的 `Exposure::ev100` 以与您当前的照明值配合使用。如果您目前依赖于默认强度值,您可能需要更改强度以达到相同的效果。请注意,在 Bevy 0.12 中,点光/聚光灯的硬编码 ev100 值与方向光不同。在 Bevy 0.13 中,它们使用相同的 ev100,因此如果您在场景中同时拥有两者,这些光源类型的 *缩放* 已经改变,您可能需要调整其中一个或两个。

    许多默认照明值已更改

    • `PointLight` 的默认强度现在为 `1_000_000.0`
    • `SpotLight` 的默认强度现在为 `1_000_000.0`
    • `DirectionalLight` 的默认照度现在为 `light_consts::lux::AMBIENT_DAYLIGHT`(`10_000.`)
    • `AmbientLight` 的默认亮度现在为 `80.0`

    从 RAM 中卸载渲染资源 #

    渲染
    资产

    渲染资源现在可以在渲染世界中准备好渲染后从主世界中自动卸载。这由 `RenderAssetUsages` 位标志控制。要模拟旧的行为并将资源保留在主世界中,请使用 `RenderAssetUsages::default()`。

    • `Mesh` 现在需要一个新的 `asset_usage` 字段。将其设置为 `RenderAssetUsages::default()` 以模拟之前的行为。

    • `Image` 现在需要一个新的 `asset_usage` 字段。将其设置为 `RenderAssetUsages::default()` 以模拟之前的行为。

    • `MorphTargetImage::new()` 现在需要一个新的 `asset_usage` 参数。将其设置为 `RenderAssetUsages::default()` 以模拟之前的行为。

    • `DynamicTextureAtlasBuilder::add_texture()` 现在要求您传递的 `TextureAtlas` 具有一个 `Image`,其 `asset_usage: RenderAssetUsages::default()`。确保为纹理图集正确构建图像。

    • `RenderAsset` 特征已发生重大改变,需要您调整现有的实现。

      • 该特征现在需要 `Clone`。
      • `ExtractedAsset` 关联类型已移除(类型本身现在被提取)。
      • `prepare_asset()` 的签名略有不同
      • 现在需要一个新的 `asset_usage()` 方法(返回 `RenderAssetUsages::default()` 以匹配之前的行为)。
    • 对 `AssetEvent` 的穷举匹配使用新的 `NoLongerUsed` 变体进行匹配。

    将 `Ray` 拆分为 `Ray2d` 和 `Ray3d` 并简化平面构造 #

    渲染
    数学

    `Ray` 已重命名为 `Ray3d`。

    射线创建 #

    // 0.12
    Ray {
        origin: Vec3::ZERO,
        direction: Vec3::new(0.5, 0.6, 0.2).normalize(),
    }
    
    // 0.13
    // Option 1:
    Ray3d {
        origin: Vec3::ZERO,
        direction: Direction3d::new(Vec3::new(0.5, 0.6, 0.2)).unwrap(),
    }
    
    // Option 2:
    Ray3d::new(Vec3::ZERO, Vec3::new(0.5, 0.6, 0.2))
    

    平面交点 #

    // 0.12
    let result = ray.intersect_plane(Vec2::X, Vec2::Y);
    
    // 0.13
    let result = ray.intersect_plane(Vec2::X, Plane2d::new(Vec2::Y));
    

    引入 AspectRatio 结构 #

    渲染
    数学

    在您目前期望获得纵横比时获取 f32 的任何地方,您现在将收到一个 `AspectRatio` 结构。这仍然保持相同的值。

    在 UiMaterial 的顶点输入中包含 UI 节点大小。 #

    渲染
    UI

    此更改应向后兼容,使用新字段是可选的。

    可选的 ImageScaleMode #

    渲染
    UI

    在 `bevy_tasks` 中重新导出 `futures_lite` #

    任务
    • 从 `Cargo.toml` 中删除 `futures_lite`。
    [dependencies]
    bevy = "0.12.0"
    - futures-lite = "1.4.0"
    
    • 用 `bevy::tasks::futures_lite` 替换 `futures_lite` 导入。
    - use futures_lite::future::poll_once;
    + use bevy::tasks::futures_lite::future::poll_once;
    

    将 `TextAlignment` 重命名为 `JustifyText`。 #

    文字
    • `TextAlignment` 已重命名为 `JustifyText`
    • `TextBundle::with_text_alignment` 已重命名为 `TextBundle::with_text_justify`
    • `Text::with_alignment` 已重命名为 `Text::with_justify`
    • `TextMeasureInfo` 的 `text_alignment` 字段已重命名为 `justification`

    重命名 `Time::<Fixed>::overstep_percentage()` 和 `Time::<Fixed>::overstep_percentage_f64()` #

    时间
    • `Time::<Fixed>::overstep_percentage()` 已重命名为 `Time::<Fixed>::overstep_fraction()`
    • `Time::<Fixed>::overstep_percentage_f64()` 已重命名为 `Time::<Fixed>::overstep_fraction_f64()`

    重命名 `Timer::{percent,percent_left}` 为 `Timer::{fraction,fraction_remaining}` #

    时间
    • `Timer::percent()` 已重命名为 `Timer::fraction()`
    • `Timer::percent_left()` 已重命名为 `Timer::fraction_remaining()`

    从 Transform::up 及其同类函数返回 Direction3d #

    变换
    数学

    `Transform::up()` 及其同类函数的调用者可能需要取消引用返回的 `Direction3d` 以获取内部 `Vec3`。

    使 UI 节点的裁剪区域不可交互 #

    UI

    UI 节点的裁剪区域不再可交互。

    `RelativeCursorPosition` 现在相对于整个节点的位置和大小计算,而不仅仅是可见部分。它的 `mouse_over` 方法仅在光标位于节点的未裁剪部分上时返回 true。

    `RelativeCursorPosition` 不再实现 `Deref` 和 `DerefMut`。

    为 bevy_ui 创建序列化功能 #

    UI

    如果您想使用序列化和反序列化 bevy_ui 中的类型,您需要在 TOML 中使用序列化功能

    [dependencies.bevy]
    features = ["serialize"]
    

    相机驱动的 UI #

    UI

    如果世界包含多个相机,请在每个 UI 根节点中插入 `TargetCamera(Entity)` 组件,其中 `Entity` 是您希望此特定 UI 树渲染到的相机的 ID。测试 UI 定位和缩放所需的任何调整。

    // 0.12
    commands.spawn(Camera3dBundle { ... });
    commands.spawn(NodeBundle { ... });
    
    // 0.13
    let camera = commands.spawn(Camera3dBundle { ... }).id();
    commands.spawn((TargetCamera(camera), NodeBundle { ... }));
    

    从所有相机中删除 `UiCameraConfig` 组件。如果它被用于控制 UI 可见性,请改为在 UI 根节点上插入 `Visibility` 组件。

    将 `Window` 缩放因子更改为 f32(已采用) #

    窗口

    如果操作 `scale_factor`,可能需要进行一些从 f64 到 f32 的转换。

    将 winit 依赖项更新到 0.29 #

    窗口
    输入

    KeyCode 更改 #

    几个 `KeyCode` 变体已重命名,现在代表键盘上的物理键,替换 `ScanCode`。

    更新后的变体的常见示例如下

    • `KeyCode::W` -> `KeyCode::KeyW`
    • `KeyCode::Up` -> `KeyCode::ArrowUp`
    • `KeyCode::Key1` -> `KeyCode::Digit1`

    有关更多信息,请参阅相关的 文档

    ReceivedCharacter 更改 #

    `ReceivedCharacter` 的 `char` 字段现在是 `SmolStr`,并且 *可能* 包含多个字符。有关详细信息,请参阅这些 winit 文档

    如果您需要一个 `char`,一个简单的解决方法是调用 `chars().last()`。

    如果您需要一致的跨平台行为和/或与非字符事件统一的事件流,现在可以使用 `KeyEvent::logical_key` 的 `Character` 变体。

    移除 CanvasParentResizePlugin #

    窗口

    删除 `Window::fit_canvas_to_parent` 的使用,改为使用 CSS 属性,例如

    canvas {
      width: 100% !important;
      height: 100% !important;
    }
    

    清理 bevy winit #

    窗口

    `bevy::window::WindowMoved` 的 `entity` 字段已重命名为 `window`。这样做是为了与其他窗口事件保持一致。

    考虑更改使用情况

    -window_moved.entity
    +window_moved.window
    

    在 `bevy::window::Window` 中添加 `name` #

    窗口

    `Window` 有一个新的 `name` 字段,用于指定“窗口类名称”。如果您不需要此功能,请将其设置为 `None`。

    删除 0.12 中已弃用的方法 #

    没有区域标签

    许多在 0.12 中被标记为已弃用的方法现已被移除。您应该考虑在迁移到 0.13 之前修复弃用警告。

    在 bevy_winit 中将 Accessibility 插件重命名为 AccessKitPlugin #

    没有区域标签

    `bevy_winit::AccessibilityPlugin` 已重命名为 `AccessKitPlugin`。

    尽可能使用 TypeIdMap #

    没有区域标签
    • `TypeIdMap` 现在位于 `bevy_utils` 中
    • `DrawFunctionsInternal::indices` 现在使用 `TypeIdMap`。

    bevy_render: 使用来自 bevy_core 的非发送标记 #

    没有区域标签

    如果您正在使用 `bevy::render::view::NonSendMarker` 或 `bevy::render::view::window::NonSendMarker`,请改为使用 `bevy::core::NonSendMarker`