迁移指南

迁移指南:0.10 到 0.11

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

Schedule-First:改进后的 add_systems #

ECS

我们已经 将添加系统到调度的操作统一到一个 API 下add_systems 现在接受 ScheduleLabel 作为第一个参数。app.add_systemapp.add_startup_systemapp.add_startup_systemssystem.on_startup()system.in_schedule() 已被弃用,建议使用统一的 app.add_systems API。

“基础集”已被完全移除,取而代之的是调度。内置的 CoreSetStartupSet 基础集已被顶级调度所取代。(例如:CoreSet::Update 现在是 Update 调度)。

这去掉了大量冗余的 API,完全消除了隐式默认值,并消除了基础集引入的大量混淆。我们相信新的 add_systems API 的一致性和人体工程学不言而喻

// 0.10
app.add_system(a)
// 0.11
app.add_systems(Update, a)

// 0.10
app.add_systems((a, b).in_schedule(CoreSchedule::Startup))
// 0.11
app.add_systems(Startup, (a, b))

// 0.10
app.add_systems((a, b).in_schedule(CoreSchedule::Startup).in_base_set(StartupSet::PreStartup))
// 0.11
app.add_systems(PreStartup, (a, b))

// 0.10
app.add_startup_systems((a, b))
// 0.11
app.add_systems(Startup, (a, b))

// 0.10
app.add_systems((a, b).on_startup())
// 0.11
app.add_systems(Startup, (a, b))

// 0.10
app.add_systems((c, d, e))
// 0.11 (Update is no longer implied by default)
app.add_systems(Update, (c, d, e))

// 0.10
app.add_systems((f, g).in_schedule(CoreSchedule::FixedUpdate))
// 0.11
app.add_systems(FixedUpdate, (f, g))

// 0.10
app.add_systems(h.in_base_set(CoreSet::PostUpdate))
// 0.11
app.add_systems(PostUpdate, h)

// 0.10
app.add_systems(enter_menu.in_schedule(OnEnter(AppState::Menu)))
// 0.11
app.add_systems(OnEnter(AppState::Menu), enter_menu)

// 0.10
app.add_systems(exit_menu.in_schedule(OnExit(AppState::Menu)))
// 0.11
app.add_systems(OnExit(AppState::Menu), exit_menu)

// 0.10
render_app.add_systems((a, b).in_set(RenderSet::Queue))
// 0.11
render_app.add_systems(Render, (a, b).in_set(RenderSet::Queue))

设置配置现在也接受调度

// 0.10
app.configure_set(A.in_schedule(PostUpdate).after(B))
// 0.11
app.configure_set(PostUpdate, A.after(B))

// 0.10
app.configure_set(A.after(B))
// 0.11 (Update is no longer implied by default)
app.configure_set(Update, A.after(B))

// 0.10
app.configure_sets((A, B).in_schedule(PostUpdate).after(C))
// 0.11
app.configure_sets(PostUpdate, (A, B).after(C))

// 0.10
app.configure_sets((A, B).after(C))
// 0.11 (Update is no longer implied by default)
app.configure_sets(Update, (A, B).after(C))

bevy_audio:基于 ECS 的 API 重构 #

音频
// 0.10

/// Need to store handles somewhere
#[derive(Resource)]
struct MyMusic {
    sink: Handle<AudioSink>,
}

fn play_music(
    asset_server: Res<AssetServer>,
    audio: Res<Audio>,
    audio_sinks: Res<Assets<AudioSink>>,
    mut commands: Commands,
) {
    let weak_handle = audio.play_with_settings(
        asset_server.load("music.ogg"),
        PlaybackSettings::LOOP.with_volume(0.5),
    );
    // upgrade to strong handle and store it
    commands.insert_resource(MyMusic {
        sink: audio_sinks.get_handle(weak_handle),
    });
}

fn toggle_pause_music(
    audio_sinks: Res<Assets<AudioSink>>,
    mymusic: Option<Res<MyMusic>>,
) {
    if let Some(mymusic) = &mymusic {
        if let Some(sink) = audio_sinks.get(&mymusic.sink) {
            sink.toggle();
        }
    }
}

// 0.11

/// Marker component for our music entity
#[derive(Component)]
struct MyMusic;

fn play_music(
    mut commands: Commands,
    asset_server: Res<AssetServer>,
) {
    commands.spawn((
        AudioBundle {
            source: asset_server.load("music.ogg"),
            settings: PlaybackSettings::LOOP.with_volume(Volume::new_relative(0.5)),
        },
        MyMusic,
    ));
}

fn toggle_pause_music(
    // `AudioSink` will be inserted by Bevy when the audio starts playing
    query_music: Query<&AudioSink, With<MyMusic>>,
) {
    if let Ok(sink) = query_music.get_single() {
        sink.toggle();
    }
}

允许在 add_plugins 中使用元组和单个插件,弃用 add_plugin #

应用程序

app.add_plugin(plugin) 调用替换为 app.add_plugins(plugin)

改进着色器导入模型 #

渲染

不使用 #import 指令的着色器应该无需更改即可工作。

最显著的用户界面差异是导入的函数/变量等需要在使用点进行限定,并且没有从导入的导入中将可见的东西“泄漏”到你的着色器作用域中,因此,如果你使用了导入的导入中导入的东西,现在需要直接导入它们并进行限定。

当前直接将 mesh_vertex_output 包含/“扩展”到结构体中的策略不再有效,因此需要根据示例进行修改(例如:color_material.wgsl 或许多其他示例)。网格数据默认情况下假定在绑定组 2 中,如果网格数据绑定到绑定组 1,则需要将着色器定义 MESH_BINDGROUP_1 添加到管道的 shader_defs 中。

