迁移指南

草稿页面

此页面仍在开发中(跟踪问题:1741)。此页面及其下的所有页面都将从搜索引擎和菜单中隐藏!

迁移指南:0.14 到 0.15

无区域 #

修复 asset_settings 示例回归 #

此 PR 显然不需要迁移指南,因为这只是一个错误修复,但我认为 #15812 应该提到元文件需要更新。建议

  • 必须在图像的 .meta 文件中更新资产加载器名称。更改:loader: "bevy_render::texture::image_loader::ImageLoader", 为:loader: "bevy_image::image_loader::ImageLoader", 它将修复以下错误:no AssetLoader found with the name 'bevy_render::texture::image_loader::ImageLoader

移除 AVIF 功能 #

不再支持 AVIF 图像。它们从未真正起作用,并且需要系统依赖项(libdav1d)才能正常工作,因此,最好在需要时通过非官方插件提供此支持。相应的类型已从 Bevy 中移除以解决此问题。

动画 #

使 AnimationPlayer::start::play 按照文档工作 #

AnimationPlayer::start 现在根据其文档相应地重新启动正在运行的动画。AnimationPlayer::play 不再重置权重。

实现动画遮罩,允许精细控制动画影响的目标。 #

  • 随着动画遮罩的添加,动画图的序列化格式已更改。要升级动画图 RON 文件,请根据需要添加 maskmask_groups 字段。(它们可以安全地设置为零。)

删除 TransformCurve #

目前没有发布的版本包含此更改,但如果我们合并此拉取请求,应该确保 TransformCurve 从 #15434 的发布说明中排除。

对动画图的评估实施更合理的排序。 #

本节可选。如果没有任何重大更改,可以删除本节。

  • 如果此 PR 是重大更改(相对于 Bevy 的上次发布),请描述用户如何迁移其代码以支持这些更改。
  • 仅仅添加新功能并不构成重大更改。
  • 修复明确为错误的行为,而不是有问题的设计选择,并不构成重大更改。

为动画图实现加法混合。 #

  • animgraph.ron 格式已更改以适应新的加法混合功能。你需要将 clip 字段更改为新的 AnimationNodeType 枚举的实例。

修复四元数的加法混合 #

此 PR 更改了 Quat: Animatable 的实现,该实现在此发布版本之前未被 Bevy 内部使用。如果你在手动应用中依赖于旧的加法四元数混合行为,则需要更新该代码,因为旧的行为是错误的。

用包装器替换 Handle<AnimationGraph> 组件 #

Handle<AnimationGraph> 不再是组件。而是使用 AnimationGraphHandle 组件,该组件包含一个 Handle<AnimationGraph>

基于曲线的动画 #

大多数不直接处理 AnimationClipVariableCurve 的用户代码不需要更改。另一方面,VariableCurve 已经完全改版。如果你之前使用关键帧在代码中定义动画曲线,则需要迁移该代码以使用曲线构造函数。例如,使用关键帧定义并添加到动画剪辑中的旋转动画,如下所示

animation_clip.add_curve_to_target(
    animation_target_id,
    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,
    },
);

现在将添加如下所示:

animation_clip.add_curve_to_target(
    animation_target_id,
    AnimatableKeyframeCurve::new([0.0, 1.0, 2.0, 3.0, 4.0].into_iter().zip([
        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,
    ]))
    .map(RotationCurve)
    .expect("Failed to build rotation curve"),
);

请注意,AnimationClip::add_curve_to_target 的接口也已更改(如本示例所示,虽然细微),现在将曲线输入作为 impl AnimationCurve 接收。如果你需要直接添加 VariableCurve,则新的方法 add_variable_curve_to_target 支持此操作(并且在这方面充当一对一迁移)。

供审阅者参考

差异非常大,一些更改的结构可能并不十分明显。

  • keyframes.rs 变成了 animation_curves.rs,并且 AnimationCurve 很大程度上基于 Keyframes,适配器也大多遵循此模式。
  • 曲线 API 适配器结构已从 bevy_math::curve::mod 移动到自己的模块 adaptors 中。这些适配器的功能没有改变,只是为了为专门的反射实现腾出空间,因为 mod.rs 变得有点拥挤。
  • 新的模块 gltf_curves 包含 glTF 加载器所需的额外曲线构造。请注意,加载器使用这些构造和现成的 bevy_math 曲线内容的混合。
  • animatable.rs 不再包含与关键帧插值相关的逻辑,这些逻辑现在委托给 bevy_math::curve::cores 中现有的抽象。

允许动画剪辑动画化任意属性。 #

  • 动画关键帧现在是一个可扩展的特征,而不是枚举。用 Box::new(TranslationKeyframes(...))Box::new(ScaleKeyframes(...))Box::new(RotationKeyframes(...))Box::new(MorphWeightsKeyframes(...)) 分别替换 Keyframes::Translation(...)Keyframes::Scale(...)Keyframes::Rotation(...)Keyframes::Weights(...)

App #

.add_before.add_after 中删除第二个泛型 #

PluginGroupBuilder 方法中删除了第二个泛型:add_beforeadd_after

// Before:
DefaultPlugins
    .build()
    .add_before::<WindowPlugin, _>(FooPlugin)
    .add_after::<WindowPlugin, _>(BarPlugin)

// After:
DefaultPlugins
    .build()
    .add_before::<WindowPlugin>(FooPlugin)
    .add_after::<WindowPlugin>(BarPlugin)

删除对 EventLoopProxyNonSend 的需求 #

EventLoopProxy 已重命名为 EventLoopProxyWrapper,现在为 Send,使其成为一个普通的资源。

之前

event_loop_system(event_loop: NonSend<EventLoopProxy<MyEvent>>) {
    event_loop.send_event(MyEvent);
}

之后

event_loop_system(event_loop: Res<EventLoopProxy<MyEvent>>) {
    event_loop.send_event(MyEvent);
}

删除已弃用的 bevy_dynamic_plugin #

动态插件在 0.14 中被弃用,原因是它们不安全,现在它们已被完全删除。请考虑使用 bevy_dynamic_plugin crate 文档中列出的替代方案,或者在最坏的情况下,你可以从 0.14 中复制代码。

允许在固定时间步长周围排序可变时间步长 #

run_fixed_main_schedule 不再是公共的。如果你以前针对它进行排序,请使用新的专用 RunFixedMainLoopSystem 系统集。你可以通过 RunFixedMainLoopSystem::FixedMainLoop 一对一地替换你对 run_fixed_main_schedule 的使用,但现在更惯用的做法是将你的系统放置在 RunFixedMainLoopSystem::BeforeFixedMainLoopRunFixedMainLoopSystem::AfterFixedMainLoop 中。

app.add_systems(
    RunFixedMainLoop,
    some_system.before(run_fixed_main_schedule)
);

app.add_systems(
    RunFixedMainLoop,
    some_system.in_set(RunFixedMainLoopSystem::BeforeFixedMainLoop)
);

添加功能以切换 NativeActivityGameActivity 的使用 #

由于 cargo-apkGameActivity 不兼容,因此使用 cargo apk build/run -p bevy_mobile_example 进行构建/运行已不再可能。用户应按照文档中描述的新工作流程进行操作。

Assets #

AssetServer LoadState API 一致性 #

清理 bevy_asset 中不需要的生命周期 #

bevy_asset 中的 AssetLoaderAssetSaverProcess 特征现在使用省略的生命周期。如果你实现了这些特征,则删除命名生命周期。

AsyncSeekForward 替换 ReaderAsyncSeek 特征以解决 #12880 #

在你的资产读取器实现中,将所有 AsyncSeek 实例替换为 AsyncSeekForward

bevy_asset:改进 NestedLoader API #

使用 bevy_assetLoadContext::loader / NestedLoader 的代码将看到一些命名更改

  • untyped 被替换为 with_unknown_type
  • with_asset_type 被替换为 with_static_type
  • with_asset_type_id 被替换为 with_dynamic_type
  • direct 被替换为 immediate(“immediate” 的反义词是 “deferred”)

删除对资产加载错误类型的错误相等性比较 #

bevy_asset::AssetLoadErrorbevy_asset::LoadState 类型不再支持相等性比较。如果你需要检查资产的加载状态,请考虑使用 LoadState::is_loadedmatches! 宏检查特定变体。同样,如果你需要检查代码中资产加载错误的值,请考虑使用 matches! 宏检查 AssetLoadError 类型的特定变体。

DependencyLoadStateRecursiveDependencyLoadState 尚未发布,因此不需要迁移。

更快的 MeshletMesh 反序列化 #

  • 重新生成你的 MeshletMesh 资产,因为磁盘格式已更改,并且 MESHLET_MESH_ASSET_VERSION 已被更新。
  • MeshletMesh 字段现在是私有的。
  • MeshletMeshSaverLoad 现在命名为 MeshletMeshSaverLoader
  • MeshletMeshletBoundingSpheresMeshletBoundingSphere 类型现在是私有的。
  • 已删除 MeshletMeshSaveOrLoadError::SerializationOrDeserialization
  • 已添加 MeshletMeshSaveOrLoadError::WrongFileType,如果你匹配 MeshletMeshSaveOrLoadError,请匹配此变体。

TextureAtlasSourcesTextureAtlasLayout 中分离,并使 TextureAtlasLayout 可序列化 #

TextureAtlasBuilder 不再存储 TextureAtlasLayout 中指向原始图像的映射;该功能已添加到新的结构 TextureAtlasSources 中。这也意味着 TextureAtlasBuilder::finish 的签名已更改,这意味着以下形式的调用

let (atlas_layout, image) = builder.build()?;

现在将更改为以下形式

let (atlas_layout, atlas_sources, image) = builder.build()?;

而不是从布局中执行反向查找,如下所示

let atlas_layout_handle = texture_atlases.add(atlas_layout.clone());
let index = atlas_layout.get_texture_index(&my_handle);
let handle = TextureAtlas {
    layout: atlas_layout_handle,
    index,
};

你可以从源中执行查找

let atlas_layout = texture_atlases.add(atlas_layout);
let index = atlas_sources.get_texture_index(&my_handle);
let handle = TextureAtlas {
    layout: atlas_layout,
    index,
};

此外,TextureAtlasSources 还提供了一个便利方法 handle,它直接将索引和现有的 TextureAtlasLayout 句柄组合成一个新的 TextureAtlas

let atlas_layout = texture_atlases.add(atlas_layout);
let handle = atlas_sources.handle(atlas_layout, &my_handle);

将 glTF 皮肤导出为 Gltf 结构 #

  • GltfAssetLabel::Skin(..) 更改为 GltfAssetLabel::InverseBindMatrices(..)

atomicow 替换 bevy_utils::CowArc #

bevy_utils::CowArc 已移至名为 atomicow 的新 crate 中。

Audio #

将音频迁移到必需的组件 #

AudioPlayer 组件替换所有 AudioSourceBundleAudioBundlePitchBundle 的插入。现在将自动插入它所需的其它组件。

在无法推断泛型的情况下,你可能需要显式指定它们。例如

commands.spawn(AudioPlayer::<AudioSource>(asset_server.load("sounds/sick_beats.ogg")));

Color #

更新网格小部件以使用 Color #

这应该不会添加迁移指南中没有的内容?我假设,由于它在公共接口中使用 impl Into<...>,因此这些 API 的任何用户都不必进行任何代码更改。

Core #

bevy_core::name::DebugName 重命名为 bevy_core::name::NameOrEntity #

  • bevy_core::name::DebugName 的用法重命名为 bevy_core::name::NameOrEntity

跨领域 #

添加 corealloc 超过 std 的 Lint #

MSRV 现在为 1.81。请更新到此版本或更高版本。

Handle 中删除 Component 特征实现 #

Handle 不再可用作 Component。所有使用此模式的现有 Bevy 类型都已封装在自己的语义上有意义的类型中。对于你的项目需要的任何自定义 Handle 组件,你应该执行相同的操作。

Handle<MeshletMesh> 组件现在为 MeshletMesh3d

已删除 WithMeshletMesh 类型别名。请改用 With<MeshletMesh3d>

修复浮点数学 #

  • 不是重大更改
  • 项目应在适用的情况下使用 bevy 数学。

添加自定义光标 #

  • CursorIcon 现在不再是 Window 中的字段,而是一个可以插入到窗口实体的独立组件。它已更改为一个枚举,除了系统图标外,还可以保存自定义图像。
  • Cursor 重命名为 CursorOptionsWindowcursor 字段重命名为 cursor_options
  • CursorIcon 重命名为 SystemCursorIcon

诊断 #

不要忽略绘制错误 #

如果您以前使用 RenderCommandResult::Failure 来忽略错误并稍后重试,请改用 RenderCommandResult::Skip

这并非有意为之,但此 PR 也有助于解决 https://github.com/bevyengine/bevy/issues/12660,因为我们现在可以将一些解包操作转换为错误消息。

ECS #

创建了 EventMutator,用于在读取事件之前对其进行修改 #

目前使用 ManualEventReader 的用户应改用 EventCursorManualEventReader 将在 Bevy 0.16 中移除。此外,Events::get_reader 已被 Events::get_cursor 替换。

目前直接访问 Events 资源进行修改的用户,如果可能,应改为使用 EventMutator

更新 trigger_observers 以对数据切片进行操作 #

  • 待定

简化运行条件 #

一些运行条件已被简化。

// Before:
app.add_systems(Update, (
    system_0.run_if(run_once()),
    system_1.run_if(resource_changed_or_removed::<T>()),
    system_2.run_if(resource_removed::<T>()),
    system_3.run_if(on_event::<T>()),
    system_4.run_if(any_component_removed::<T>()),
));

// After:
app.add_systems(Update, (
    system_0.run_if(run_once),
    system_1.run_if(resource_changed_or_removed::<T>),
    system_2.run_if(resource_removed::<T>),
    system_3.run_if(on_event::<T>),
    system_4.run_if(any_component_removed::<T>),
));

World::increment_change_tick 现在需要 &mut self 而不是 &self #

方法 World::increment_change_tick 现在需要 &mut self 而不是 &self。如果您需要调用此方法但没有对世界进行可变访问,请考虑使用 world.as_unsafe_world_cell_readonly().increment_change_tick(),它执行相同的操作,但由于需要原子同步,效率不如 World 上的方法高。

fn my_system(world: &World) {
    // Before
    world.increment_change_tick();

    // After
    world.as_unsafe_world_cell_readonly().increment_change_tick();
}

添加 FilteredAccess::empty 并简化 AnyOf/Orupdate_component_access 实现 #

  • AnyOf<()>Or<()> 的行为已更改为匹配无原型,而不是匹配所有原型,以自然地匹配相应的逻辑运算。请考虑用 () 代替它们。

在变更检测中跟踪源位置 #

  • 在启用 track_change_detection 特性标志时,为许多与变更检测一起使用的内部 ECS 函数添加了 changed_by 字段。使用 Location::caller() 提供函数调用的源。

使 QueryState::transmute 等验证所用 &Components 的世界 #

  • QueryState::transmuteQueryState::transmute_filteredQueryState::joinQueryState::join_filtered 现在使用 impl Into<UnsafeWorldCell> 而不是 &Components

修复涉及 read_allwrite_all 的冲突的健全性问题 #

Accessget_conflicts 方法现在返回一个 AccessConflict 枚举,而不是仅仅返回一个导致访问冲突的 ComponentId 向量。这在没有特定 ComponentId 冲突,而是 所有 ComponentId 都冲突的情况下很有用;例如 fn system(q1: Query<EntityMut>, q2: Query<EntityRef>)

在可构建系统中支持更多类型的系统参数。 #

SystemBuilder 的 API 已更改。您不再使用世界构建一个构建器,然后添加参数,而是先创建一个参数构建器元组,然后提供世界。

// Before
let system = SystemBuilder::<()>::new(&mut world)
    .local::<u64>()
    .builder::<Local<u64>>(|x| *x = 10)
    .builder::<Query<&A>>(|builder| { builder.with::<B>(); })
    .build(system);

// After
let system = (
    ParamBuilder,
    LocalBuilder(10),
    QueryParamBuilder::new(|builder| { builder.with::<B>(); }),
)
    .build_state(&mut world)
    .build_system(system);

添加查询重借 #

  • WorldQuery 现在有一个额外的 shrink_fetch 方法,如果您手动实现 WorldQuery,则必须实现此方法。

重命名 Commands::register_one_shot_system -> register_system #

Commands::register_one_shot_system 已重命名为 register_system

将 QueryFilter 设为不安全特征 #

QueryFilter 现在是一个 unsafe trait。如果您手动实现它,您需要验证 WorldQuery 实现是只读的,然后在 impl 中添加 unsafe 关键字。

EntityRef/Mut get_components(仅不可变变体) #

  • FilteredEntityRef::components 重命名为 FilteredEntityRef::accessed_componentsFilteredEntityMut::components 重命名为 FilteredEntityMut::accessed_components

从 Observer 中移除类型参数 #

如果您使用 Observer<A, B> 筛选观察者,请改为筛选 Observer

移除 Table 中的冗余信息并优化动态分配 #