// 0.10
struct FragmentInput {
    #import bevy_pbr::mesh_vertex_output
}
@fragment
fn fragment(in: FragmentInput) -> @location(0) vec4<f32> {}

// 0.11
#import bevy_pbr::mesh_vertex_output MeshVertexOutput
@fragment
fn fragment(in: MeshVertexOutput) -> @location(0) vec4<f32> {}

如果你正在导入类似 mesh_view_bindings 的东西,但仅用于 globals 统一缓冲区,现在可以将其直接导入。

// 0.10
#import bevy_pbr::mesh_view_bindings
// use globals.time after this

// 0.11
#import bevy_pbr::mesh_view_bindings globals
// globals is now in scope, but nothing else is imported

扁平化使用 Size 的 UI Style 属性并移除 Size #

UI

sizemin_sizemax_sizegap 属性已被 widthheightmin_widthmin_heightmax_widthmax_heightrow_gapcolumn_gap 属性取代。请使用新的属性。

将 ScheduleRunnerSettings 合并到 ScheduleRunnerPlugin #

应用程序

不要插入 ScheduleRunnerSettings 资源,而是配置 ScheduleRunnerPlugin

// 0.10
.insert_resource(ScheduleRunnerSettings::run_loop(Duration::from_secs(5)))
.add_plugin(ScheduleRunnerPlugin::default())

// 0.11
.add_plugin(ScheduleRunnerPlugin::run_loop(Duration::from_secs(5)))

添加对自定义 glTF 顶点属性的支持。 #

资产

如果你之前使用类似于单元的结构体语法实例化 GltfPlugin,则必须改为使用 GltfPlugin::default(),因为该类型不再是类似于单元的类型。

延迟资产热重载 #

资产
// 0.10
.add_plugins(DefaultPlugins.set(AssetPlugin {
    watch_for_changes: true,
    ..default()
}))

// 0.11
.add_plugins(DefaultPlugins.set(AssetPlugin {
    // You can now give it a configurable delay. This is a safe default.
    watch_for_changes: ChangeWatcher::with_delay(Duration::from_millis(200)),
    ..default()
}))

允许使用 Diagnostics 的系统并行运行 #

诊断
  • 使用新的 app.register_diagnostic(Diagnostic::new(DIAGNOSTIC_ID, "diagnostic_name", 10)); 注册 Diagnostic
  • 在用于写入新度量的系统中,将 mut diagnostics: ResMut<Diagnostics> 更改为 mut diagnostics: Diagnostics 以允许系统并行运行。
- fn system(mut diagnostics: ResMut<Diagnostics>) {}
+ fn system(mut diagnostics: Diagnostics) {}
  • 在用于读取度量的系统中,将 diagnostics: Res<Diagnostics> 更改为 diagnostics: Res<DiagnosticsStore>
- fn system(diagnostics: Res<Diagnostics>) {}
+ fn system(diagnostics: Res<DiagnosticsStore>) {}

将日志记录到 stderr 而不是 stdout #

诊断

在类 Unix 系统上,当打印日志(如 info!trace!error! 等)时,从 stderr 中读取,而不是从 stdout 中读取

  • 在命令行上使用 2> output.logstderr 保存到文件

使 WorldQuery 元类型不可命名 #

ECS

使用 #[derive(WorldQuery)] 创建的类型的 StateFetch 类型现在不可命名。如果需要引用它们,请使用语法 <T as WorldQuery>::State<T as WorldQuery>::Fetch

提高变更检测的类型安全性和清晰度 #

ECS

引擎现在使用 Tick 类型来处理变更滴答,而不是 u32。任何与引擎内部进行交互的代码都需要更新,包括

  • 手动实现 SystemParamWorldQueryDetectChangesDetectChangesMut 特性的代码。
  • World::change_tickread_change_tick 方法。
  • System::set_last_change_tickget_last_change_tick。此外,这些方法已分别重命名为 set_last_runget_last_run
  • SystemChangeTick::change_ticklast_change_tick 方法。这些方法已分别重命名为 this_runlast_run
  • Tick::set_changed 方法,该方法已重命名为 set

移除 ChangeTrackers #

ECS

ChangeTrackers 已被移除。请改用 Ref<T> 查询。

assert_is_system 中检查冲突访问 #

ECS