Table 现在使用 ThinColumn 而不是 Column。这意味着以前返回 Column 的方法现在将返回 ThinColumn

ThinColumn 的 API 更加有限和低级,但您仍然可以在 ThinColumn 中实现与在 Column 中相同的操作。例如,您不再调用 Column::get_added_tick,而是调用 ThinColumn::get_added_ticks_slice 并对其进行索引以获取特定的已添加的刻度。

将 push children 重命名为 add children #

本节可选。如果没有任何重大更改,可以删除本节。

  • 如果此 PR 是重大更改(相对于 Bevy 的上次发布),请描述用户如何迁移其代码以支持这些更改。

push_children() 的任何使用重命名为更新的 add_children()

将 Add to Queue 重命名为具有延迟语义的方法 #

  • Commands::addCommands::push 已被 Commands::queue 替换。
  • ChildBuilder::add_command 已重命名为 ChildBuilder::queue_command

将 World::resource_ref 的返回值类型更改为 Ref #

以前 World::get_resource_ref::<T>World::resource_ref::<T> 将返回 Res<T>,这与 World 的其余 API(尤其是 resource_scope)不一致。此问题已得到修复,方法现在返回 Ref<T>

这意味着不再可能从 World 中获取 Res<T>。如果您依赖此功能,请尝试改用 Ref<T>,因为它具有相同的功能。

之前

let my_resource: Res<MyResource> = world.resource_ref();
function_taking_resource(my_resource);

fn function_taking_resource(resource: Res<MyResource>) { /* ... */ }

之后

let my_resource: Ref<MyResource> = world.resource_ref();
function_taking_resource(my_resource);

fn function_taking_resource(resource: Ref<MyResource>) { /* ... */ }

支持以引用作为输入的系统 #

  • 必须以指定的方式更改以下类型的所有当前显式用法

    • SystemId<I, O>SystemId<In<I>, O>
    • System<In = T>System<In = In<T>>
    • IntoSystem<I, O, M>IntoSystem<In<I>, O, M>
    • Condition<M, T>Condition<M, In<T>>
  • In<Trigger<E, B>> 不再是有效的输入参数类型。请改用 Trigger<E, B>

缓存 run_system 的后续操作 #

  • IntoSystem::pipeIntoSystem::map 现在返回 IntoPipeSystemIntoAdapterSystem 而不是 PipeSystemAdapterSystem。最重要的是,这些类型不实现 System,而是只实现 IntoSystem

列出 QueryEntityError::QueryDoesNotMatch 的组件 #

  • QueryEntityError 现在有了一个生命周期。如果您需要存储它,请将其转换为自定义错误。

重命名 init_component 及其朋友 #

  • World::init_component 已重命名为 register_component
  • World::init_component_with_descriptor 已重命名为 register_component_with_descriptor
  • World::init_bundle 已重命名为 register_bundle
  • Components::init_component 已重命名为 register_component
  • Components::init_component_with_descriptor 已重命名为 register_component_with_descriptor
  • Components::init_resource 已重命名为 register_resource
  • Components::init_non_send 已重命名为 register_non_send

针对观察者、系统注册表和运行一次的系统参数验证 #

  • RunSystemOnce::run_system_onceRunSystemOnce::run_system_once_with 现在返回 Result<Out> 而不是 Out

15540 将 World::flush_commands 设置为私有 #

启用 EntityRef::get_by_id 及其朋友以接受多个 ID 并获取多个指针 #

  • 以下函数现在返回 Result<_, EntityComponentError> 而不是 Option<_>EntityRef::get_by_idEntityMut::get_by_idEntityMut::into_borrow_by_idEntityMut::get_mut_by_idEntityMut::into_mut_by_idEntityWorldMut::get_by_idEntityWorldMut::into_borrow_by_idEntityWorldMut::get_mut_by_idEntityWorldMut::into_mut_by_id

将 EntityWorldMut 上的 observe 重命名为 observe_entity #

实体上的 observe() 方法已重命名为 observe_entity(),以避免在某些情况下对正在观察的内容造成混淆。

弃用 Events::oldest_id #

  • Events::oldest_id 的用法更改为 Events::oldest_event_count
  • 如果 Events::oldest_id 用于获取实际最老的 EventId::id,请注意,弃用的方法从未可靠地执行过此操作,因为缓冲区可能当前不包含任何 ID。

允许 World::entity 系列函数接受多个实体并获取多个引用 #

  • World::get_entity 现在返回 Result<_, Entity> 而不是 Option<_>

    • 使用 world.get_entity(..).ok() 返回到以前的行为。
  • World::get_entity_mutDeferredWorld::get_entity_mut 现在返回 Result<_, EntityFetchError> 而不是 Option<_>

    • 使用 world.get_entity_mut(..).ok() 返回到以前的行为。
  • World::entityWorld::entity_mutWorld::get_entityWorld::get_entity_mutDeferredWorld::entity_mutDeferredWorld::get_entity_mut 的类型推断已更改,现在可能需要在闭包中显式编写输入参数的类型。

  • 以下函数已被弃用,应替换为:

    • World::many_entities -> World::entity::<[Entity; N]>

    • World::many_entities_mut -> World::entity_mut::<[Entity; N]>

    • World::get_many_entities -> World::get_entity::<[Entity; N]>

    • World::get_many_entities_dynamic -> World::get_entity::<&[Entity]>

    • World::get_many_entities_mut -> World::get_entity_mut::<[Entity; N]>

      • 等效返回值类型已从 Result<_, QueryEntityError> 更改为 Result<_, EntityFetchError>
    • World::get_many_entities_dynamic_mut -> World::get_entity_mut::<&[Entity]>

      • 等效返回值类型已从 Result<_, QueryEntityError> 更改为 Result<_, EntityFetchError>
    • World::get_many_entities_from_set_mut -> World::get_entity_mut::<&EntityHashSet>

      • 等效返回值类型已从 Result<Vec<EntityMut>, QueryEntityError> 更改为 Result<EntityHashMap<EntityMut>, EntityFetchError>。如果需要,您仍然可以将 EntityHashMap 转换为 Vec

弃用 get_or_spawn #

如果您有一个Entity,并且想对其进行操作,请使用Commands.entity(...)World.entity(...)。如果您想生成一些东西,请使用Commands.spawn(...)World.spawn(...)。如果您不确定实体是否存在,您可以始终使用get_entity并匹配返回的Option<...>

bevy_ecs: 特殊情况Entity::PLACEHOLDER格式化 #

EntityDebugDisplay 实现现在为 Entity::PLACEHOLDER 常量返回 PLACEHOLDER。如果您有任何依赖这些值的代码,您可能需要考虑此更改。

最小冒泡观察者 #

  • Event 的手动实现应该添加关联类型 Traverse = TraverseNone 和关联常量 AUTO_PROPAGATE = false
  • Trigger::new 具有新的字段 propagation: &mut Propagation,它提供冒泡状态。
  • ObserverRunner 现在以 &mut Propagation 作为最终参数。

更改 ReflectMapEntities 以在插入之前对组件进行操作 #

  • ReflectMapEntities 的使用者需要在将值插入世界之前对其调用 map_entities
  • MapEntities 的实现者需要删除 mappings 方法,该方法不再需要用于 ReflectMapEntities 并且已从该特征中删除。

冒泡观察者遍历应该使用查询数据 #

更新 Traversal 的实现。

迁移 bevy 拾取 #

此 API 尚未发布,因此我懒得进行弃用。但是,对于跟踪主分支的任何板条箱,更改如下

以前的 api

commands.insert(PointerBundle::new(PointerId::Mouse));
commands.insert(PointerBundle::new(PointerId::Mouse).with_location(location));

新的 api

commands.insert(PointerId::Mouse);
commands.insert((PointerId::Mouse, PointerLocation::new(location)));

feat: 添加 World::get_reflect()World::get_reflect_mut() #

没有重大更改,但如果用户之前手动完成,他们可以使用新方法。

使用板条箱:disqualified #

将对 bevy_utils::ShortName 的引用替换为 disqualified::ShortName

迁移可见性到必需组件 #

将所有插入 VisibilityBundle 替换为 Visibility 组件。它需要的其他组件现在将自动插入。

迁移雾体积到必需组件 #

将所有插入 FogVolumeBundle 替换为 Visibility 组件。它需要的其他组件现在将自动插入。

迁移网格和材质到必需组件 #

网格和网格材质的资产句柄现在必须包装在 Mesh2dMeshMaterial2dMesh3dMeshMaterial3d 组件中,分别用于 2D 和 3D。作为组件的原始句柄不再渲染网格。

此外,MaterialMesh2dBundleMaterialMeshBundlePbrBundle 已被弃用。而是直接使用网格和材质组件。