函数 assert_is_systemassert_is_read_only_system(位于 bevy_ecs::system 中)现在会在传递的系统具有无效的世界访问权限时引发恐慌。任何在具有无效访问权限的系统上调用此函数的测试现在都会失败。请修复系统的冲突访问权限,或者指定该测试旨在失败

  • 对于常规测试(即使用 #[test] 注解的函数),请向函数添加 #[should_panic] 属性。
  • 对于文档测试,请在代码块的开头添加 should_panic ```should_panic

移除 ScheduleBuildError 的基础集合错误变体 #

ECS

随着基础集的移除,ScheduleBuildError 的相关变体也被移除。如果你之前处理过任何这些变体,可以安全地移除处理它们的代码。

移除 #[system_param(ignore)]#[world_query(ignore)] #

ECS

属性 #[system_param(ignore)]#[world_query(ignore)] 已被移除。如果你之前在 PhantomData 字段中使用过这两个属性,可以简单地移除该属性

#[derive(SystemParam)]
struct MyParam<'w, 's, Marker> {
    ...
    // 0.10
    #[system_param(ignore)]
    _marker: PhantomData<Marker>,
    // 0.11
    _marker: PhantomData<Marker>,
}

#[derive(WorldQuery)]
struct MyQuery<Marker> {
    ...
    // 0.10
    #[world_query(ignore)]
    _marker: PhantomData<Marker>,
    // 0.11
    _marker: PhantomData<Marker>,
}

如果你之前在实现 Default 的其他类型中使用过该属性,请考虑将该类型包装在 Local<> 中(这只对 SystemParam 有效)

#[derive(SystemParam)]
struct MyParam<'w, 's> {
    // 0.10
    #[system_param(ignore)]
    value: MyDefaultType, // This will be initialized using `Default` each time `MyParam` is created.
    // 0.11
    value: Local<MyDefaultType>, // This will be initialized using `Default` the first time `MyParam` is created.
}

如果你正在实现这两个特性,并且需要保留旧 ignore 属性的精确行为,请考虑为使用 Default 特性的包装结构手动实现 SystemParamWorldQuery

// 0.10
#[derive(WorldQuery)]
struct MyQuery {
   #[world_query(ignore)]
    str: String,
}
// 0.11
#[derive(WorldQuery)]
struct MyQuery {
    str: DefaultQuery<String>,
}

pub struct DefaultQuery<T: Default>(pub T);

unsafe impl<T: Default> WorldQuery for DefaultQuery<T> {
    type Item<'w> = Self;
    ...
    unsafe fn fetch<'w>(...) -> Self::Item<'w> {
        Self(T::default())
    }
}

用安全代码替换一些不安全的系统执行器代码 #

ECS

函数 bevy_utils::SyncUnsafeCell::get_mut 现在返回类型为 &mut SyncUnsafeCell<T> 的值。以前,这返回了一个不可变引用。

使用 UnsafeWorldCell 来提高 SystemParam 的代码质量 #

ECS

对于 SystemParam 的手动实现者:函数 get_item 现在接受 UnsafeWorldCell 而不是 &World。要访问世界数据,请使用

  • .get_entity(),它返回一个 UnsafeEntityCell,可用于访问组件数据。
  • get_resource() 及其变体,用于访问资源数据。

更新 increment_change_tick 以返回强类型 Tick #

ECS

函数 UnsafeWorldCell::increment_change_tick 现在是强类型的,返回类型为 Tick 的值,而不是原始 u32

将状态设为私有,只能通过 State 资源的获取器访问 #

ECS

使用 State::get 而不是直接访问元组字段。

仅在 next_state != old_state 时触发状态转换 #

ECS

状态转换现在仅在退出和进入的状态不同时触发。这意味着如果世界当前处于状态 A,如果排队进行到同一状态 A 的状态转换,则 OnEnter(A) 调度(或 OnExit)将不再运行。

简化系统管道并使其更灵活 #

ECS

IntoPipeSystem 特性已被移除,pipe 方法已移至 IntoSystem 特性。

// 0.10
use bevy_ecs::system::IntoPipeSystem;
schedule.add_systems(first.pipe(second));

// 0.11
use bevy_ecs::system::IntoSystem;
schedule.add_systems(first.pipe(second));

简化世界调度方法 #

ECS

方法 World::run_schedule_ref 已弃用,将在下一个版本的 Bevy 中删除。请改用 run_schedule

重命名 UnsafeWorldCell::read_change_tick #

ECS

UnsafeWorldCell 方法 read_change_tick 已重命名为 change_tick

使用 UnsafeWorldCell 提高多线程执行器的安全性 #

ECS

System 特性现在使用 UnsafeWorldCell 而不是 &World。这种类型为内部可变世界访问提供了一个强大的 API。

  • 方法 run_unsafe 使用这种类型来管理跨多个线程的世界变异。
  • 方法 update_archetype_component_access 使用这种类型来确保只能使用世界元数据。
let mut system = IntoSystem::into_system(my_system);
system.initialize(&mut world);

// 0.10
system.update_archetype_component_access(&world);
unsafe { system.run_unsafe(&world) }

// 0.11
system.update_archetype_component_access(world.as_unsafe_world_cell_readonly());
unsafe { system.run_unsafe(world.as_unsafe_world_cell()) }

改进命令的封装并添加文档 #

ECS

Command 类型 RemoveRemoveResource 不再可以手动构造。

// 0.10
commands.add(Remove::<T> {
    entity: id,
    phantom: PhantomData,
});
// 0.11
commands.add(Remove::<T>::new(id));
// 0.10
commands.add(RemoveResource::<T> { phantom: PhantomData });
// 0.11
commands.add(RemoveResource::<T>::new());

命令类型 GetOrSpawn 已被移除。在 bevy_ecs 之外使用此类型是不可能的。

将 apply_system_buffers 重命名为 apply_deferred #

ECS
  • apply_system_buffers 已重命名为 apply_deferred
  • System 特性上的 apply_system_buffers 方法已重命名为 apply_deferred
  • 函数 is_apply_system_buffers 已被 is_apply_deferred 替换
  • Executor::set_apply_final_buffers 现在是 Executor::set_apply_final_deferred
  • Schedule::apply_system_buffers 现在是 Schedule::apply_deferred

将 Command 的 "write" 方法重命名为 "apply" #

ECS
  • Command::write 实现需要改为实现 Command::apply。这仅仅是一个名称更改,无需采取进一步的措施。
  • EntityCommand::write 实现需要改为实现 EntityCommand::apply。这仅仅是一个名称更改,无需采取进一步的措施。

QueryState::par_iter 中要求只读查询 #

ECS

函数 QueryState::par_iter 现在强制所有世界访问为只读,类似于 QueryState::iter 的工作方式。以前使用此方法修改世界的任何代码都是不安全的。如果您需要修改世界,请改用 par_iter_mut

将引擎的其余部分迁移到 UnsafeWorldCell #

ECS

使用 &World 修改任何世界数据现在被认为是不安全的——必须使用类型 UnsafeWorldCell 来实现内部可变性。以下方法现在接受 UnsafeWorldCell 而不是 &World

  • QueryStateget_uncheckediter_uncheckediter_combinations_uncheckedfor_each_uncheckedget_single_uncheckedget_single_unchecked_manual
  • SystemStateget_unchecked_manual
let mut world = World::new();
let mut query = world.query::<&mut T>();

// 0.10
let t1 = query.get_unchecked(&world, entity_1);
let t2 = query.get_unchecked(&world, entity_2);

// 0.11
let world_cell = world.as_unsafe_world_cell();
let t1 = query.get_unchecked(world_cell, entity_1);
let t2 = query.get_unchecked(world_cell, entity_2);

方法 QueryState::validate_worldSystemState::matches_world 现在接受 WorldId 而不是 &World

// 0.10
query_state.validate_world(&world);

// 0.11
query_state.validate_world(world.id());

方法 QueryState::update_archetypesSystemState::update_archetypes 现在接受 UnsafeWorldCell 而不是 &World

// 0.10
query_state.update_archetypes(&world);

// 0.11
query_state.update_archetypes(world.as_unsafe_world_cell_readonly());

简化 ComponentIdFor 类型 #

ECS

类型 ComponentIdFor<T> 现在实现了 SystemParam 而不是 FromWorld——这意味着它应该直接用作系统的参数,而不是用在 Local 中。

// 0.10
fn my_system(component_id: Local<ComponentIdFor<MyComponent>>) {
    let component_id = **component_id;
}

// 0.11
fn my_system(component_id: ComponentIdFor<MyComponent>) {
    let component_id = component_id.get();
}

QueryParIter::for_each_unchecked 设为私有 #

ECS

方法 QueryParIter::for_each_unchecked 已被移除——请改用 for_eachfor_each_mut。如果您的用例无法使用这两个方法中的任何一个,那么您的代码可能是不安全的。

如果您有一个您认为是安全的 for_each_unchecked 用例,请打开一个问题

弃用 WorldQuery::Fetch 的类型别名 #

ECS

类型别名 bevy_ecs::query::QueryFetchROQueryFetch 已被弃用。如果您需要引用 WorldQuery 结构体的提取类型,请直接引用 WorldQuery 上定义的关联类型

// 0.10
type MyFetch<'w> = QueryFetch<'w, MyQuery>;
type MyFetchReadOnly<'w> = ROQueryFetch<'w, MyQuery>;

// 0.11
type MyFetch<'w> = <MyQuery as WorldQuery>::Fetch;
type MyFetchReadOnly<'w> = <<MyQuery as WorldQuery>::ReadOnly as WorldQuery>::Fetch;

为 EntityRef 实现 WorldQuery #

ECS

EntityRef::world 已被移除,以使 EntityRef 用作查询结果时安全。如果您是通过 World::entityWorld::get_entity 获取 EntityRef 的。在调用 World::entity 之前保存对 World 的引用的副本。

// In 0.10
let entity_ref = world.entity(id);
let world_2 = entity_ref.world();

// In 0.11
let world_2 = &world;
let entity_ref = world.entity(id);

将 AppTypeRegistry 移动到 bevy_ecs #

ECS
反射
应用程序

如果您没有使用 prelude::* 来导入 AppTypeRegistry,则应更新您的导入

// 0.10
use bevy::app::AppTypeRegistry;
// 0.11
use bevy::ecs::reflect::AppTypeRegistry

使场景处理实体引用更健壮 #

ECS
场景

MapEntities 实现必须从 &EntityMap 参数更改为 &mut EntityMapper 参数,并且不再可以返回 Result。最后,它们应该从调用 EntityMap::get 切换为调用 EntityMapper::get_or_reserve

重命名 map_entities 和 map_specific_entities #

ECS
场景

bevy_ecs 中,ReflectMapEntities::map_entities 现在需要一个额外的 entities 参数来指定它应用于哪些实体。要保留旧的行为,请使用新的 ReflectMapEntities::map_all_entities,但请考虑将实体专门传递进来是否对您的用例更好,以避免错误。

要求所有事件都使用 #[derive(Event)] #

ECS

为事件添加 #[derive(Event)] 宏。用作事件的第三方类型应该包装在新的类型中。

修复带框标签 #

ECS

ScheduleLabel 特性已被重构,不再依赖于特性 std::any::Anybevy_utils::DynEqbevy_utils::DynHash。任何手动实现都需要改用新的特性方法。

impl ScheduleLabel for MyType {
    // 0.10
    fn dyn_clone(&self) -> Box<dyn ScheduleLabel> { ... }

    // 0.11
    fn dyn_clone(&self) -> Box<dyn ScheduleLabel> { ... }

    fn as_dyn_eq(&self) -> &dyn DynEq {
        self
    }

    // No, `mut state: &mut` is not a typo.
    fn dyn_hash(&self, mut state: &mut dyn Hasher) {
        self.hash(&mut state);
        // Hashing the TypeId isn't strictly necessary, but it prevents collisions.
        TypeId::of::<Self>().hash(&mut state);
    }
}

移除 OnUpdate 系统集 #

ECS

run_if(in_state(xxx)) 替换 OnUpdate

记录查询错误 #

ECS

QueryEntityError::QueryDoesNotMatch 的显示消息已从 “给定的实体没有请求的组件。” 更改为 “给定的实体的组件与查询不匹配。”。

更新 syn、encase、glam 和 hexasphere #

ECS

在为嵌套的捆绑包派生 Bundle 时使用 #[bundle] 属性现在会抛出错误。它从 0.9 版本起就不再需要了,请参阅迁移指南

#[derive(Bundle)]
struct PlayerBundle {
    #[bundle] // Remove this line
    sprite_bundle: SpriteBundle,
    collider: Collider,
}

重命名 LAlt 等键为 AltLeft #

输入

通过替换进行迁移

  • LAltAltLeft
  • RAltAltRight
  • LBracketBracketLeft
  • RBracketBracketRight
  • LControlControlLeft
  • RControlControlRight
  • LShiftShiftLeft
  • RShiftShiftRight
  • LWinSuperLeft
  • RWinSuperRight

重命名 Interaction::Clicked -> Interaction::Pressed #

输入
UI

重命名所有 Interaction::Clicked -> Interaction::Pressed 的实例

不要忽略 UntypedReflectDeserializerVisitor 中的额外条目 #

反射

如果您使用多个条目(即除 "type": { /* fields */ } 之外的条目)反序列化 Box<dyn Reflect> 值,则应将其删除,否则反序列化将失败。

FromReflect 人体工程学实现 #

反射

FromReflect 现在在 Reflect 派生宏中自动派生。同时具有这两种派生的项需要删除 FromReflect 派生项。

// 0.10
#[derive(Reflect, FromReflect)]
struct Foo;

// 0.11
#[derive(Reflect)]
struct Foo;

如果使用 FromReflect 的手动实现和 Reflect 派生,用户将需要选择退出自动实现。

// 0.10
#[derive(Reflect)]
struct Foo;

impl FromReflect for Foo {/* ... */}

// 0.11
#[derive(Reflect)]
#[reflect(from_reflect = false)]
struct Foo;

impl FromReflect for Foo {/* ... */}

bevy_reflect:稳定的类型路径 v2 #

反射
  • AssetMaterialMaterial2d 的实现者现在还需要派生 TypePath

  • Reflect 的手动实现者需要实现新的 get_type_path 方法。

  • 为了在类型注册表中注册具有泛型参数的类型,即使泛型参数仅用于 `#[reflect(ignore)]` 字段,也必须实现 `TypePath`。

    // 0.10
    struct MyType {}
    
    #[derive(Reflect)]
    struct MyTypeWithGeneric<A>{
        #[reflect(ignore)]
        _phantom: PhantomData<A>
    }
    
    fn main() {
        App::new().add_plugins(DefaultPlugins)
            .register_type::<MyTypeWithGeneric<MyType>>() // in 0.11 this would error
            .run();
    }
    
    // 0.11
    #[derive(TypePath)] // New. Can also just use #[derive(Reflect)]
    struct MyType {}
    
    #[derive(Reflect)]
    struct MyTypeWithGeneric<A: TypePath>{ // changed
        #[reflect(ignore)]
        _phantom: PhantomData<A>
    }
    
    fn main() {
        App::new().add_plugins(DefaultPlugins)
            .register_type::<MyTypeWithGeneric<MyType>>()
            .run();
    }
    

    如果您不拥有某个类型,则可能需要将其包装在 newtype 中,并为 newtype 手动实现 `TypePath`。

    // 0.10
    use other_crate::RemoteType;
    
    #[derive(Reflect, Default)]
    struct MyTypeWithGeneric<A>{
        #[reflect(ignore)]
        _phantom: PhantomData<A>
    }
    
    fn main() {
        App::new().add_plugins(DefaultPlugins)
            .register_type::<MyTypeWithGeneric<RemoteType>>()
            .run();
    }
    
    // 0.11
    use other_crate::RemoteType;
    
    #[derive(Default)]
    struct MyType(RemoteType);
    
    impl TypePath for MyType {
        fn type_path() -> &'static str {
            "my_crate::my_module::MyType"
        }
        fn short_type_path() -> &'static str {
            "MyType"
        }
    }
    
    #[derive(Reflect, Default)]
    struct MyTypeWithGeneric<A: TypePath> {
        #[reflect(ignore)]
        _phantom: PhantomData<A>
    }
    
    fn main() {
        App::new().add_plugins(DefaultPlugins)
            .register_type::<MyTypeWithGeneric::<MyType>>()
            .run();
    }
    

在 `bevy_reflect::Map` 特性中添加 `get_at_mut` #

反射

`Map` 特性的实现者现在需要实现 `get_at_mut`。

bevy_reflect:更好的代理 #

反射
  • 动态类型不再接受字符串类型名称。相反,它们需要对 `TypeInfo` 的静态引用。
#[derive(Reflect)]
struct MyTupleStruct(f32, f32);

let mut dyn_tuple_struct = DynamicTupleStruct::default();
dyn_tuple_struct.insert(1.23_f32);
dyn_tuple_struct.insert(3.21_f32);

// 0.10
let type_name = std::any::type_name::<MyTupleStruct>();
dyn_tuple_struct.set_name(type_name);

// 0.11
let type_info = <MyTupleStruct as Typed>::type_info();
dyn_tuple_struct.set_represented_type(Some(type_info));
  • `Reflect::get_type_info` 已重命名为 `Reflect::represented_type_info`,现在也返回 `Option<&'static TypeInfo>`(而不是仅 `&'static TypeInfo`)。
// 0.10
let info: &'static TypeInfo = value.get_type_info();
// 0.11
let info: &'static TypeInfo = value.represented_type_info().unwrap();
  • `TypeInfo::Dynamic` 和 `DynamicInfo` 已被删除。使用 `Reflect::is_dynamic` 代替。