以前

commands.spawn(MaterialMesh2dBundle {
    mesh: meshes.add(Circle::new(100.0)).into(),
    material: materials.add(Color::srgb(7.5, 0.0, 7.5)),
    transform: Transform::from_translation(Vec3::new(-200., 0., 0.)),
    ..default()
});

现在

commands.spawn((
    Mesh2d(meshes.add(Circle::new(100.0))),
    MeshMaterial2d(materials.add(Color::srgb(7.5, 0.0, 7.5))),
    Transform::from_translation(Vec3::new(-200., 0., 0.)),
));

如果网格材质缺失,现在将使用白色默认材质。以前,如果材质缺失,则不会渲染任何内容。

WithMesh2dWithMesh3d 查询过滤器类型别名也被删除了。只需使用 With<Mesh2d>With<Mesh3d>

迁移运动模糊、TAA、SSAO 和 SSR 到必需组件 #

MotionBlurBundleTemporalAntiAliasBundleScreenSpaceAmbientOcclusionBundleScreenSpaceReflectionsBundle 已被弃用,取而代之的是 MotionBlurTemporalAntiAliasingScreenSpaceAmbientOcclusionScreenSpaceReflections 组件。插入它们现在也将自动插入它们需要的其他组件。

迁移相机到必需组件 #

Camera2dBundleCamera3dBundle 已被弃用,取而代之的是 Camera2dCamera3d。插入它们现在也将自动插入它们需要的其他组件。

将删除的组件与渲染世界同步 #

保留的渲染世界注释应更新以解释此边缘情况和 SyncComponentPlugin

迁移反射探测到必需组件 #

ReflectionProbeBundle 已被弃用,取而代之的是直接插入 LightProbeEnvironmentMapLight 组件。插入它们现在将自动插入 TransformVisibility 组件。

迁移 bevy_transform 到必需组件 #

将所有插入 GlobalTransform 和/或 TransformBundle 替换为仅 Transform

弃用 SpatialBundle #

SpatialBundle 现在已被弃用,请改而插入 TransformVisibility,它们将自动插入捆绑包中所有其他组件。如果您没有指定这些值,并且您在 spawn/insert 调用中使用的任何其他组件都需要这两个组件中的任何一个,则可以省略该组件。

之前

commands.spawn(SpatialBundle::default());

之后