// 0.10
if matches!(value.get_type_info(), TypeInfo::Dynamic) {
  // ...
}
// 0.11
if value.is_dynamic() {
  // ...
}

从世界构造 `Box<dyn Reflect>` 用于 `ReflectComponent` #

反射
场景

如果您之前手动创建了 `ReflectComponentFns`,则现在需要添加一个 `from_world` 函数指针。

ReflectComponentFns {
  from_world: |world| Box::new(MyComponent::from_world(world)),
  // where `from_world: fn(&mut World) -> Box<dyn Reflect>`
  // ...
}

向预处理着色器添加 Globals 结构体 #

渲染

现在可以在预处理着色器中访问 `Globals` 着色器结构体。如果您之前手动绑定它,您可以删除该代码并直接使用 globals。

使渲染图槽位在大多数情况下可选 #

渲染

您现在可以直接从 `RenderGraphContext` 中获取 `view_entity`。

当实现 Node 时

// 0.10
struct FooNode;
impl FooNode {
    const IN_VIEW: &'static str = "view";
}
impl Node for FooNode {
    fn input(&self) -> Vec<SlotInfo> {
        vec![SlotInfo::new(Self::IN_VIEW, SlotType::Entity)]
    }
    fn run(
        &self,
        graph: &mut RenderGraphContext,
        // ...
    ) -> Result<(), NodeRunError> {
        let view_entity = graph.get_input_entity(Self::IN_VIEW)?;
        // ...
        Ok(())
    }
}

// 0.11
struct FooNode;
impl Node for FooNode {
    fn run(
        &self,
        graph: &mut RenderGraphContext,
        // ...
    ) -> Result<(), NodeRunError> {
        let view_entity = graph.view_entity();
        // ...
        Ok(())
    }
}

将节点添加到图时,您不需要为 `view_entity` 指定 `slot_edge`。

// 0.10
let mut graph = RenderGraph::default();
graph.add_node(FooNode::NAME, node);
let input_node_id = draw_2d_graph.set_input(vec![SlotInfo::new(
    graph::input::VIEW_ENTITY,
    SlotType::Entity,
)]);
graph.add_slot_edge(
    input_node_id,
    graph::input::VIEW_ENTITY,
    FooNode::NAME,
    FooNode::IN_VIEW,
);
// add_node_edge ...

// 0.11
let mut graph = RenderGraph::default();
graph.add_node(FooNode::NAME, node);
// add_node_edge ...

从 DynamicUniformBuffer 和 DynamicStorageBuffer 中移除不必要的 values Vec #

渲染

由于内部更改导致计算变得非平凡,因此已删除 `len()` 访问器。如果您正在使用它并且没有解决方法,请创建问题。

将 (Vec2, Vec2) 更改为 Camera::logical_viewport_rect 中的 Rect #

渲染
// 0.10
fn view_logical_camera_rect(camera_query: Query<&Camera>) {
    let camera = camera_query.single();
    let Some((min, max)) = camera.logical_viewport_rect() else { return };
    dbg!(min, max);
}

// 0.11
fn view_logical_camera_rect(camera_query: Query<&Camera>) {
    let camera = camera_query.single();
    let Some(Rect { min, max }) = camera.logical_viewport_rect() else { return };
    dbg!(min, max);
}

使 glsl 和 spirv 支持可选 #

渲染
  • 如果您想在 `spirv` 中使用着色器,请启用 `shader_format_spirv` 特性。
  • 如果您想在 `glsl` 中使用着色器,请启用 `shader_format_glsl` 特性。

更改默认色调映射方法 #

渲染

默认色调映射器已从 ReinhardLuminance 更改为 TonyMcMapface。在您的相机上显式设置 ReinhardLuminance 以恢复之前的外观。

TonyMcMapface 需要 `ktx2`、`tonemapping_luts` 和 `zstd` 特性,这些特性在默认情况下是启用的。如果您禁用了默认特性并注意到场景为粉色,则可以添加 `ktx2`、`tonemapping_luts` 和 `zstd` 特性,或者使用其他色调映射器。

在不需要查找表 (LUT) 的色调映射器中,SomewhatBoringDisplayTransform 最接近 TonyMcMapface。基于 LUT 的色调映射器更可取,因为它们通常更快。

应用代码库更改以准备 `StandardMaterial` 传输 #

渲染
  • `ViewTarget::main_texture()` 和 `ViewTarget::main_texture_other()` 现在返回 `&Texture` 而不是 `&TextureView`。如果您依赖这些方法,请将您的使用替换为 `ViewTarget::main_texture_view()` 和 `ViewTarget::main_texture_other_view()`;
  • `ViewTarget::sampled_main_texture()` 现在返回 `Option<&Texture>` 而不是 `Option<&TextureView>`。如果您依赖此方法,请将您的使用替换为 `ViewTarget::sampled_main_texture_view()`;
  • `apply_fog()`、`linear_fog()`、`exponential_fog()`、`exponential_squared_fog()` 和 `atmospheric_fog()` 函数现在接受可配置的 `Fog` 结构体。如果您依赖它们,请通过将全局 `fog` 统一变量作为它们的第一个参数来更新您的使用;

删除 `AlphaMode` 的 `Component` 派生 #

渲染

`AlphaMode` 不再是组件。

它在引擎中没有任何地方使用。如果您将其用作自己的目的的组件,则应使用 newtype,如下所示

#[derive(Component, Deref)]
struct MyAlphaMode(AlphaMode);

然后用 `MyAlphaMode` 替换 `AlphaMode` 的使用。