commands.spawn((Transform::default(), Visibility::default());

Gizmos #

使 bevy_render 成为 bevy_gizmos 的可选依赖项 #

用户无需进行任何用户可见的更改。

Wireframe2dWireframe 之间的 consistency #

  • Wireframe2dConfig.default_color 类型现在为 Color 而不是 Srgba。使用 .into() 在它们之间进行转换。
  • Wireframe2dColor.color 类型现在为 Color 而不是 Srgba。使用 .into() 在它们之间进行转换。

修复 Gizmos 在选择一部分功能时出现的警告和文档错误 #

不应该有迁移的理由,但如果出于某种原因您使用 GizmoMeshConfigbevy_render 但不使用 bevy_pbrbevy_sprite(因此它什么都不做),那么您将收到一个错误,提示它不再存在。

修复 arc_2d Gizmos #

  • 用户必须调整他们对 arc_2d 的使用方式
    • 之前
arc_2d(
  pos,
  angle,
  arc_angle,
  radius,
  color
)
  • 之后
arc_2d(
  // this `+ arc_angle * 0.5` quirk is only if you want to preserve the previous behavior 
  // with the new API.
  // feel free to try to fix this though since your current calls to this function most likely
  // involve some computations to counter-act that quirk in the first place
  Isometry2d::new(pos, Rot2::radians(angle + arc_angle * 0.5),
  arc_angle,
  radius,
  color
)

bevy_gizmos 中尽可能使用 Isometry #

Gizmos 方法的功能签名更改如下

  • 2D

    • 如果它之前接受 positionrotation_angle -> Isometry2d::new(position, Rot2::radians(rotation_angle))
    • 如果它之前只接受 position -> Isometry2d::from_translation(position)
  • 3D

    • 如果它之前接受 positionrotation -> Isometry3d::new(position, rotation)
    • 如果它之前只接受 position -> Isometry3d::from_translation(position)

改进 Plane3d 的 Gizmo,重用网格 #

可选的构建器方法在


gizmos.primitive_3d(&Plane3d { }, ...);

  • segment_length
  • segment_count
  • axis_count

  • cell_count
  • spacing

在网格 Gizmo 中切换旋转和平移 #

  • 用户可能需要仔细检查他们对所有 grid 方法的现有调用。不过现在应该更直观了。

使 TrackedRenderPass::set_vertex_buffer 了解切片大小 #

  • TrackedRenderPass::set_vertex_buffer 函数已修改,以便在提供相同偏移量的相同缓冲区但大小已更改时更新顶点缓冲区。一些现有代码可能依赖于之前的行为,该行为在此情况下不会更新顶点缓冲区。

层次结构 #

优化变换传播 #

此更改不会引入任何重大更改。Bevy 引擎的用户将自动从这种性能改进中受益,而无需修改其代码。

仅对具有 GlobalTransforms 的实体传播变换。 #

  • 为了避免意外的性能陷阱,Transform / GlobalTransform 传播不再在中间父级缺少 GlobalTransform 的层次结构中向下执行。要恢复之前的行为,请将 GlobalTransform::default 添加到中间实体。

输入 #

删除 ReceivedCharacter #

由于 winit 重构了他们的键盘系统,ReceivedCharacter 在 0.14 中被弃用。它现在已被完全删除。改为使用 KeyboardInput

// 0.14
fn listen_characters(events: EventReader<ReceivedCharacter>) {
    for event in events.read() {
        info!("{}", event.char);
    }
}

// 0.15
fn listen_characters(events: EventReader<KeyboardInput>) {
    for event in events.read() {
        // Only check for characters when the key is pressed.
        if !event.state.is_pressed() {
            continue;
        }

        // Note that some keys such as `Space` and `Tab` won't be detected as a character.
        // Instead, check for them as separate enum variants.
        match &event.logical_key {
            Key::Character(character) => {
                info!("{} pressed.", character);
            },
            Key::Space => {
                info!("Space pressed.");
            },
            _ => {},
        }
    }
}

将游戏手柄实现为实体 #

游戏手柄输入不再使用资源访问,而是实体,只要游戏手柄连接,就可以使用 Gamepad 组件访问它们。

游戏手柄资源已被删除,不再使用内部 ID 来识别游戏手柄,您可以使用其 Entity。断开连接的游戏手柄将**不会**被销毁。不需要保留其状态的游戏手柄组件将被删除,即 Gamepad 组件被删除,但 GamepadSettings 会保留。重新连接的游戏手柄将尝试保留其 Entity ID,并将重新插入必要的组件。

GamepadSettings 不再是资源,而是附加到 Gamepad 实体的组件。

, 轴和 ButtonInput方法可通过 Gamepad 组件访问。

fn gamepad_system(
-   gamepads: Res<Gamepads>,
-   button_inputs: Res<ButtonInput<GamepadButton>>,
-   button_axes: Res<Axis<GamepadButton>>,
-   axes: Res<Axis<GamepadAxis>>,
+   gamepads: Query<&Gamepad>
) {
    for gamepad in gamepads.iter() {
-      if button_inputs.just_pressed(GamepadButton::new(gamepad, GamepadButtonType::South)) {
+      if gamepad.just_pressed(GamepadButton::South) {
            println!("just pressed South");
        } 
         
-      let right_trigger = button_axes
-           .get(GamepadButton::new(
-               gamepad,
-               GamepadButtonType::RightTrigger2,
-           ))
-           .unwrap();
+      let right_trigger = gamepad.get(GamepadButton::RightTrigger2).unwrap();
        if right_trigger.abs() > 0.01 {
            info!("RightTrigger2 value is {}", right_trigger);      
        }

-        let left_stick_x = axes
-           .get(GamepadAxis::new(gamepad, GamepadAxisType::LeftStickX))
-           .unwrap();
+       let left_stick_x = gamepad.get(GamepadAxis::LeftStickX).unwrap();
        if left_stick_x.abs() > 0.01 {
            info!("LeftStickX value is {}", left_stick_x);        
        }
    }
}

拾取事件排序 #

对于从 bevy_mod_picking 切换到 bevy_picking 的用户

  • 不要添加 On<T> 组件,请使用 .observe(|trigger: Trigger<T>|)。您现在可以使用此命令对同一实体应用多个处理程序。
  • 指针交互事件现在具有半确定性排序,该排序(或多或少)与原始输入流的顺序一致。请参阅 bevy_picking::event::pointer_events 上的文档以获取当前信息。您可能需要相应地调整您的事件处理逻辑。
  • PointerCancel 已被 Pointer<Canceled> 替换,它现在具有 OS 触摸指针取消事件的语义。
  • InputMoveInputPress 已合并为 PointerInput。使用方式完全相同。
  • 拾取交互事件现在只能通过观察者访问,而不能通过 EventReader 访问。此功能可能会在以后重新实现。

对于 bevy_winit 的用户

  • 事件 bevy_winit::WinitEvent 已移至 bevy_window::WindowEvent。如果这是您依赖 bevy_winit 的唯一原因,您应该将依赖项切换到 bevy_window
  • bevy_window 现在依赖于 bevy_inputbevy_input 的依赖项是 bevy_window 现有依赖项的子集,因此这应该是非侵入性的。

数学 #

向 Cone 3D 原语添加了 new 方法 #

  • 向 3D 原语 Cone 结构添加了 new 方法。

禁止空三次和有理曲线 #

Bevy 的三次样条曲线上的 to_curve 方法现在是易错的(返回 Result),这意味着任何现有的调用都需要通过处理错误变体的可能性来更新。

类似地,任何 CubicGeneratorRationalGenerator 的自定义实现都需要修改为包含 Error 类型并使其自身易错。

最后,CubicCurveRationalCurve 的字段现在是私有的,因此任何从段直接构建这些结构体都需要替换为新的 CubicCurve::from_segmentsRationalCurve::from_segments 方法。

重构 Bounded2d/Bounded3d 以使用等距 #

Bounded2dBounded3d 特性现在分别接受 Isometry2dIsometry3d 参数,而不是单独的平移和旋转参数。对 aabb_2dbounding_circleaabb_3dbounding_sphere 的现有调用将必须更改为使用等距。一个简单的转换是通过调用 Isometry2d/3d::new 来重构,如下所示

// Old:
let aabb = my_shape.aabb_2d(my_translation, my_rotation);

// New:
let aabb = my_shape.aabb_2d(Isometry2d::new(my_translation, my_rotation));

但是,如果旧的平移和旋转是源自 TransformGlobalTransform 的 3d 平移/旋转,则可以使用 to_isometry。例如

// Old:
let bounding_sphere = my_shape.bounding_sphere(shape_transform.translation, shape_transform.rotation);

// New:
let bounding_sphere = my_shape.bounding_sphere(shape_transform.to_isometry());

此讨论也适用于 Aabb2d/BoundingCircle/Aabb3d/BoundingSpherefrom_point_cloud 构造方法,该方法也类似地被更改为使用等距。

将三次样条曲线与 Curve API 基本集成 #

RationalCurve::domain 方法已重命名为 RationalCurve::length。在 RationalCurve 上调用 .domain() 现在会将其整个域作为 Interval 返回。

对于 Ray2d::new/Ray3d::new 使用 Dir2/Dir3 而不是 Vec2/Vec3 #

Ray2d::newRay3d::new 现在分别接受 Dir2Dir3 而不是 Vec2Vec3 来表示射线方向。

bevy_reflect:更新 EulerRot 以匹配 glam 0.29 #

EulerRot 的反射实现已更新以与 glam 0.29 对齐。请相应地更新所有基于反射的使用方法。

拾取 #

将 Drop 重命名为 bevy::picking::events::DragDrop 以避免与 std::ops:Drop 冲突 #

  • Drop 重命名为 DragDrop
    • bevy::picking::events::Drop 现在是 bevy::picking::events::DragDrop

反射 #

bevy_reflect:嵌套的 TypeInfo 获取器 #

所有反射类型的活动字段(包括列表、映射、元组等)必须实现 Typed。对于大多数用户来说,这将没有任何可见影响。

但是,手动实现 Reflect 的用户可能需要更新其类型以实现 Typed,如果他们还没有实现的话。

此外,自定义动态类型将需要实现新的隐藏 MaybeTyped 特性。

为动态类型实现 FromIterator/IntoIterator #

  • DynamicArray::from_vec 更改为 DynamicArray::from_iter

为类似 Set 的事物提供专用的 Reflect 实现 #

  • 更改部分中列出的枚举上的新 Set 变体可能应该被使用此级别库的人员考虑。

需要帮助!

我不确定此更改是否能够破坏代码。据我了解,它不应该破坏代码,因为我们只是添加了功能,但我还不知道我的实现中是否缺少任何由 impl_reflect_value! 通常提供的功能。

bevy_reflect:添加 Type 类型 #

某些类型信息结构体现在只将其项目类型作为 Type 返回,而不是公开它们上的直接方法。

以下方法已被删除

  • ArrayInfo::item_type_path_table
  • ArrayInfo::item_type_id
  • ArrayInfo::item_is
  • ListInfo::item_type_path_table
  • ListInfo::item_type_id
  • ListInfo::item_is
  • SetInfo::value_type_path_table
  • SetInfo::value_type_id
  • SetInfo::value_is
  • MapInfo::key_type_path_table
  • MapInfo::key_type_id
  • MapInfo::key_is
  • MapInfo::value_type_path_table
  • MapInfo::value_type_id
  • MapInfo::value_is

相反,使用以下新方法之一直接访问 Type

  • ArrayInfo::item_ty
  • ListInfo::item_ty
  • SetInfo::value_ty
  • MapInfo::key_ty
  • MapInfo::value_ty

例如

// BEFORE
let type_id = array_info.item_type_id();

// AFTER
let type_id = array_info.item_ty().id();

bevy_reflect:重构 serde 模块 #

ReflectSerializerTypedReflectSerializer 上的字段现在是私有的。要实例化,必须使用相应的构造函数。

// BEFORE
let serializer = ReflectSerializer {
    value: &my_value,
    registry: &type_registry,
};

// AFTER
let serializer = ReflectSerializer::new(&my_value, &type_registry);

此外,以下类型不再公开

  • ArraySerializer
  • EnumSerializer
  • ListSerializer
  • MapSerializer
  • ReflectValueSerializer(完全删除)
  • StructSerializer
  • TupleSerializer
  • TupleStructSerializer

以及以下特性

  • DeserializeValue(完全删除)

bevy_reflect:添加 DynamicTyped 特性 #

Reflect 现在具有一个 DynamicTyped 的超特性。如果您手动实现了 Reflect 并且没有实现 Typed,则现在需要这样做。

bevy_reflect:用“opaque”替换“value”术语 #

反射概念“值类型”已被更清晰的“不透明类型”取代。以下重命名已进行调整

  • ReflectKind::ValueReflectKind::Opaque
  • ReflectRef::ValueReflectRef::Opaque
  • ReflectMut::ValueReflectMut::Opaque
  • ReflectOwned::ValueReflectOwned::Opaque
  • TypeInfo::ValueTypeInfo::Opaque
  • ValueInfoOpaqueInfo
  • impl_reflect_value!impl_reflect_opaque!
  • impl_from_reflect_value!impl_from_reflect_opaque!

此外,声明您自己的不透明类型不再使用 #[reflect_value]。此属性已被 #[reflect(opaque)] 替换。

// BEFORE
#[derive(Reflect)]
#[reflect_value(Default)]
struct MyOpaqueType(u32);

// AFTER
#[derive(Reflect)]
#[reflect(opaque)]
#[reflect(Default)]
struct MyOpaqueType(u32);

请注意,#[reflect(opaque)] 出现的顺序无关紧要。

删除 Return::Unit 变体 #

  • 删除了 Return::Unit 变体;使用 Return::unit() 代替。

对于反射的 MapListSet,使 drain 接受可变借用而不是 Box<Self>#

  • reflect::Mapreflect::Listreflect::Set 现在都接受 &mut self 而不是 Box<Self>。这些特性的调用者应该在它们的框之前添加 &mut,而这些特性的实现者应该更新以匹配。

将具有一个字段的元组结构序列化和反序列化为新类型结构体 #

  • 反射现在将具有单个字段的元组结构序列化和反序列化为新类型结构体。考虑以下代码。
#[derive(Reflect, Serialize)]
struct Test(usize);
let reflect = Test(3);
let serializer = TypedReflectSerializer::new(reflect.as_partial_reflect(), &registry);
return serde_json::to_string(&serializer)

旧行为将返回 ["3"]。新行为将返回 "3"。如果您依赖于旧行为,则需要更新您的逻辑。特别是使用 serde_jsonron 不会受到影响。

在提取动态场景中的实体时使用 FromReflect #

DynamicScene 格式已更改为使用自定义序列化实现,因此需要更新旧的场景文件。

(
  resources: {},
  entities: {
    4294967299: (
      components: {
        "bevy_render::camera::projection::OrthographicProjection": (
          near: 0.0,
          far: 1000.0,
          viewport_origin: (
            x: 0.5,
            y: 0.5,
          ),
          scaling_mode: WindowSize(1.0),
          scale: 1.0,
          area: (
            min: (
              x: -1.0,
              y: -1.0,
            ),
            max: (
              x: 1.0,
              y: 1.0,
            ),
          ),
        ),
      },
    ),
  },
)

(
  resources: {},
  entities: {
    4294967299: (
      components: {
        "bevy_render::camera::projection::OrthographicProjection": (
          near: 0.0,
          far: 1000.0,
          viewport_origin: (0.5, 0.5),
          scaling_mode: WindowSize(1.0),
          scale: 1.0,
          area: (
            min: (-1.0, -1.0),
            max: (1.0, 1.0),
          ),
        ),
      },
    ),
  },
)

将 ShortName 移动到 bevy_reflect #

  • bevy_utils::ShortName 的引用现在应该改为 bevy_reflect::ShortName

渲染 #

照明应该只保留 Vec而不是 TypeId<Vec> #

现在 SpotLightBundleCascadesVisibleEntitiesCubemapVisibleEntities 使用 VisibleMeshEntities 而不是 VisibleEntities

添加对天空盒变换的支持 #

  • 由于我们在 Skybox 结构体中添加了一个新字段,因此用户需要在初始化代码中包含 ..Default::default() 或一些旋转值。

允许体积雾气定位到特定(可选地进行体素化的)区域。 #

  • 为了启用体积雾气,现在除了相机上的 VolumetricFogSettings 之外,还需要一个 FogVolume。现有体积雾气的使用可以通过放置一个围绕场景的大型 FogVolume 来迁移。

将多个顶点和索引数组打包到可增长的缓冲区中。 #

已更改

  • 网格的顶点和索引缓冲区现在可以与其他缓冲区一起打包,以提高性能。
  • GpuMesh 已重命名为 RenderMesh,以反映它不再直接存储对 GPU 对象的句柄。
  • 由于网格不再拥有自己的顶点和索引缓冲区,因此缓冲区的责任已从 GpuMesh(现在称为 RenderMesh)转移到 MeshAllocator 资源。要访问网格的顶点数据,请使用 MeshAllocator::mesh_vertex_slice。要访问网格的索引数据,请使用 MeshAllocator::mesh_index_slice

添加对环境贴图变换的支持 #

  • 由于我们在 EnvironmentMapLight 结构体中添加了一个新字段,因此用户需要在初始化代码中包含 ..default() 或一些旋转值。

使用 Cas 而不是 CAS #14341 #

Msaa 移动到组件 #

Msaa 不再配置为全局资源,如果需要非默认设置,应在每个生成的相机上指定。

添加具有深度缓冲区的 2d 不透明阶段 #

  • ColorMaterial 现在包含 AlphaMode2d。要保留以前的行为,请使用 AlphaMode::BLEND。如果您知道您的精灵是不透明的,请使用 AlphaMode::OPAQUE

已将 Mesh::attributes* 函数更改为返回 MeshVertexAttribute #

  • 使用 Mesh::attributesMesh::attributes_mut 返回的迭代器时,元组的第一个值不是 MeshVertexAttribute 而是 MeshVertexAttributeId。要访问 MeshVertexAttributeId,请使用 MeshVertexAttribute.id 字段。

World::clear_entities 添加 RenderSet::FinalCleanup #

World::clear_entities 现在是 RenderSet::PostCleanup 的一部分,而不是 RenderSet::Cleanup。您的清理系统应该可能保留在 RenderSet::Cleanup 中。

在图像加载文档中添加功能要求信息 #

与图像格式相关的实体是功能门控的,如果存在关于未知名称的编译错误,则应添加列表中的某些功能(exrhdrbasis-universalpngddstgajpegbmpktx2webppnm)。

修复 InitTriInfo 中的下溢恐慌 #

  • 没有引入重大更改。

重写屏幕截图。 #

ScreenshotManager 已被移除。要截取屏幕截图,请使用指定的渲染目标生成 Screenshot 实体,并提供一个目标为 ScreenshotCaptured 事件的观察者。查看 window/screenshot 示例以了解示例。

wgpu_trace 功能替换为 bevy_render::settings::WgpuSettings 中的一个字段 #

  • bevy/wgpu_tracebevy_render/wgpu_tracebevy_internal/wgpu_trace 功能不再存在。从您的 Cargo.toml、CI、工具和其它内容中删除它们。
  • 请按照存储库中更新的 docs/debugging.md 文件(在 WGPU 跟踪部分)中的说明操作。

由于进行的更改,您现在可以将跟踪生成到任何路径,而不是硬编码的 %WorkspaceRoot%/wgpu_trace(其中 %WorkspaceRoot% 是…您的板条箱工作区的根目录)文件夹。

(如果 WGPU 尚未恢复跟踪功能…)请注意,WGPU 尚未恢复跟踪功能。但是,一旦恢复,上述操作应足以生成新的跟踪。

重构 AsBindGroup 以使用关联的 SystemParam#

AsBindGroup 现在允许用户指定一个 SystemParam 来用于创建绑定组。

Meshlet 软件光栅化 + 清理开始 #

  • 待定(请在发布结束时询问我关于 meshlet 的整体更改)

添加 ShaderStorageBuffer 资产 #

AsBindGroupstorage 属性已修改为引用新的 Handle<Storage> 资产。应将 Vec` 的用法转换为资产。

Camera 的世界/视口转换方法返回 Result #

Camera 上的以下方法现在返回 Result 而不是 Option,以便它们可以提供有关失败的更多信息

  • world_to_viewport
  • world_to_viewport_with_depth
  • viewport_to_world
  • viewport_to_world_2d

Result 上调用 .ok() 以将其转换回 Option,或直接处理 Result

用默认值替换隐式发射权重。 #

将 OrthographicProjection::default 分割为 2d 和 3d(已采纳) #

  • OrthographicProjection 的初始化中,将 ..default() 更改为 ..OrthographicProjection::default_2d()..OrthographicProjection::default_3d()

示例

--- a/examples/3d/orthographic.rs
+++ b/examples/3d/orthographic.rs
@@ -20,7 +20,7 @@ fn setup(
         projection: OrthographicProjection {
             scale: 3.0,
             scaling_mode: ScalingMode::FixedVertical(2.0),
-            ..default()
+            ..OrthographicProjection::default_3d()
         }
         .into(),
         transform: Transform::from_xyz(5.0, 5.0, 5.0).looking_at(Vec3::ZERO, Vec3::Y),

移除 OrthographicProjection.scale(已采纳) #

scaling_mode 替换所有对 scale 的使用,请记住 scale 是(曾经是)一个乘数。例如,替换

    scale: 2.0,
    scaling_mode: ScalingMode::FixedHorizontal(4.0),

    scaling_mode: ScalingMode::FixedHorizontal(8.0),

为了提高一致性和清晰度,重命名渲染组件 #

许多渲染组件已重命名,以提高一致性和清晰度。

  • AutoExposureSettingsAutoExposure
  • BloomSettingsBloom
  • BloomPrefilterSettingsBloomPrefilter
  • ContrastAdaptiveSharpeningSettingsContrastAdaptiveSharpening
  • DepthOfFieldSettingsDepthOfField
  • FogSettingsDistanceFog
  • SmaaSettingsSmaa
  • TemporalAntiAliasSettingsTemporalAntiAliasing
  • ScreenSpaceAmbientOcclusionSettingsScreenSpaceAmbientOcclusion
  • ScreenSpaceReflectionsSettingsScreenSpaceReflections
  • VolumetricFogSettingsVolumetricFog

将灯光迁移到所需组件 #

PointLightBundleSpotLightBundleDirectionalLightBundle 已被弃用。请使用 PointLightSpotLightDirectionalLight 组件。添加它们现在将自动插入它们所需的其它组件。

修复网格分配器错误,并将网格数据复制减少两次 #

  • Mesh::get_vertex_buffer_data 已重命名为 Mesh::create_packed_vertex_buffer_data,以反映它复制数据并分配的事实。

添加可见性位掩码作为一种可选的 SSAO 方法 #

SSAO 算法已从 GTAO 更改为 VBAO(可见性位掩码)。ScreenSpaceAmbientOcclusion 添加了一个新字段 constant_object_thicknessScreenSpaceAmbientOcclusion 还失去了其 EqHash 实现。

将 bevy_mesh 从 bevy_render 中分离出来 #

bevy_render::mesh::morph::inherit_weights 现在是 bevy_render::mesh::inherit_weights

如果您之前使用过 Mesh::compute_aabb,则现在需要 use bevy_render::mesh::MeshAabb;

对所有图像格式进行功能门控 #

以前未进行功能门控的图像格式现在已进行功能门控,这意味着如果您使用它们,则必须启用它们

  • avif
  • ff (Farbfeld)
  • gif
  • ico
  • tiff

此外,已添加 qoi 功能以支持加载 QOI 格式的图像。

以前,这些格式默认出现在枚举中,但实际上并未通过 image 板条箱启用,这可能会导致奇怪的错误。现在,您应该能够将这些功能添加到您的项目中以适当地支持它们。 #

如果您之前单独配置了 bevy_render 板条箱,则一般图像格式的功能标志已移至 bevy_image。例如,bevy_render/png 不再存在,bevy_image/png 是它的新位置。纹理格式仍然在 bevy_render 上可用,例如,bevy_render/ktx2 是完全启用 ktx2 支持所必需的,这将自动启用 bevy_image/ktx2 以加载纹理。

每个 meshlet 的压缩顶点数据 #

  • 待 JMS55 在发布结束时确定

将 bevy_sprite 迁移到所需组件 #

Sprite 替换所有对 SpriteBundle 的使用。有几个新的便利构造函数:Sprite::from_imageSprite::from_atlas_imageSprite::from_color

警告:在精灵实体上使用 Handle<Image>TextureAtlas 作为组件将不再起作用。请改用 Sprite 上的字段。除了它仍在 ui 中使用,我本想从 TextureAtlasHandle<Image> 中删除 Component 实现。我们应该在迁移过程中修复这个问题。

类型安全的保留渲染世界 #

随着保留渲染世界的出现,包含对提取到渲染世界中的 Entity 的引用的集合已更改为包含 MainEntity,以防止出现错误,即渲染世界实体 ID 被意外地用于查找项目。自定义渲染代码可能需要更改为查询 &MainEntity 以便从这样的集合中查找正确的项目。此外,为包含主世界实体的集合实现自己的提取逻辑的用户应该认真考虑提取到使用 MainEntity 作为键的另一个集合中。

此外,渲染阶段现在需要为给定的 PhaseItem 指定 EntityMainEntity。自定义渲染阶段应确保在排队阶段项目时 MainEntity 可用。

ImageLoaderCompressedImageSaver 移动到 bevy_image#

  • ImageLoader 不再可以通过 init_asset_loader 直接初始化。现在您必须使用 app.register_asset_loader(ImageLoader::new(supported_compressed_formats))(查看 bevy_render::ImagePlugin 的实现)。这只会影响您手动初始化加载器的情况,不会影响 bevy_render::ImagePlugin 的用户。

如果未提取,尝试从渲染世界中删除组件。 #

实现 ExtractComponent 并返回 None 的组件将导致提取的组件从渲染世界中删除。

改进正交相机缩放的 API #

ScalingMode 已重构以提高清晰度,尤其是在如何缩放正交相机及其投影方面

  • ScalingMode::WindowSize 不再存储浮点数,并充当其值为 1 的情况。将相机的比例除以任何先前值以实现相同的结果。
  • ScalingMode::FixedVerticalFixedHorizontal 现在使用命名字段。

修复具有偏移量的 UI 纹理图集 #

let ui_node = ExtractedUiNode {
                    stack_index,
                    transform,
                    color,
                    rect,
                    image,
-                   atlas_size: Some(atlas_size * scale_factor),      
+                   atlas_scaling: Some(Vec2::splat(scale_factor)),
                    clip,
                    flip_x,
                    flip_y,
                    camera_entity,
                    border,
                    border_radius,
                    node_type,
                },
let computed_slices = ComputedTextureSlices {
    slices,
-    image_size,
}

使用预计算的边框值 #

Node 中的 logical_rectphysical_rect 方法已删除。请改用 Rect::from_center_size,并使用平移和节点大小。

ExtractedUiNode 的字段边框和边框半径的类型分别更改为 BorderRectResolvedBorderRadius

反转 bevy_render 对 bevy_winit 的依赖关系,并将光标移动到 bevy_winit #

bevy::render::view::cursor 之前提供的 CursorIconCustomCursor 现在可从 bevy::winit 获取。新功能 custom_cursor 启用了此功能(默认功能)。

场景 #

在生成任何类型的场景时发送 SceneInstanceReady #

  • SceneInstanceReady { parent: Entity } 现在是 SceneInstanceReady { id: InstanceId, parent: Option<Entity> }

使 Scene::write_to_world_withDynamicScene::write_to_world_with 保持一致 #

Scene::write_to_world_with 不再返回 InstanceInfo

之前

scene.write_to_world_with(world, &registry)

之后

let mut entity_map = EntityHashMap::default();
scene.write_to_world_with(world, &mut entity_map, &registry)

使 Scene::write_to_world_withDynamicScene::write_to_world_with 保持一致 #

Scene::write_to_world_with 不再返回 InstanceInfo

之前

scene.write_to_world_with(world, &registry)

之后

let mut entity_map = EntityHashMap::default();
scene.write_to_world_with(world, &mut entity_map, &registry)

更改 SceneInstanceReady 以触发观察者。 #

如果您有一个读取 SceneInstanceReady 事件的系统

fn ready_system(ready_events: EventReader<'_, '_, SceneInstanceReady>) {

它必须重写为观察者

commands.observe(|trigger: Trigger<SceneInstanceReady>| {

或者,如果您希望在与特定实体或实体相关的事件中使用它,作为实体观察者

commands.entity(entity).observe(|trigger: Trigger<SceneInstanceReady>| {

DynamicSceneBuilder 上的方法中明确提及 component #

DynamicSceneBuilder::allow_alldeny_all 现在设置资源访问,而不仅仅是组件。要恢复到以前的行为,请使用新的 allow_all_componentsdeny_all_components 方法。

以下 DynamicSceneBuilder 方法已重命名

  • with_filter -> with_component_filter
  • allow -> allow_component
  • deny -> deny_component

将场景迁移到必需的组件 #

场景和动态场景的资源句柄现在必须包装在 SceneRootDynamicSceneRoot 组件中。作为组件的原始句柄不再生成场景。

此外,SceneBundleDynamicSceneBundle 已弃用。请改用场景组件。

以前

let model_scene = asset_server.load(GltfAssetLabel::Scene(0).from_asset("model.gltf"));

commands.spawn(SceneBundle {
    scene: model_scene,
    transform: Transform::from_xyz(-4.0, 0.0, -3.0),
    ..default()
});

现在

let model_scene = asset_server.load(GltfAssetLabel::Scene(0).from_asset("model.gltf"));

commands.spawn((
    SceneRoot(model_scene),
    Transform::from_xyz(-4.0, 0.0, -3.0),
));

文本 #

文本重构 #

TODO:非常重大更改

通过索引访问文本跨度

文本部分现在是不同实体上的文本部分,位于层次结构中。使用新的 TextReaderTextWriter 系统参数通过索引访问跨度。

之前

fn refresh_text(mut query: Query<&mut Text, With<TimeText>>, time: Res<Time>) {
    let text = query.single_mut();
    text.sections[1].value = format_time(time.elapsed());
}

之后

fn refresh_text(
    query: Query<Entity, With<TimeText>>,
    mut writer: UiTextWriter,
    time: Res<Time>
) {
    let entity = query.single();
    *writer.text(entity, 1) = format_time(time.elapsed());
}

迭代文本跨度

文本跨度现在是层次结构中的实体,因此新的 UiTextReaderUiTextWriter 系统参数提供了迭代该层次结构的方法。UiTextReader::iter 方法将为您提供跨度的普通迭代器,UiTextWriter::for_each 允许您访问每个跨度。

拆分 TextStyle #

TextStyle 已重命名为 TextFont,其 color 字段已移至名为 TextColor 的单独组件,该组件对 Color 进行重新类型化。

文本重构清理 #

兼作 #15591 迁移指南。

文本捆绑包(TextBundleText2dBundle)已删除,改为使用 TextText2d。共享配置字段已替换为 TextLayoutTextFontTextColor 组件。只有 TextBundle 的附加字段变成了 TextNodeFlags 组件,而 Text2dBundle 的附加字段变成了 TextBoundsAnchor 组件。

文本部分已删除,改为使用基于层次结构的方法。对于具有 TextText2d 组件的根文本实体,具有 TextSpan 的子实体将充当附加文本部分。要通过索引访问文本跨度,请使用新的 TextUiReaderText2dReaderTextUiWriterText2dWriter 系统参数。

添加控制字体平滑度的功能 #

  • Text 现在包含一个 font_smoothing: FontSmoothing 属性,请确保包含它或在直接使用该结构时添加 ..default()
  • FontSizeKey 已重命名为 FontAtlasKey,现在还包含 FontSmoothing 设置;
  • 以下方法现在采用额外的 font_smoothing: FontSmoothing 参数
    • FontAtlas::new()
    • FontAtlasSet::add_glyph_to_atlas()
    • FontAtlasSet::get_glyph_atlas_info()
    • FontAtlasSet::get_outlined_glyph_texture()

时间 #

对齐 Time、Timer 和 Stopwatch 的公共 API #

TimeTimerStopwatch 的 API 已清理,以确保它们彼此一致,并与标准库的 Duration 类型一致。以下方法已重命名

  • Stowatch::paused -> Stopwatch::is_paused
  • Time::elapsed_seconds -> Time::elapsed_secs(包括 _f64_wrapped 变体)

UI #

清理 UiSystem 系统集 #

UiSystem 系统集调整。

  • UiSystem::Outline 系统集现在严格位于 UiSystem::Layout 之后,而不是与它重叠。

修复在没有 bevy_text 的情况下构建 bevy_ui 时出现的错误 #

对于从 0.14 迁移的用户来说,这不是重大更改,因为当时 MeasureArgs 不存在。

当为 bevy_ui 禁用 bevy_text 功能时,MeasureArgs::font_system 字段的类型现在是 PhantomData,而不是完全删除。这样做是为了保留生命周期参数,即使它在没有启用文本的情况下未使用。

删除无用的 Direction 字段 #

Style 不再具有 direction 字段,Direction 已删除。它们没有任何作用,因此您也可以删除对它们的任何引用。

将 BreakLineOn 重命名为 LineBreak #

BreakLineOn 已重命名为 LineBreak,名为 linebreak_behavior 的参数已重命名为 linebreak

添加 UI GhostNode #

以前依赖 Parent/Children 来迭代 UI 子节点的任何代码现在可能希望使用 bevy_ui::UiChildren 来确保跳过幽灵节点,并包含其第一个后代节点。

UI 根节点现在可以是幽灵节点的子节点,这意味着 Without<Parent> 可能不会查询所有根节点。在需要的地方使用 bevy_ui::UiRootNodes 来迭代根节点。

UiMaterialHandle 包装器替换 Handle<M: UiMaterial> 组件 #

我们将迁移指南推迟到必需的组件端口。我只想暂时删除 Handle 上的 Component 实现 :)

剪切到 UI 节点的內容框 #

迁移指南在 #15561 上

溢出剪切边距 #

Style 具有一个新的字段 OverflowClipMargin。它允许用户在使用溢出剪切、隐藏或滚动并将内容扩展为边距时设置剪切内容的可见区域。

有三个关联的构造函数 content_boxpadding_boxborder_box

  • content_box:在节点的內容框区域(节点的最内部分,不包括填充和边框)之外绘制的元素将被剪切。这是新的默认行为。
  • padding_box:在节点的填充区域之外绘制的元素将被剪切。
  • border_box:在节点边界之外绘制的元素将被剪切。这与 Bevy 0.14 的行为一致。

还有一个 with_margin 方法,它以逻辑像素为单位增加可见区域的大小,负边距值被钳制为零。

除非在 UI 节点的至少一个轴上也设置了溢出剪切、隐藏或滚动,否则 OverflowClipMargin 将被忽略。

将 UI 捆绑包迁移到必需的组件 #

  • 将所有使用 NodeBundle 的地方替换为 Node。例如:
     commands
-        .spawn(NodeBundle {
-            style: Style {
+        .spawn((
+            Node::default(),
+            Style {
                 width: Val::Percent(100.),
                 align_items: AlignItems::Center,
                 justify_content: JustifyContent::Center,
                 ..default()
             },
-            ..default()
-        })
+        ))
  • 将所有使用 ButtonBundle 的地方替换为 Button。例如:
                     .spawn((
-                        ButtonBundle {
-                            style: Style {
-                                width: Val::Px(w),
-                                height: Val::Px(h),
-                                // horizontally center child text
-                                justify_content: JustifyContent::Center,
-                                // vertically center child text
-                                align_items: AlignItems::Center,
-                                margin: UiRect::all(Val::Px(20.0)),
-                                ..default()
-                            },
-                            image: image.clone().into(),
+                        Button,
+                        Style {
+                            width: Val::Px(w),
+                            height: Val::Px(h),
+                            // horizontally center child text
+                            justify_content: JustifyContent::Center,
+                            // vertically center child text
+                            align_items: AlignItems::Center,
+                            margin: UiRect::all(Val::Px(20.0)),
                             ..default()
                         },
+                        UiImage::from(image.clone()),
                         ImageScaleMode::Sliced(slicer.clone()),
                     ))
  • 将所有使用 ImageBundle 的地方替换为 UiImage。例如:
-    commands.spawn(ImageBundle {
-        image: UiImage {
+    commands.spawn((
+        UiImage {
             texture: metering_mask,
             ..default()
         },
-        style: Style {
+        Style {
             width: Val::Percent(100.0),
             height: Val::Percent(100.0),
             ..default()
         },
-        ..default()
-    });
+    ));

将 Style 属性合并到 Node 中。使用 ComputedNode 用于计算属性。 #

Style 上设置的任何字段移至 Node,并将所有 Style 组件用法替换为 Node

之前

commands.spawn((
    Node::default(),
    Style {
        width:  Val::Px(100.),
        ..default()
    },
));

之后

commands.spawn(Node {
    width:  Val::Px(100.),
    ..default()
});

对于以前位于 Node 上的“计算节点属性”的任何用法,请改用 ComputedNode

之前

fn system(nodes: Query<&Node>) {
    for node in &nodes {
        let computed_size = node.size();
    }
}

之后

fn system(computed_nodes: Query<&ComputedNode>) {
    for computed_node in &computed_nodes {
        let computed_size = computed_node.size();
    }
}

明确将 CameraUpdateSystem 排在 UiSystem::Prepare 之前 #

CameraUpdateSystem 现在明确排在 UiSystem::Prepare 之前,而不是与它含糊不清。

实用程序 #

删除 Parallel::drain() 中未使用的类型参数 #

Parallel::drain() 的类型参数未被使用,因此现在已删除。如果您之前手动指定了它,您可以删除边界。

// 0.14
// Create a `Parallel` and give it a value.
let mut parallel: Parallel<Vec<u8>> = Parallel::default();
*parallel.borrow_local_mut() = vec![1, 2, 3];

for v in parallel.drain::<u8>() {
    // ...
}

// 0.15
let mut parallel: Parallel<Vec<u8>> = Parallel::default();
*parallel.borrow_local_mut() = vec![1, 2, 3];

// Remove the type parameter.
for v in parallel.drain() {
    // ...
}
  • 现在必须从 bevy::ecs::entity 中导入 bevy::utils::{EntityHash, EntityHasher, EntityHashMap, EntityHashSet} 的使用情况。

允许 bevy_utilsno_std 上下文中使用 #

如果您之前导入过 bevy_utils 并将 default_features 设置为 false,但依赖于现在受 stdalloc 功能限制的元素,请在您的 Cargo.toml 中包含相关功能。

删除 get_short_name 中的分配 #

对于 format!dbg!panic! 等。

// Before
panic!("{} is too short!", get_short_name(name));

// After
panic!("{} is too short!", ShortName(name));

需要 String

// Before
let short: String = get_short_name(name);

// After
let short: String = ShortName(name).to_string();

窗口 #

bevy_window 中删除未使用的 default 功能 #

bevy_window 具有一个空的默认功能标志,它没有任何作用,因此已删除。如果您手动指定了它,则可能需要删除对它的任何引用。

# 0.14
[dependencies]
bevy_window = { version = "0.14", default-features = false, features = ["default"] }

# 0.15
[dependencies]
bevy_window = { version = "0.15", default-features = false }

公开 winit 的 MonitorHandle #

  • WindowMode 变体现在采用 MonitorSelection,可以将其设置为 MonitorSelection::Primary 以反映旧的行为。

将 ANDROID_APP 移动到 bevy_window #

如果您使用的是 bevy::winit::android_activity 中重新导出的 android_activity,它现在位于 bevy::window::android_activity 中。ANDROID_APP 静态变量也是如此。

添加 bevy_window::Window 选项用于 MacOS #

bevy_window::Window 现在具有用于配置 MacOS 窗口设置的额外字段

    pub movable_by_window_background: bool,
    pub fullsize_content_view: bool,
    pub has_shadow: bool,
    pub titlebar_shown: bool,
    pub titlebar_transparent: bool,
    pub titlebar_show_title: bool,
    pub titlebar_show_buttons: bool,

使用 Window::default 保持与之前相同的行为。