- Query<&AlphaMode, …>,
+ Query<&MyAlphaMode, …>,

将 `Plane` 结构体重命名为 `HalfSpace` #

渲染
  • 将 `render::primitives::Plane` 的实例更改为 `render::primitives::HalfSpace`
  • 将 `render::primitives::Frustum` 中 `planes` 成员的实例更改为 `half_spaces`

修复 `Plane` UV / 纹理翻转 #

渲染

翻转您在 `Plane` 形状上使用的纹理。

添加 `RenderTarget::TextureView` #

渲染

对 `RenderTarget` 枚举的引用将需要处理附加字段,例如在 `match` 语句中。

一致的屏幕空间坐标 #

渲染
窗口
UI

`Window::cursor_position` 现在返回相对于左上角的游标位置,而不是相对于左下角。这现在与其他屏幕空间坐标相匹配,例如 `RelativeCursorPosition`、UI 和视口。

`Camera` 上的 `world_to_viewport`、`viewport_to_world` 和 `viewport_to_world_2d` 方法现在返回/接受相对于左上角的视口位置,而不是相对于左下角。

如果您使用 `world_to_viewport` 来定位 UI 节点,则返回的 `y` 值现在应该传递给 `Style` 上的 `top` 字段,而不是 `bottom` 字段。请注意,这可能会移动 UI 节点的位置,因为它现在锚定在顶部。

如果您将 `Window::cursor_position` 传递给 `viewport_to_world` 或 `viewport_to_world_2d`,则无需更改。

Webgpu 支持 #

应用程序
渲染
  • `Plugin::setup` 已重命名为 `Plugin::cleanup`
  • 已添加 `Plugin::finish`,添加管道的插件应在此函数中执行,而不是在 `Plugin::build` 中执行。
// 0.10
impl Plugin for MyPlugin {
    fn build(&self, app: &mut App) {
        app.insert_resource::<MyResource>
            .add_systems(Update, my_system);

        let render_app = match app.get_sub_app_mut(RenderApp) {
            Ok(render_app) => render_app,
            Err(_) => return,
        };

        render_app
            .init_resource::<RenderResourceNeedingDevice>()
            .init_resource::<OtherRenderResource>();
    }
}

// 0.11
impl Plugin for MyPlugin {
    fn build(&self, app: &mut App) {
        app.insert_resource::<MyResource>
            .add_systems(Update, my_system);

        let render_app = match app.get_sub_app_mut(RenderApp) {
            Ok(render_app) => render_app,
            Err(_) => return,
        };

        render_app
            .init_resource::<OtherRenderResource>();
    }

    fn finish(&self, app: &mut App) {
        let render_app = match app.get_sub_app_mut(RenderApp) {
            Ok(render_app) => render_app,
            Err(_) => return,
        };

        render_app
            .init_resource::<RenderResourceNeedingDevice>();
    }
}

在 CI 中拍摄示例屏幕截图 #

渲染

`TimeUpdateStrategy::ManualDuration` 的含义已更改。它不再将时间设置为 `Instant::now()` 加上给定的持续时间,而是将时间设置为上次更新加上给定的持续时间。

计算 `GpuMesh` 上索引网格的 `vertex_count` #

渲染

`vertex_count` 现在直接存储在 `GpuMesh` 上,而不是 `GpuBufferInfo::NonIndexed` 上。

内置天空盒 #

渲染

如果需要,翻转 `EnvironmentMapLight` 地图以匹配它们之前渲染的方式(反向)。

左手 Y 轴向上立方体贴图坐标 #

渲染

从点光源阴影立方体贴图采样时,使用(预期)光到片段方向向量,但将 z 坐标取反。以前,您会使用片段到光方向向量。

为 `Sprite`、`TextureAtlasSprite` 和 `Mesh2d` 添加 `Aabb` 计算 #

渲染
  • 2D 实体现在会受到视锥体剔除的影响,请检查您的 2D 相机的 z 坐标和投影 `far`,如果其中一些不再渲染。
  • 特别是,具有负 z 值的 2D 实体现在被具有 `default` `Camera2dBundle` 的视锥体剔除所剔除。我们计划在版本 `0.11.1` 中为默认 2D 相机重新添加对负值的支持。

添加变形目标 #

动画
渲染
  • `MeshPipeline` 现在只有一个 `mesh_layouts` 字段,而不是单独的 `mesh_layout` 和 `skinned_mesh_layout` 字段。您应该在您的实现中处理所有可能的网格绑定组布局。
  • 您还应该正确处理新的 `MORPH_TARGETS` 着色器定义和网格管道键。公开了一个新函数来简化此操作:`setup_moprh_and_skinning_defs`
  • `MeshBindGroup` 现在是 `MeshBindGroups`,缓存的绑定组现在可以通过 `get` 方法访问。

bevy_scene:添加 `SceneFilter` #

场景

`DynamicScene::from_scene` 和 `DynamicScene::from_world` 不再需要 `AppTypeRegistry` 引用。

// 0.10
let registry = world.resource::<AppTypeRegistry>();
let dynamic_scene = DynamicScene::from_world(&world, registry);
// let dynamic_scene = DynamicScene::from_scene(&scene, registry);

// 0.11
let dynamic_scene = DynamicScene::from_world(&world);
// let dynamic_scene = DynamicScene::from_scene(&scene);

已删除 `DynamicSceneBuilder::from_world_with_type_registry`。现在注册表将自动从给定的世界中获取。

// 0.10
let registry = world.resource::<AppTypeRegistry>();
let builder = DynamicSceneBuilder::from_world_with_type_registry(&world, registry);

// 0.11
let builder = DynamicSceneBuilder::from_world(&world);

在场景中(反)序列化资源 #

场景

场景格式已更改,用户可能无法使用在此版本之前保存的场景,因为资源场景字段丢失了。

修复 look_to 变量命名 #

转换

`Transform::look_to` 方法将 `direction.try_normalize()` 的默认值从 `Vec3::Z` 更改为 `Vec3::NEG_Z`

修复孤儿实体的转换传播 #

转换
层次结构
  • 如果您调用了 `bevy_transform::systems::sync_simple_transforms` 和 `bevy_transform::systems::propagate_transforms`(bevy 没有重新导出这些),您需要考虑额外的 `RemovedComponents<Parent>` 参数。

删除 `Val::Undefined` #

UI
  • `Val::Undefined` 已被删除。Bevy UI 使用默认值的行為應保持不變。
  • `UiRect` 字段的默认值已更改为 `Val::Px(0.)`。
  • `Style` 的 `position` 字段已被删除。它的 `left`、`right`、`top` 和 `bottom` 字段已直接添加到 `Style` 中。
  • 对于 `Style` 的 `size`、`margin`、`border` 和 `padding` 字段,`Val::Undefined` 应替换为 `Val::Px(0.)`。
  • 对于 `Style` 的 `min_size`、`max_size`、`left`、`right`、`top` 和 `bottom` 字段,`Val::Undefined` 应替换为 `Val::Auto`

将拼写 linebreak_behaviour 更改为 linebreak_behavior #

UI

更新使用 `linebreak_behaviour` 的地方为 `linebreak_behavior` 更新事件 `FileDragAndDrop::HoveredFileCancelled` 的使用位置为 `HoveredFileCanceled` 更新 `Touches.just_cancelled` 的使用位置为 `Touches.just_canceled` 事件 `TouchPhase::Cancelled` 现在称为 `TouchPhase::Canceled`

向 `bevy_ui` 添加 CSS 网格支持 #

UI
  • `UiSystem::Flex` 系统集已重命名为 `UiSystem::Layout`。
  • 现在无法在常量时间内使用结构体字面量更新语法与 `Style`,因为它的一个字段实现了 `Drop`,这样做会导致“此类型的析构函数无法在常量中求值”错误(参见 此问题)。
// 0.10
pub const ABSOLUTE_STYLE: Style = Style {
    position_type: PositionType::Absolute,
    ..Style::DEFAULT
};

// 0.11
pub const ABSOLUTE_STYLE: Style = {
    let mut style = Style::DEFAULT;
    style.position_type = PositionType::Absolute;
    style
};

`MeasureFunc` 改进 #

UI
  • `CalculatedSize` 已重命名为 `ContentSize`。

  • 已从 `UiSurface` 中删除 `upsert_leaf` 函数,并替换为 `update_measure`,该函数更新 `MeasureFunc` 而不进行节点插入。

  • 已从 `Measure` 特性中删除 `dyn_clone` 方法。

  • CalculatedSize 的新功能已被 set 方法取代。

  • ImageBundleTextBundle 不再实现 Clone您可以:

    1. 包装您自己的 bundle 类型,并通过跳过克隆 ContentSize 字段来实现 Clone
    2. 使用闭包代替 clone
    // 0.10
    let circle = ImageBundle {
        style: image_style,
        image: materials.circle.clone(),
        ..Default::default()
    };
    commands.spawn(circle.clone());
    commands.spawn(circle.clone());
    commands.spawn(circle.clone());
    commands.spawn(circle.clone());
    
    // 0.11
    let circle = || ImageBundle {
        style: image_style,
        image: materials.circle.clone(),
        ..Default::default()
    };
    commands.spawn(circle());
    commands.spawn(circle());
    commands.spawn(circle());
    commands.spawn(circle());
    

在将 UI 坐标从物理坐标转换为逻辑坐标时除以 UiScale #

UI

现在,物理 UI 坐标将被 UiScale 和窗口的缩放因子同时除以,以计算 UI 节点的逻辑大小和位置。

这确保了由 NodeGlobalTransform 组件保存的 UI 节点大小和位置值,与用于派生它们的样式约束一致,遵循相同的逻辑坐标系,而不受当前 scale_factorUiScale 的影响。

NoWrap Text 功能 #

UI

bevy_text::text::BreakLineOn 现在有一个新的变体 NoWrap,用于禁用 Text 的文本换行。 也可以使用 TextBundlewith_no_wrap 方法禁用文本换行。

将文本系统中的本地文本队列替换为存储在组件中的标志 #

UI

TextBundle 现在有一个新的字段 text_flag,类型为 TextFlags

按轴拆分 UI Overflow #

UI

Style 属性 Overflow 现在是一个结构体,具有 xy 字段,允许对每个轴进行溢出控制。

使用这些辅助函数替换 Overflow 的变体。

  • Overflow::visible() 替换 Overflow::Visible
  • Overflow::clip() 替换 Overflow::Hidden

text_system 拆分 #

UI

ImageBundle 现在有一个新的字段 image_size,类型为 UiImageSize,它包含图像 bundle 纹理的大小,并由 update_image_calculated_size_system 自动更新。

更新 ahash 和 hashbrown #

无区域标签

如果您之前使用过资产的哈希值或使用 Bevy 公开的固定哈希器,则需要更新哈希值。

将 bevy_ui 可访问性系统移动到 PostUpdate #

无区域标签

bevy_ui 可访问性系统已移至 PostUpdate,如果您之前是在相对于这些系统安排的,请确保现在在 PostUpdate 中进行。