迁移指南

迁移指南:0.13 到 0.14

动画 #

AnimationClip 现在使用 UUID,NoOpTypeIdHash 现在称为 NoOpHash #

AnimationClip 现在使用 UUID 而不是基于 Name 组件的层次路径来引用骨骼。这有几个后果

  • 一个新的组件 AnimationTarget 应该放置在你希望动画的每个骨骼上,以指定它的 UUID 和关联的 AnimationPlayer。glTF 加载器会根据需要自动创建这些组件,因此 glTF 模型的大多数用法都不需要更改。
  • 在树中移动骨骼或重命名骨骼不再阻止 AnimationPlayer 影响它。
  • 动态更改 AnimationPlayer 组件可能需要手动更新 AnimationTarget 组件。

具有 AnimationPlayer 组件的实体现在可能拥有也具有 AnimationPlayer 组件的后代。但是,它们可能不会动画相同的骨骼。

此外,NoOpTypeIdHashNoOpTypeIdHasher 已重命名为 NoOpHashNoOpHasher

实现 AnimationGraph 以混合动画 #

AnimationPlayer 现在不能再单独播放动画:它们需要与 Handle<AnimationGraph> 配对。使用 AnimationPlayer 播放动画的代码需要先创建一个 AnimationGraph 资产,添加你想要播放的片段的节点(或片段),然后将该节点的索引提供给 AnimationPlayerplay 方法。

// 0.13
fn setup(mut commands: Commands, mut animations: ResMut<Assets<AnimationClip>>) {
    let mut animation = AnimationClip::default();

    // ...

    let mut player = AnimationPlayer::default();
    player.play(animations.add(animation));

    commands.spawn((
        player,
        // ...
    ));
}

// 0.14
fn setup(
    mut commands: Commands,
    mut animations: ResMut<Assets<AnimationClip>>,
    // You now need access to the `AnimationGraph` asset.
    mut graphs: ResMut<Assets<AnimationGraph>>,
) {
    let mut animation = AnimationClip::default();

    // ...

    // Create a new `AnimationGraph` and add the animation handle to it.
    let (graph, animation_index) = AnimationGraph::from_clip(animations.add(animation));

    let mut player = AnimationPlayer::default();
    // Play the animation index, not the handle.
    player.play(animation_index);

    commands.spawn((
        player,
        // Add the new `AnimationGraph` to the assets, and spawn the entity with its handle.
        graphs.add(graph),
        // ...
    ));
}

此外,AnimationPlayer::play_with_transition() 方法已被删除,并被 AnimationTransitions 组件替换。如果你之前使用的是 AnimationPlayer::play_with_transition(),请将你要播放的所有动画添加到 AnimationGraph 并创建一个 AnimationTransitions 组件来管理它们之间的混合。

有关此更改的更多信息,你可能感兴趣的是 RFC 51.

将颜色乘以 f32 将不再忽略 alpha 通道 #

之前可以通过 f32Color 乘以或除以,现在已删除。现在必须操作特定的颜色空间,例如 LinearRgba。此外,这些操作过去会跳过 alpha 通道,但现在不会了。

// 0.13
let color = Color::RgbaLinear {
    red: 1.0,
    green: 1.0,
    blue: 1.0,
    alpha: 1.0,
} * 0.5;

// Alpha is preserved, ignoring the multiplier.
assert_eq!(color.a(), 1.0);

// 0.14
let color = LinearRgba {
    red: 1.0,
    green: 1.0,
    blue: 1.0,
    alpha: 1.0,
} * 0.5;

// Alpha is included in multiplication.
assert_eq!(color.alpha, 0.5);

如果你需要 alpha 通道保持不变,请考虑创建自己的辅助方法

fn legacy_div_f32(color: &mut LinearRgba, scale: f32) {
    color.red /= scale;
    color.green /= scale;
    color.blue /= scale;
}

let mut color = LinearRgba {
    red: 1.0,
    green: 1.0,
    blue: 1.0,
    alpha: 1.0,
};

legacy_div_f32(&mut color, 2.0);

如果你不介意 alpha 发生变化,但需要它保持在 0.0 到 1.0 的范围内,请考虑将其钳位

let mut color = LinearRgba {
    red: 1.0,
    green: 1.0,
    blue: 1.0,
    alpha: 1.0,
} * 10.0;

// Force alpha to be within [0.0, 1.0].
color.alpha = color.alpha.clamp(0.0, 1.0);

请注意,在某些情况下,例如渲染精灵,alpha 会自动钳位,因此你不需要手动执行。

应用程序 #

OnEnter 状态调度程序现在在 Startup 调度程序之前运行 #

在 Bevy 0.13 中,通过 [app.init_state] 初始化的状态的 [OnEnter] 调度程序将在 Startup 调度程序中的任何系统之后运行。这是因为 [apply_state_transitions] 仅在 [StateTransition] 调度程序期间运行。

这是一个微妙的错误:游戏可能处于特定状态,而没有首先进入该状态。现在,[OnEnter] 状态转换逻辑会立即处理。有关此决定的更多上下文,请参阅 bevy#13968

要进行迁移,请选择以下选项之一

  1. 将你的启动系统移到一个状态,作为你等待的状态的一个变体(例如 AppState::Setup),然后在设置完成后从该状态过渡出来。
  2. 将你的启动系统移到一个状态,并将另一个状态设置为一个 子状态,该子状态依赖于启动状态的完成(例如 SetupState::SetupComplete)。
// 0.13
#[derive(States, Default)]
enum AppState {
    #[default]
    InMenu,
    InGame,
}

app
   .init_state::<AppState>()
   .add_systems(Startup, initial_setup)
   .add_systems(OnEnter(AppState::InMenu), relies_on_initial_setup);

// 0.14 (Solution 1)
#[derive(States, Default)]
enum AppState {
    // Make this the default instead of `InMenu`.
    #[default]
    Setup
    InMenu,
    InGame,
}

fn transition_to_in_menu(mut app_state: ResMut<NextState<AppState>>) {
    app_state.set(AppState::InMenu);
}

app
    .init_state::<AppState>()
    .add_systems(OnEnter(AppState::Setup), initial_setup)
    .add_system(Update, transition_to_in_menu.run_if(in_state(AppState::Setup)))
    .add_systems(OnEnter(AppState::InMenu), relies_on_initial_setup);

// 0.14 (Solution 2)
#[derive(States, Default)]
enum SetupState {
    #[default]
    SettingUp,
    SetupComplete,
}

#[derive(SubStates, Default)]
#[source(SetupState = SetupState::SetupComplete)]
enum AppState {
    #[default]
    InMenu,
    InGame,
}

fn finish_setup(mut app_state: ResMut<NextState<SetupState>>) {
    app_state.set(SetupState::SetupComplete);
}

app
    .init_state::<SetupState>()
    // Note that we don't call `init_state()` for substates!
    .add_sub_state::<AppState>()
    .add_systems(OnEnter(AppState::InitialSetup), initial_setup)
    .add_system(Update, finish_setup.run_if(in_state(AppState::Setup)))
    .add_systems(OnEnter(AppState::InMenu), relies_on_initial_setup);

SubAppApp 分开 #

SubApp 已从 App 中分离出来,因此与这些类型交互时涉及一些较大的更改。

构造 SubApp #

SubApp 不再包含 App,因此您不再能够将 App 转换为 SubApp。此外,提取函数现在必须在构造函数之外设置。

// 0.13
#[derive(AppLabel, Clone, Copy, Hash, PartialEq, Eq, Debug)]
struct MySubApp;

let mut app = App::new();
let mut sub_app = App::empty();

sub_app.add_systems(Main, ...);
sub_app.insert_resource(...);

app.insert_sub_app(MySubApp, SubApp::new(sub_app, |main_world, sub_app| {
    // Extraction function.
}));

// 0.14
#[derive(AppLabel, Clone, Copy, Hash, PartialEq, Eq, Debug)]
struct MySubApp;

let mut app = App::new();
// Use `SubApp::new()` instead of `App::new()`.
let mut sub_app = SubApp::new();

// Instead of setting the extraction function when you create the `SubApp`, you must set it
// afterwards. If you do not set an extraction function, it will do nothing.
sub_app.set_extract(|main_world, sub_world| {
    // Extraction function.
});

// You can still add systems and resources like normal.
sub_app.add_systems(Main, ...);
sub_app.insert_resource(...);

app.insert_sub_app(MySubApp, sub_app);

App 的变化 #

App 不再是 Send,但 SubApp 仍然是。

由于 AppSubApp 的分离,一些其他方法也发生了变化。

首先,App::world 作为属性不再可以直接访问。请改用 App::worldApp::world_mut 获取器。

#[derive(Component)]
struct MyComponent;

// 0.13
let mut app = App::new();
println!("{:?}", app.world.id());
app.world.spawn(MyComponent);

// 0.14
let mut app = App::new();
println!("{:?}", app.world().id()); // Notice the added paranthesese.
app.world_mut().spawn(MyComponent);

其次,子应用程序的所有获取器现在返回 SubApp 而不是 App。这包括 App::sub_appApp::sub_app_mutApp::get_sub_appApp::get_sub_app_mut

#[derive(AppLabel, Clone, Copy, Hash, PartialEq, Eq, Debug)]
struct MySubApp;

let mut app = App::new();
app.insert_sub_app(MySubApp, SubApp::new());

assert_eq!(app.sub_app(MySubApp).type_id(), TypeId::of::<SubApp>());

最后,App::runnerApp::main_schedule_label 现在是私有的。您不再能够获取运行器闭包,但您可以使用 SubApp::update_schedule 获取主调度标签。

let app = App::new();
let label = app.main().update_schedule;

App 上的第三方特性 #

如果您在 App 上实现了扩展特性,请考虑也将其在 SubApp 上实现。

trait SpawnBundle {
    /// Spawns a new `Bundle` into the `World`.
    fn spawn_bundle<T: Bundle>(&mut self, bundle: T) -> &mut Self;
}

impl SpawnBundle for App {
    fn spawn_bundle<T: Bundle>(&mut self, bundle: T) -> &mut Self {
        self.world_mut().spawn(bundle);
        self
    }
}

/// `SubApp` has a very similar API to `App`, so the code will usually look the same.
impl SpawnBundle for SubApp {
    fn spawn_bundle<T: Bundle>(&mut self, bundle: T) -> &mut Self {
        self.world_mut().spawn(bundle);
        self
    }
}

使 AppExit 更具体地说明退出原因 #

AppExit 事件现在是一个枚举类型,表示代码是否成功退出。如果您要构造它,您现在必须指定 SuccessError

// 0.13
fn send_exit(mut writer: EventWriter<AppExit>) {
    writer.send(AppExit);
}

// 0.14
fn send_exit(mut writer: EventWriter<AppExit>) {
    writer.send(AppExit::Success);
    // Or...
    writer.send(AppExit::Error(NonZeroU8::new(1).unwrap()));
}

如果您在系统中订阅了此事件,请考虑使用 match 来判断它是成功还是错误。

// 0.13
fn handle_exit(mut reader: EventReader<AppExit>) {
    for app_exit in reader.read() {
        // Something interesting here...
    }
}

// 0.14
fn handle_exit(mut reader: EventReader<AppExit>) {
    for app_exit in reader.read() {
        match *app_exit {
            AppExit::Success => {
                // Something interesting here...
            },
            AppExit::Error(exit_code) => panic!("App exiting with an error! (Code: {exit_code})"),
        }
    }
}

此外,App::run() 现在返回 AppExit 而不是单位类型 ()。由于 AppExit 实现了 Termination,您现在可以从主函数中返回它。

// 0.13
fn main() {
    App::new().run()
}

// 0.14
fn main() -> AppExit {
    App::new().run()
}

// 0.14 (alternative)
fn main() {
    // If you want to ignore `AppExit`, you can add a semicolon instead. :)
    App::new().run();
}

最后,如果您配置了自定义 App 运行器函数,它现在必须返回 AppExit

let mut app = App::new();

app.set_runner(|_app| {
    // ...

    // Return success by default, though you may also return an error code.
    AppExit::Success
});

弃用动态插件 #

动态插件现在已弃用。如果可能,请从您的代码中删除所有使用它们的地方。

// 0.13
// This would be compiled into a separate dynamic library.
#[derive(DynamicPlugin)]
pub struct MyPlugin;

impl Plugin for MyPlugin {
    // ...
}

// This would be compiled into the main binary.
App::new()
    .load_plugin("path/to/plugin")
    .run()

// 0.14
// This would now be compiled into the main binary as well.
pub struct MyPlugin;

impl Plugin for MyPlugin {
    // ...
}

App::new()
    .add_plugins(MyPlugin)
    .run()

如果您无法做到这一点,您可以通过使用 #[allow(deprecated)] 注解所有使用来暂时静默弃用警告。请注意,当前的动态插件系统将在下一个主要 Bevy 版本中删除,因此您最终将不得不迁移。您可能对这些更安全、相关的链接感兴趣

如果您确实无法没有动态插件,您可以从 Bevy 复制代码并将其添加到您的项目中。

将状态初始化方法移至 bevy::state #

State 已移至 bevy::state。随之而来的是,App::init_state 已从普通方法移至扩展特性。如果您没有使用前置声明,现在可能需要导入 AppExtStates 才能使用此方法。(此特性位于 bevy_state 特性标志后面,您可能需要启用它。)

// 0.13
App::new()
    .init_state::<MyState>()
    .run()

// 0.14
use bevy::state::app::AppExtStates as _;

App::new()
    .init_state::<MyState>()
    .run()

资产 #

删除 UpdateAssetsAssetEvents 调度程序 #

UpdateAssets 调度程序已被删除。如果您将系统添加到此调度程序中,请将其移至 PreUpdate 上运行。(您可能需要使用 system.before(...)system.after(...) 来配置排序。)

// 0.13
App::new()
    .add_plugins(DefaultPlugins)
    .add_systems(UpdateAssets, my_system)
    .run()

// 0.14
App::new()
    .add_plugins(DefaultPlugins)
    .add_systems(PreUpdate, my_system)
    .run()

此外,AssetEvents 已从 ScheduleLabel 更改为 First 调度程序中的 SystemSet

// 0.13
App::new()
    .add_plugins(DefaultPlugins)
    .add_systems(AssetEvents, my_system)
    .run()

// 0.14
App::new()
    .add_plugins(DefaultPlugins)
    .add_systems(First, my_system.in_set(AssetEvents))
    .run()

在特性中使用 async fn 而不是 BoxedFuture #

在 Rust 1.75 中,async fn 已为特性稳定。一些特性已从返回 BoxedFuture 切换为 async fn,具体来说是

  • AssetReader
  • AssetWriter
  • AssetLoader
  • AssetSaver
  • Process

请更新您的特性实现。

// 0.13
impl AssetLoader for MyAssetLoader {
    // ...

    fn load<'a>(
        &'a self,
        reader: &'a mut Reader,
        _settings: &'a (),
        _load_context: &'a mut LoadContext,
    ) -> BoxedFuture<'a, Result<Self::Asset, Self::Error>> {
        // Note that you had to pin the future.
        Box::pin(async move {
            let mut bytes = Vec::new();
            reader.read_to_end(&mut bytes).await?;
            Ok(bytes)
        })
    }
}

// 0.14
impl AssetLoader for MyAssetLoader {
    // ...

    async fn load<'a>(
        &'a self,
        reader: &'a mut Reader<'_>,
        _settings: &'a (),
        _load_context: &'a mut LoadContext<'_>,
    ) -> Result<Self::Asset, Self::Error> {
        // No more need to pin the future, just write it!
        let mut bytes = Vec::new();
        reader.read_to_end(&mut bytes).await?;
        Ok(bytes)
    }
}

由于这些特性现在使用 async,因此它们不再是对象安全的。如果您需要接收或存储 &dyn Trait,请改用 &dyn ErasedTrait 变体。例如

// 0.13
struct MyReader(Box<dyn AssetReader>);

// 0.14
struct MyReader(Box<dyn ErasedAssetReader>);

ProcessResult 中添加 Ignore 变体 #

ProcessResult 枚举类型用于资产加载,它有一个新的 Ignore 变体。您可能需要更新您的 match 语句。

删除 Handle<T>Into<AssetId<T>> #

使用 IntoHandle 转换为 AssetId 已被删除,因为它是一个易于出错的地方,可能会在 Handle 是强引用时删除资产。如果您需要 AssetId,请改用 Handle::id()

// 0.13
let id: AssetId<T> = handle.into();

// 0.14
let id = handle.id();

Reader 中添加 AsyncSeek 特性以能够在资产加载器中进行查找 #

资产加载器的 Reader 类型别名现在需要新的 AsyncSeek 特性。请为必须是 Reader 的任何结构实现 AsyncSeek,或者在不支持查找的情况下使用其他方法。

如果您遇到了问题,请在 bevy#12880 上告诉我们,帮助我们在 0.15 版本中改进设计!

LoadState::Failed 中添加错误信息 #

Rust 以其错误处理而自豪,Bevy 一直在稳步赶上。以前,在使用 AssetServer::load_state(及其变体)检查资产是否已加载时,返回的错误信息只是空的 LoadState::Failed。这对调试来说没什么用!

现在,完整的 AssetLoadError 包含在 Failed 中,以便准确地告诉您出了什么问题。您可能需要更新您的 matchif let 语句以处理此新值。

// 0.13
match asset_server.load_state(asset_id) {
    // ...
    LoadState::Failed => eprintln!("Could not load asset!"),
}

// 0.14
match asset_server.load_state(asset_id) {
    // ...
    LoadState::Failed(error) => eprintln!("Could not load asset! Error: {}", error),
}

此外,LoadState 中的 CopyPartialOrdOrd 实现已被删除。您可以显式调用 .clone() 而不是复制枚举类型,您也可以手动重新实现 Ord 作为帮助器方法(如果需要)。

AssetMetaCheck 设为 AssetPlugin 的字段 #

AssetMetaCheck 用于配置 AssetPlugin 如何读取 .meta 文件。它以前是一个资源,但现在已更改为 AssetPlugin 中的字段。如果您使用 DefaultPlugins,您可以使用 .set 来配置此字段。

// 0.13
App::new()
    .add_plugins(DefaultPlugins)
    .insert_resource(AssetMetaCheck::Never)
    .run()

// 0.14
App::new()
    .add_plugins(DefaultPlugins.set(AssetPlugin {
        meta_check: AssetMetaCheck::Never,
        ..default()
    }))
    .run()

使 LoadContext 使用构建器模式 #

LoadContext 用于 AssetLoader,它已更新,因此其所有 load_* 方法都已合并到一个构建器结构体中。

// 0.13
load_context.load_direct(path);
// 0.14
load_context.loader().direct().untyped().load(path);

// 0.13
load_context.load_direct_with_reader(reader, path);
// 0.14
load_context.loader().direct().with_reader(reader).untyped().load(path);

// 0.13
load_context.load_untyped(path);
// 0.14
load_context.loader().untyped().load(path);

// 0.13
load_context.load_with_settings(path, settings);
// 0.14
load_context.loader().with_settings(settings).load(path);

使用 RenderAssetUsages 在加载过程中配置 gLTF 网格和材质 #

现在可以使用 GltfLoaderSettings 配置是否应在主世界、渲染世界或两者中加载网格和材质。load_meshes 字段已从 bool 更改为 RenderAssetUsages 位标志,并且添加了一个新的 load_materials 字段。

您可能需要更新所有 gLTF .meta 文件。

// 0.13
load_meshes: true

// 0.14
load_meshes: ("MAIN_WORLD | RENDER_WORLD")

如果您在加载 gLTF 文件时使用 AssetServer::load_with_settings,您还需要更新

// 0.13
asset_server.load_with_settings("model.gltf", |s: &mut GltfLoaderSettings| {
    s.load_meshes = true;
});

// 0.14
asset_server.load_with_settings("model.gltf", |s: &mut GltfLoaderSettings| {
    s.load_meshes = RenderAssetUsages::MAIN_WORLD | RenderAssetUsages::RENDER_WORLD;
});

RenderMaterials 及类似类型合并到 RenderAssets 中,为目标类型实现 RenderAsset #

RenderMaterialsRenderMaterials2dRenderUiMaterials 已被 RenderAssets 资源替换。如果您需要使用 AssetId 访问 PreparedMaterial<T>,请改用 RenderAssets::get

此外,RenderAsset 特性现在应该为目标类型而不是源类型实现。如果您需要访问源类型,请使用 RenderAsset::SourceAsset 关联类型。

// 0.13
impl RenderAsset for Image {
    type PreparedAsset = GpuImage;

    // ...
}

// 0.14
impl RenderAsset for GpuImage {
    type SourceAsset = Image;

    // ...
}

音频 #

在取消生成音频实体时修复对子项的剩余引用 #

您可以使用 PlaybackMode 枚举类型配置生成的音频的行为。它的一个变体 PlaybackMode::Despawn 会在音频播放完毕时取消生成实体。

以前有一个错误,导致它只会取消生成实体,而不会取消生成其子项。此错误已修复,现在在音频播放完毕时会调用 despawn_recursive()

如果您依赖此行为,请考虑使用 PlaybackMode::Remove 只从实体中删除音频组件,或者使用 AudioSink::empty() 来检查是否有任何音频已播放完毕,并手动 despawn() 它。

颜色 #

彻底修改 Color #

Bevy 的颜色支持已经过重大修改,并随之而来的是新的 bevy::color 模块。系好安全带,很多东西都变了!

颜色空间表示 #

Bevy 的主要 Color 枚举类型用于在许多不同的颜色空间(例如 RGB、HSL 等)中表示颜色。以前,这些颜色空间都作为变体内联表示。

enum Color {
    Rgba {
        red: f32,
        green: f32,
        blue: f32,
        alpha: f32,
    },
    Hsla {
        hue: f32,
        saturation: f32,
        lightness: f32,
        alpha: f32,
    },
    // ...
}

此方法已更改,现在每个颜色空间都有其自己的专用结构体。

struct Srgba {
    red: f32,
    green: f32,
    blue: f32,
    alpha: f32,
}

struct Hsla {
    hue: f32,
    saturation: f32,
    lightness: f32,
    alpha: f32,
}

enum Color {
    Srgba(Srgba),
    Hsla(Hsla),
    // ...
}

这样可以更轻松地组织和管理不同的颜色空间,并且还添加了许多新的颜色空间!为了处理此更改,您可能需要更新您的 match 语句。

// 0.13
match color {
    Color::Rgba { red, green, blue, alpha } => {
        // Something cool here!
    },
    _ => {},
}

// 0.14
match color {
    Color::Srgba(Srgba { red, green, blue, alpha }) => {
        // Something cool here!
    },
    // If you explicitly match every possible color space, you may need to handle more variants.
    // Color::Xyza(Xyza { x, y, z, alpha }) => {
    //     // Something else even cooler here!
    // },
    _ => {}
}

此外,您现在必须在颜色空间之间转换时使用 FromInto 实现,而不是像以前那样使用帮助器方法,例如 as_rgbaas_hsla

// 0.13
let color = Color::rgb(1.0, 0.0, 1.0).as_hsla();

// 0.14
let color: Hsla = Srgba::rgb(1.0, 0.0, 1.0).into();

Color 方法 #

所有与 RGB 相关的都已重命名为 sRGB。这包括 Color::Rgba 变为 Color::Srgba,以及 Color::rgbColor::rgb_u8 变为 Color::srgbColor::srgb_u8

由于会导致静默的、相对昂贵的转换,因此已删除了访问 Color 的特定通道的方法。这包括 Color::rColor::set_rColor::with_r,以及 gb hsl 的所有等效方法。将您的 Color 转换为所需的颜色空间,在那里执行您的操作,然后转换回来。

// 0.13
let mut color = Color::rgb(0.0, 0.0, 0.0);
color.set_b(1.0);

// 0.14
let color = Color::srgb(0.0, 0.0, 0.0);
let srgba = Srgba {
    blue: 1.0,
    ..Srgba::from(color),
};
let color = Color::from(srgba);

Color::hex 已移至 Srgba::hex。调用 .into() 或手动构造 Color::Srgba 变体进行转换。

Color::rgb_linearColor::rgba_linear 已重命名为 Color::linear_rgbColor::linear_rgba 以符合 LinearRgba 结构体的命名方案。

Color::as_linear_rgba_f32Color::as_linear_rgba_u32 已被删除。请改用 LinearRgba::to_f32_arrayLinearRgba::to_u32,并在必要时进行转换。

已删除了将 LCH 或 HSL 颜色转换为浮点数数组或 Vec 类型的其他几个颜色转换方法。请在外部重新实现这些方法,或者如果发现它们特别有用,请打开一个 PR 重新添加它们。

Color 上的向量场算术运算(加、减、乘以 f32 和除以 f32)已被移除。请将您的颜色转换为 LinearRgba 空间并在那里显式执行运算。这在处理自发光或 HDR 颜色时尤其重要,它们的色彩通道值通常超出普通 0 到 1 范围。

Alpha #

Alpha,也称为透明度,过去用字母 a 来表示。现在在结构体和方法中用其全称来表示。

  • Color::set_aColor::with_aColor::a 现在分别为 Color::set_alphaColor::with_alphaColor::alpha。这些是新 Alpha 特征的一部分。
  • 此外,Color::is_fully_transparent 现在也属于 Alpha 特征。

CSS 常量 #

各种 CSS 颜色常量不再直接存储在 Color 中。而是定义在 Srgba 颜色空间中,并通过 bevy::color::palettes 访问。在它们上调用 .into() 将它们转换为 Color 以便快速调试使用。

// 0.13
let color = Color::BLUE;

// 0.14
use bevy::color::palettes::css::BLUE;

let color = BLUE;

请注意,palettes::css 不一定与之前定义的常量一一对应,因为一些名称和颜色已经更改以符合 CSS 规范。如果您需要与之前相同的颜色,请查看下面的表格或使用来自 旧常量 的颜色值。

0.130.14
CYANAQUA
DARK_GRAYSrgba::gray(0.25)
DARK_GREENSrgba::rgb(0.0, 0.5, 0.0)
GREENLIME
LIME_GREENLIMEGREEN
PINKDEEP_PINK

切换到 LinearRgba #

WireframeMaterialExtractedUiNodeExtractedDirectionalLightExtractedPointLightExtractedSpotLightExtractedSprite 现在存储 LinearRgba 而不是多态的 Color。此外,Color 不再实现 AsBindGroup。您应该存储 LinearRgba 以避免转换成本。

将 WGSL 数学常量和颜色运算从 bevy_pbr 移动到 bevy_render #

用于着色器的数学常量和颜色转换函数已从 bevy_pbr::utils 移动到 bevy_render::mathsbevy_render::color_operations。如果您在自己的着色器中依赖这些,请更新您的导入语句。

// 0.13
#import bevy_pbr::utils::{PI, rgb_to_hsv}

// 0.14
#import bevy_render::{maths::PI, color_operations::rgb_to_hsv}

移除旧的颜色空间工具 #

SrgbColorSpace 特征、HslRepresentation 结构体和 LchRepresentation 结构体已被移除,取而代之的是特定颜色空间结构体。

对于 SrgbColorSpace,请使用 Srgba::gamma_function()Srgba::gamma_function_inverse()。如果您使用过 SrgbColorSpaceu8 实现,请先将其转换为 f32

// 14 is random, this could be any number.
let nonlinear: u8 = 14;

// Apply gamma function, converting `u8` to `f32`.
let linear: f32 = Srgba::gamma_function(nonlinear as f32 / 255.0);

// Convert back to a `u8`.
let linear: u8 = (linear * 255.0) as u8;

请注意,这种转换可能很昂贵,尤其是在 Update 调度期间调用时。请考虑直接使用 f32

HslRepresentationLchRepresentation 可以用 SrgbaHslaLcha 之间的 From 实现来代替。

// 0.13
let srgb = HslRepresentation::hsl_to_nonlinear_srgb(330.0, 0.7, 0.8);
let lch = LchRepresentation::nonlinear_srgb_to_lch([0.94, 0.66, 0.8]);

// 0.14
let srgba: Srgba = Hsla::new(330.0, 0.7, 0.8, 1.0).into();
let lcha: Lcha = Srgba::new(0.94, 0.66, 0.8, 1.0).into();

ColorAttachment 中使用 LinearRgba #

ColorAttachment::new() 现在为 clear_color 接受 Option<LinearRgba> 而不是 Option<Color>。您可以使用 From<Color> 实现来转换您的颜色。

let clear_color: Option<LinearRgba> = Some(color.into());

开发工具 #

移除 close_on_esc #

close_on_esc 系统已被移除,因为它过于主观且缺乏定制性。如果您使用过此系统,您可以复制其内容如下。

pub fn close_on_esc(
    mut commands: Commands,
    focused_windows: Query<(Entity, &Window)>,
    input: Res<ButtonInput<KeyCode>>,
) {
    for (window, focus) in focused_windows.iter() {
        if !focus.focused {
            continue;
        }

        if input.just_pressed(KeyCode::Escape) {
            commands.entity(window).despawn();
        }
    }
}

您可能对使用操作系统提供的内置快捷键感兴趣,例如 Alt+F4Command+Q

诊断 #

使 sysinfo 诊断插件可选 #

bevy::diagnostic 依赖于 sysinfo 来使用 SystemInformationDiagnosticsPlugin 跟踪 CPU 和内存使用情况,但是编译和轮询系统信息可能非常慢。sysinfo 现在位于 sysinfo_plugin 特征标志之后,该标志默认情况下在 bevy 中启用,但在 bevy_diagnostic 中则不启用。

如果您直接依赖于 bevy_diagnostic,请在 Cargo.toml 中切换标志。

[dependencies]
bevy_diagnostic = { version = "0.14", features = ["sysinfo_plugin"] }

如果您为 bevy 设置了 default-features = false,请在 Cargo.toml 中执行相同的操作。

[dependencies]
bevy = { version = "0.14", default-features = false, features = ["sysinfo_plugin"] }

改进 tracing 层定制 #

Bevy 使用 tracing 通过 LogPlugin 处理日志记录和跨度。这可以使用 update_subscriber 字段进行定制,但它非常严格。这已经过修改,用更灵活的 custom_layer 替换了 update_subscriber 字段,它返回一个 Layer

// 0.13
fn update_subscriber(_app: &mut App, subscriber: BoxedSubscriber) -> BoxedSubscriber {
    Box::new(subscriber.with(CustomLayer))
}

App::new()
    .add_plugins(LogPlugin {
        update_subscriber: Some(update_subscriber),
        ..default()
    })
    .run();

// 0.14
use bevy::log::tracing_subscriber;

fn custom_layer(_app: &mut App) -> Option<BoxedLayer> {
    // You can provide a single layer:
    return Some(CustomLayer.boxed());

    // Or you can provide multiple layers, since `Vec<Layer>` also implements `Layer`:
    Some(Box::new(vec![
        tracing_subscriber::fmt::layer()
            .with_file(true)
            .boxed(),
        CustomLayer.boxed(),
    ]))
}

App::new()
    .add_plugins(LogPlugin {
        custom_layer,
        ..default()
    })
    .run();

BoxedSubscriber 类型别名也被移除,它被 BoxedLayer 类型别名替换。

ECS #

使用观察者泛化 ECS 反应性 #

在 0.14 中,引入了 ECS 观察者:立即响应世界中事件的机制。作为此更改的一部分,Event 特征被扩展以要求 Component#[derive(Event)] 现在自动为带注释的类型实现 Component,这可能会破坏也 #[derive(Component)] 的类型。

// 0.13
#[derive(Event, Component)]
struct MyEvent;

// 0.14
// `Component` is still implemented by the `Event` derive.
#[derive(Event)]
struct MyEvent;

有关更多信息,请参阅有关钩子和观察者的 发行说明

System::run 中立即应用延迟的系统参数 #

System::run 的默认实现现在将始终立即运行 System::apply_deferred。如果您在此情况下手动调用 System::apply_deferred,您可能需要将其移除。请注意,System::run_unsafe 仍然 *不会* 调用 apply_deferred,因为它无法保证它是安全的。

// 0.13
system.run(world);

// Sometime later:
system.apply_deferred(world);

// 0.14
system.run(world);

// `apply_deferred` no longer needs to be called!

CommandCommandQueue 移动到 bevy::ecs::world #

CommandCommandQueue 已从 bevy::ecs::system 移动到 bevy::ecs::world。如果您直接导入它们,您需要更新您的导入语句。

// 0.13
use bevy::ecs::system::{Command, CommandQueue};

// 0.14
use bevy::ecs::world::{Command, CommandQueue};

使 Component::Storage 成为一个常量 #

Component::Storage 关联类型已被关联常量 STORAGE_TYPE 替换,从而使 ComponentStorage 特征不再必要。如果您手动实现 Component 而不是使用 derive 宏,请更新您的定义。

// 0.13
impl Component for MyComponent {
    type Storage = TableStorage;
}

// 0.14
impl Component for MyComponent {
    const STORAGE_TYPE: StorageType = StorageType::Table;

    // ...
}
之前之后
TableStorageStorageType::Table
SparseStorageStorageType::SparseSet

Component 现在也不再是对象安全的。如果您使用过 dyn Component,请考虑 提交一个问题 来描述您的用例。

不要在 QueryState 中存储 Access<ArchetypeComponentId> #

QueryState 不再存储 Access<ArchetypeComponentId>,您现在必须将其作为参数传递给使用它的每个方法。为了解决此更改

  • QueryState::archetype_component_access 已被移除。您可以通过访问周围的 SystemState 来解决此问题。
  • QueryState::new_archetypeQueryState::update_archetype_component_access 现在需要 &mut Access<ArchetypeComponentId> 作为参数。

移除 WorldCell #

WorldCell 由于其不完整性、容易产生运行时恐慌以及存在多种良好替代方案而被移除。如果您使用它来获取多个不同的资源值,请考虑改用 SystemState,使用 SystemState::get() 方法。

如果 SystemState 不适合您的用例并且可以容忍 unsafe,您可以使用 UnsafeWorldCell。它更高效、功能更强大,但缺乏运行时检查。

QueryState::matched_tablesQueryState::matches_archtypes 返回迭代器而不是切片 #

QueryState::matched_tablesQueryState::matched_archetypes 现在返回迭代器而不是切片。如果可能,请使用来自 Iterator 特征的组合器。在最坏的情况下,您可以调用 Iterator::collect() 转换为 Vec,然后可以将其转换为切片。

从默认特征中移除系统步进 #

系统步进功能现在默认情况下处于禁用状态。它通常不应该包含在发布的游戏中,并且会增加一小部分但可衡量的性能开销。要启用它,请将 bevy_debug_stepping 功能添加到您的 Cargo.toml 中。

[dependencies]
bevy = { version = "0.14", features = ["bevy_debug_stepping"] }

使用 Stepping 的代码仍然可以在该功能被禁用时编译,但在应用程序调用 Stepping::enable() 时会在运行时打印一条错误消息。

优化事件更新和虚拟时间 #

Events::update() 已被优化为对注册的事件数量为 O(1)。在此过程中,一些系统和运行条件也发生了变化。

使用 EventRegistry 而不是 Events 资源将事件注册到 World 中。

// 0.13
world.insert_resource(Events::<MyEvent>::default());

// 0.14
EventRegistry::register_event::<MyEvent>(&mut world);

一些系统和运行条件也发生了变化。

  • event_update_system 不再使用泛型,并且现在具有不同的参数。
  • signal_event_update_system 现在具有不同的参数。
  • reset_event_update_signal_system 已被移除。
  • event_update_condition 现在具有不同的参数。

虽然与事件无关,但 virtual_time_system 也发生了变化。它已从一个系统转换为一个常规函数,并且现在接受 &T&mut T 而不是 Res<T>ResMut<T>

使 SystemParam::new_archetypeQueryState::new_archetype 不安全 #

QueryState::new_archetypeSystemParam::new_archetype 现在是不安全的函数,因为它们不能确保提供的 Archetype 来自与状态初始化所在的同一个 World。您需要将任何用法包装在 unsafe 块中,并且可能需要编写额外的断言以验证正确的用法。

更好的 SystemIdEntity 转换 #

如果您需要访问一次性系统的 SystemId 的底层 Entity,请使用新的 SystemId::entity() 方法。

// 0.13
let system_id = world.register_system(my_system);
let entity = Entity::from(system_id);

// 0.14
let system_id = world.register_system(my_system);
let entity = system_id.entity();

使 NextState 成为一个枚举 #

NextState 已从单元结构体转换为枚举。如果您直接访问内部 Option,无论是通过 NextState::0 还是匹配,您将需要更新您的代码以处理此更改。

// 0.13
let state = next_state.0.unwrap();

// 0.14
let NextState::Pending(state) = next_state else { panic!("No pending next state!") };
0.130.14
NextState(Some(S))NextState::Pending(S)
NextState(None)NextState::Unchanged

将状态与核心 ECS 分开 #

状态已移至一个独立的 crate,该 crate 受 bevy_state 特性控制。使用状态但未使用 Bevy 的 default-features 的项目需要将此特性添加到其 Cargo.toml 中。

直接使用 bevy_ecs 并使用状态的项目需要添加 bevy_state **crate** 作为依赖项。

直接使用 bevy_app 并使用状态的项目需要添加 bevy_state **特性**。

如果您不使用 DefaultPlugins,您将需要手动将 StatesPlugin 添加到您的应用程序中。

用户应更新引用旧位置的导入。

// 0.13
use bevy::ecs::schedule::{NextState, OnEnter, OnExit, OnTransition, State, States};
use bevy::ecs::schedule::common_conditions::in_state;

// 0.14
use bevy::state::state::{NextState, OnEnter, OnExit, OnTransition, State, States}
use bevy::state::condition::in_state;

WorldQuery::get_state() 限制为仅接受 Components #

WorldQueryQueryState 的一些方法是不安全的,因为它们传递了一个 &World。现在它们被限制为只接受 &Components。受影响的方法是

  • WorldQuery::get_state()
  • QueryState::transmute()
  • QueryState::transmute_filtered()
  • QueryState::join()
  • QueryState::join_filtered()

要从 World 中访问 Components,请调用 World::components()

如果您手动实现了 WorldQuery,则需要更新 get_state() 以仅使用 Components 提供的信息。

将状态转换名称统一为 exitedentered #

StateTransitionEventbeforeafter 字段已重命名为 exitedentered 以保持一致性。如果您访问这些字段或构造 StateTransitionEvent,则需要更新您的使用方式。

使 apply_state_transition 变为私有 #

apply_state_transition 系统不再公开。迁移依赖它的系统的最简单方法是创建一个自定义调度。

// 0.13
App::new()
    .add_plugins(DefaultPlugins)
    .add_systems(StateTransition, my_system.after(apply_state_transition))
    .run()

// 0.14
#[derive(ScheduleLabel, Debug, Hash, PartialEq, Eq, Clone)]
struct AfterStateTransition;

let mut app = App::new();

app.add_plugins(DefaultPlugins)
    .add_systems(AfterStateTransition, my_system);

// Create a new schedule and add it to the app.
let after_state_transition = Schedule::new(AfterStateTransition);
app.add_schedule(after_state_transition);

// Modify the schedule order to make this run after `StateTransition`.
app.world_mut()
    .resource_mut::<MainScheduleOrder>()
    .insert_after(StateTransition, AfterStateTransition);

app.run()

ReflectResource 上的 FromReflect 替换 FromWorld 要求 #

#[reflect(Resource)] 现在要求为您的资源实现 FromReflect 特性。如果您使用 #[derive(Reflect)],则默认情况下会执行此操作,但是您选择退出此行为的结构体将需要编写自己的实现。FromReflect 已被添加来替换 FromWorld 要求,尽管 FromReflect 是可失败的。您可能希望将 #[reflect(FromWorld)] 添加到您的资源中以维护一个不可失败的变体。

最后,如果您使用 ReflectResource 结构体,则需要将 &TypeRegistry 传递给其 insertapply_or_insertcopy 方法。

使 ReflectComponentFnsReflectBundleFns 方法与 EntityMut 协同工作 #

ReflectComponentFnsReflectBundleFns 已更新为与 EntityMut 协同工作,与更严格的 EntityWorldMut 相比。您将需要更新您对 ReflectComponentFns::applyReflectComponentFns::reflect_mutReflectBundleFns::apply 的使用。

如果您只使用 ReflectComponentReflectBundle,则不需要更改代码,因为 EntityWorldMut 实现了 Into<EntityMut>

ReflectBundle::insert() 中要求 TypeRegistry #

ReflectBundle::insert 现在需要一个额外的 &TypeRegistry 参数。

multi-threaded 特性重命名为 multi_threaded #

multi-threaded 特性已为 bevybevy_assetbevy_ecsbevy_renderbevy_tasksbevy_internal 重命名为 multi_threaded。如果您手动指定 Bevy 特性,请更新您的 Cargo.toml

internlabel 模块从 bevy::utils 移至 bevy::ecs #

bevy::utils::labelbevy::utils::intern 模块已移至 bevy::ecs,以及 bevy::utils::define_label 宏,作为缩减 bevy::utils 的一项积极努力的一部分。您将需要更新您的导入语句以使用新的路径。

Gizmos #

Gizmo 线连接 #

已为 gizmos 添加了线连接,允许在直线之间创建圆角或尖角。如果您手动创建了自己的 GizmoConfig,则需要使用 line_joins 字段指定线连接的类型。

GizmoLineJointDefault 实现为 None,但您可能对 Miter(尖角)或 Round(圆角)感兴趣。

Gizmo 线样式 #

现在可以使用 GizmoConfig::line_style 配置 gizmos 的线样式(例如实线或点线)。如果您手动创建一个 GizmoConfig,则需要指定此字段。

segments() 方法重命名为 resolution() #

所有名为 segments() 的 gizmo 方法已重命名为 resolution(),以与 bevy::render 保持一致。

使 gizmos 以引用方式获取基元 #

Gizmos::primitive_2d()Gizmos::primitive_3d() 现在以引用方式获取基元,这样非 Copy 基元就不需要在每次绘制时进行克隆。

// 0.13
fn draw(mut gizmos: Gizmos) {
    let polygon = Polygon {
        vertices: [
            // ...
        ],
    };

    // Since `Polygon` is not `Copy`, you would need to clone it if you use it more than once.
    gizmos.primitive_2d(polygon.clone(), Vec2::ZERO, 0.0, Color::WHITE);
    gizmos.primitive_2d(polygon, Vec2::ONE, 0.0, Color::BLACK);
}

// 0.14
fn draw(mut gizmos: Gizmos) {
    let polygon = Polygon {
        vertices: [
            // ...
        ],
    };

    // No need to clone the polygon anymore!
    gizmos.primitive_2d(&polygon, Vec2::ZERO, 0.0, Color::WHITE);
    gizmos.primitive_2d(&polygon, Vec2::ONE, 0.0, Color::BLACK);
}

更多 gizmos 生成器 #

Gizmos::primitive_2d(CIRLCE)Gizmos::primitive_2d(ELLIPSE)Gizmos::primitive_2d(ANNULUS)Gizmos::primitive_3d(SPHERE) 现在返回其相应的生成器,而不是单元类型 ()。此外,SphereBuilder::circle_segments() 已重命名为 resolution()

上下文清除 gizmos #

App::insert_gizmo_group() 函数现在名为 App::insert_gizmo_config()

输入 #

将触控板输入重命名为手势 #

在最近的 winit 更新中,触控板事件现在可以在移动设备上触发。为了解决这个问题,与触控板相关的项目已重命名为手势

  • bevy::input::touchpad 已重命名为 bevy::input::gestures
  • TouchpadMagnify 已重命名为 PinchGesture
  • TouchpadRotate 已重命名为 RotationGesture

弃用 ReceivedCharacter #

ReceivedCharacter 现在已弃用,因为 winit 重构了其键盘系统,请改为使用 KeyboardInput

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

// 0.14
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.");
            },
            _ => {},
        }
    }
}

添加 WinitEvent::KeyboardFocusLost #

WinitEvent 有一个新的枚举变体:WinitEvent::KeyboardFocusLost。这是在修复失去 Bevy 窗口焦点时(例如使用 Alt + Tab)按键会卡住问题时添加的。请更新所有 match 语句。

数学 #

将有限和无限 3d 平面分开 #

Plane3d 基元现在是一个有限平面,具有 half_size 字段。如果您想要一个无限平面,请使用新的 InfinitePlane3d

// 0.13
let plane = Plane3d::new(Vec3::Y);

// 0.14
let plane = Plane3d {
    normal: Dir3::Y,
    half_size: Vec2::new(10., 10.),
};
let plane = InfinitePlane3d::new(Vec3::Y);

将方向类型从 bevy::math::primitives 中移出 #

Direction2dDirection3dInvalidDirectionError 类型已从 bevy::math::primitives 移至 bevy::math

// 0.13
use bevy::math::primitives::{Direction2d, Direction3d, InvalidDirectionError};

// 0.14
use bevy::math::{Direction2d, Direction3d, InvalidDirectionError};

Direction2d/3d 重命名为 Dir2/3 #

Direction2dDirection3d 类型已重命名为 Dir2Dir3。它们已缩短,使其更容易键入,并使其与 glam 的较短命名方案(例如 Vec2Mat4)保持一致。

使基点样条曲线包含端点 #

CubicCardinalSpline 中存在一个错误,该错误导致曲线仅穿过内部控制点,而不是起点和终点处的点。(有关深入分析,请参阅 此问题。)此问题已修复,因此曲线穿过所有控制点,但它可能会破坏您依赖的行为。

如果您依赖 CubicCardinalSpline 的旧行为,则需要截断您使用的任何参数化,以便访问与您之前使用的曲线相同的曲线。这可以通过从参数化区间的每个端点裁剪一个单位距离段来完成。例如,如果您的代码如下所示

fn interpolate(t: f32) -> Vec2 {
    let points = [
        vec2(-1.0, -20.0),
        vec2(3.0, 2.0),
        vec2(5.0, 3.0),
        vec2(9.0, 8.0),
    ];
    let my_curve = CubicCardinalSpline::new(0.3, points).to_curve();
    my_curve.position(t)
}

然后为了获得类似的行为,t 需要向上移动 1 个单位(因为 CubicCardinalSpline::to_curve 的输出在区间 [0,1] 中引入了一个新段),将旧段从 [0,1] 移动到 [1,2]

fn interpolate(t: f32) -> Vec2 {
    let points = [
        vec2(-1.0, -20.0),
        vec2(3.0, 2.0),
        vec2(5.0, 3.0),
        vec2(9.0, 8.0),
    ];
    let my_curve = CubicCardinalSpline::new(0.3, points).to_curve();
    // Add 1 here to restore original behavior.
    my_curve.position(t + 1)
}

(请注意,这不会为 t 的值提供相同的输出,这些值位于区间 [0,1] 之外。)

另一方面,任何只为了让曲线穿过正确点(即不需要完全相同的输出)而指定额外端点切线的用户,都可以简单地省略只用于控制目的的端点。

VectorSpace 替换 Point #

Point 特性已被 VectorSpace 替换。这些特性非常相似,但有一些细微的改变

  • VectorSpace 实现现在必须提供 ZERO 常量。
  • VectorSpace 现在需要 Div<f32, Output = Self>Neg 特性边界。
  • VectorSpace 不再需要 Add<f32, Output = Self>SumPartialEq 特性边界。

对于大多数情况,您可以用 VectorSpace 替换所有 Point 使用情况,但如果您依赖上述列表中的任何内容,则可能需要进行进一步的更改。

Triangle2d 的 UV 映射更改 #

通过此 PR,Triangle2d 的 UV 映射已更改:主要区别在于 UV 不再依赖于三角形的绝对坐标,而是遵循三角形本身在其定义中的平移。如果您依赖于 Triangle2d 的旧 UV 坐标,那么您将需要更新受影响的区域以使用新的 UV 坐标,这些 UV 坐标可以简要描述如下

  • 第一个坐标平行于三角形的前两个顶点之间的线。
  • 第二个坐标与此正交,指向第三个点的方向。

一般来说,这意味着前两个点的坐标将为[_, 0.],而第三个点的坐标将为[_, 1.],其具体值取决于第三个点相对于前两个点的 位置。对于锐角三角形,前两个顶点始终具有 UV 坐标[0., 0.][1., 0.]。对于钝角三角形,第三个点将具有坐标[0., 1.][1., 1.],其中另一个点的坐标会发生变化,以保持比例。

例如

  • 默认的 Triangle2d 的 UV 坐标为[0., 0.][0., 1.][0.5, 1.]
  • 顶点为vec2(0., 0.)vec2(1., 0.)vec2(2., 1.)的三角形具有 UV 坐标[0., 0.][0.5, 0.][1., 1.]
  • 顶点为vec2(0., 0.)vec2(1., 0.)vec2(-2., 1.)的三角形具有 UV 坐标[2./3., 0.][1., 0.][0., 1.]

使用 Vec3A 用于 3D 包围盒和射线检测 #

Aabb3dBoundingSphereRayCast3d 现在在内部使用Vec3A 而不是Vec3Vec3AVec3 的 SIMD 加速形式,因此它应该在不明显改变行为的情况下提高性能。

如果你手动构建任何受影响的结构,则需要将其转换为Vec3A

// 0.13
let x = Vec3::new(5.0, -2.0);

let aabb = Aabb3d {
    min: Vec3::ZERO,
    max: x,
};

// 0.14
let x = Vec3::new(5.0, -2.0);

let aabb = Aabb3d {
    // Both variants are very similar, so you can usually replace `Vec3` with `Vec3A`.
    min: Vec3A::ZERO,
    // In cases where you cannot, use the `From` and `Into` traits.
    max: x.into(),
};

glam 更新到 0.27 #

glam 已从 0.25 更新到 0.27。请查看变更日志了解 0.26 和 0.27,以便更新你的代码。

最大的破坏性变化是,向量类型现在以self - self.trunc() 而不是self - self.floor() 的形式评估fract() 方法。如果你需要旧行为,请使用fract_gl() 方法。

公共 MeshBuilder 特性 #

所有形状网格构建器(ConeMeshBuilderPlaneMeshBuilder 等)都具有一个方法build(),用于将它们转换为Mesh。此方法已成为公共特性MeshBuilder。如果你使用build() 但未使用预置,则需要导入此特性。

TorusMeshBuilder 添加角度范围 #

TorusMeshBuilder 不再是Copy,因为它包含用于角度范围的RangeInclusivex..=y)。在以前隐式复制它的任何情况下,都需要手动调用clone()

PlaneMeshBuilder 添加细分 #

在 0.13 中,Plane 类型已被弃用,取而代之的是Plane2dPlane3d。新的平面类型没有提供用于细分的方法,现已进行修订。

如果你使用了Plane::subdivisions 属性,现在需要将Plane3d 转换为PlaneMeshBuilder

// 0.13
let plane = Plane {
    subdivisions: 10,
    ..default()
};

// 0.14
let plane = Plane3d::default().mesh().subdivisions(10);

使 Transform::rotate_axisTransform::rotate_local_axis 使用 Dir3 #

Transform::rotate_axis()Transform::rotate_local_axis() 现在需要Dir3 而不是Vec3,因为轴预计是标准化的。通常,你可以使用Vec3 调用Dir3::new(),它会自动将其标准化,但你必须处理Result,以防向量无效。

请注意,大多数常量(如Vec3::X)都具有相应的Dir3 变体,如Dir3::X

GlobalTransform 中使用 Dir3 用于局部轴方法 #

GlobalTransform 组件的定向轴方法(right()left()up()down()back()forward())已从返回Vec3 更新为返回Dir3Dir3 实现Deref<Target = Vec>,但如果你需要可变访问,则可以调用Vec3::from()

修复 FloatOrdOrdPartialOrd 不同 #

FloatOrdPartialOrd 实现以前在行为上与其Ord 实现不同,但现在已修复,因此两者现在都匹配。PartialOrd 的当前实现永远不会返回None,因为它现在回退到Ord 实现。如果你依赖于这种不匹配的行为,请考虑对内部的f32 使用PartialOrd 实现。

FloatOrd 移动到 bevy::math #

FloatOrd 已被移动到bevy::math 模块中。请更新你的导入语句

// 0.13
use bevy::utils::FloatOrd;

// 0.14
use bevy::math::FloatOrd;

反射 #

手动注册缺失类型 #

许多外部类型不再由 Bevy 的默认插件注册到类型注册表中。通常,只有其他 Bevy 类型使用的类型(由于新的递归注册)才会默认注册。如果你以前在stdglam 中使用过类型进行反射功能,则可能需要手动注册它们。

App::new().register_type::<DMat3>();

更改 ReflectSerialize 特性边界 #

ReflectSerialize 现在需要TypePathFromReflect 特性边界,而不是Reflect。如果你以前选择退出它们,则需要实现这些特性。例如,如果你使用了#[reflect(type_path = false)]#[reflect(from_reflect = false)],则需要删除它们。

类型的递归注册 #

现在可以递归注册类型,但在这样做时,所有(未忽略的)反射字段都需要实现GetTypeRegistration。当Reflect 被派生时,会自动完成此操作,但手动实现还需要实现GetTypeRegistration

#[derive(Reflect)]
struct Foo<T: FromReflect> {
    data: MyCustomType<T>
}

// 0.13
impl<T: FromReflect> Reflect for MyCustomType<T> {
    // ...
}

// 0.14
impl<T: FromReflect + GetTypeRegistration> Reflect for MyCustomType<T> {
    // ...
}

impl<T: FromReflect + GetTypeRegistration> GetTypeRegistration for MyCustomType<T> {
    // ...
}

UntypedReflectDeserializer 重命名为 ReflectDeserializer #

UntypedReflectDeserializer 已被重命名为ReflectDeserializer。任何用法都需要相应地更新

// 0.13
let reflect_deserializer = UntypedReflectDeserializer::new(&registry);

// 0.14
let reflect_deserializer = ReflectDeserializer::new(&registry);

Result 作为枚举实现 Reflect #

ResultReflect 实现已更改为使其成为ReflectKind::Enum 而不是ReflectKind::Value。这提高了它与Option 的一致性,并允许检查其内容。

现在,Result<T, E> 不再需要TE 都实现Clone,而是需要它们实现FromReflect。此外,<Result<T, E> as Reflect>::reflect_* 现在返回Enum 变体,而不是Value

使用 &TypeRegistry 序列化场景,并将 serialize_ron() 重命名为 serialize() #

SceneSerializer 和所有相关的序列化帮助程序现在使用&TypeRegistry 而不是&TypeRegistryArc。你可以通过TypeRegistryArc::read() 从后者访问前者。

此外,DynamicScene::serialize_ron() 已被重命名为serialize()。这样做是为了强调此函数不是专门用于序列化到 RON,而是用于官方的 Bevy 场景格式(.scn / .scn.ron)。如果需要,这为将来更改格式留下了空间。

// 0.13
let world = World::new();
let scene = DynamicScene::from_world(&world);

let type_registry_arc: &TypeRegistryArc = &**world.resource::<AppTypeRegistry>();

let serialized_scene = scene.serialize_ron(type_registry_arc).unwrap();

// 0.14
let world = World::new();
let scene = DynamicScene::from_world(&world);

let type_registry_arc: &TypeRegistryArc = &**world.resource::<AppTypeRegistry>();

// We now need to retrieve the inner `TypeRegistry`.
let type_registry = type_registry_arc.read();

// `serialize_ron` has been renamed to `serialize`, and now takes a reference to `TypeRegistry`.
let serialized_scene = scene.serialize(&type_registry).unwrap();

渲染 #

使 BackgroundColorBorderColor 的默认行为更直观 #

BackgroundColor 不再为ImageBundleButtonBundle 中的图像着色。设置UiImage::color 来为图像着色。此外,UiImage 的新默认纹理现在是一个透明的白色正方形。使用UiImage::solid_color 可以快速绘制调试图像。最后,BackgroundColorBorderColor 的默认值现在是透明的。将颜色手动设置为白色以恢复到以前的行为。

Camera3dBundle::dither 重命名为 deband_dither #

Camera3dBundle::dither 已被重命名为deband_dither,以使其与Camera2dBundle 保持一致。如果你构造或访问此字段,则需要更新你的使用方式。

affine_to_square() 重命名为 affine3_to_square() #

affine_to_square() **着色器** 函数已重命名为affine3_to_square,以便为affine2_to_square 留出空间。请更新你的导入语句和使用方式。(请注意,这不是 Rust,而是 WGSL。)

// 0.13
#import bevy_render::maths::affine_to_square

// 0.14
#import bevy_render::maths::affine3_to_square

AlphaMode 移动到 bevy::render #

AlphaMode 已从bevy::pbr 移动到bevy::render。如果你直接导入它们,则需要更新你的导入语句。

// 0.13
use bevy::pbr::AlphaMode;

// 0.14
use bevy::render::alpha::AlphaMode;

在处理纹理尺寸时使用 UVec2 #

GpuImageTextureAtlasLayoutTextureAtlasBuilderDynamicAtlasTextureBuilderFontAtlas 已更改为使用整数而不是浮点数存储其尺寸,以提高与底层纹理数据的 一致性。Vec2Rect 的实例已替换为UVec2URect

迁移此过程很棘手,因为从f32u32 的转换是有损的。如果你使用的是常量,你可以简单地重写代码。如果你使用的是用户输入,则可以选择简单地丢弃小数部分(1.4 as u32)或先四舍五入(1.83.round() as u32)。

修复 CameraProjectionPlugin 在某些情况下未实现 Plugin #

CameraProjectionPlugin<T> 存在一个错误,如果T 未实现ComponentGetTypeRegistration,它有时不会实现Plugin。现在通过要求T: CameraProjection + Component + GetTypeRegistration 解决了此问题。

random1D() 替换为 rand_f() 着色器函数 #

bevy_pbr::utils::random1D() **着色器** 函数已被类似的bevy_pbr::utils::rand_f() 替换。请注意,如果你将返回的f32 转换为其他数据类型,你可能对返回u32rand_u() 和返回vec2<f32>rand_vec2f() 感兴趣。

内部网格顶点缓冲区布局 #

现在将重复的 MeshVertexBufferLayout 合并为单个对象 MeshVertexBufferLayoutRef,其中包含指向布局的原子引用计数 (Arc) 指针。通过对这些布局进行驻留,可以缓存 PartialEq 的结果,从而加快渲染速度。使用 MeshVertexBufferLayout 的代码可能需要更新为使用 MeshVertexBufferLayoutRef

GpuArrayBufferIndex::index 设为 u32 #

GpuArrayBufferIndex::index 现在是 u32 而不是 NonMaxU32,因为不再需要限制数字。请更新任何使用 u32 的用法。

允许通过 MaterialPlugin 禁用阴影 #

MaterialPlugin 现在有一个 shadows_enabled 属性。如果您手动构建了此插件,则可能需要设置它。默认情况下它为 true,但您可以通过将其设置为 false 来完全禁用阴影。

删除 SpritePipeline::COLORED #

SpritePipelineKeyCOLORED 标志已被删除,因为它不再使用。这样做,HDRTONEMAP_IN_SHADERDEBAND_DITHER 的原始值已更改。如果您正在手动将 u32 转换为 SpritePipelineKey,则可能需要更新它。

对渲染阶段项目、资源和非网格进行排序和分箱 #

PhaseItem 的使用已分为 BinnedPhaseItemSortedPhaseItem。如果您有自定义 PhaseItem,则需要选择其中一种新类型。值得注意的是,某些阶段 *必须* 排序(例如 Transparent 和 Transmissive),而其他阶段可以分箱。有效地排序是“Bevy 之前的做法”,而分箱是新的,这种变化的目的是尽可能避免排序,从而提高性能。

如果您正在寻找快速迁移,请考虑选择 SortedPhaseItem,它需要最少的代码更改。

如果您正在寻找更高的性能(并且您的阶段不需要排序),您可能想要选择 BinnedPhaseItem。值得注意的是,箱子是根据 BinKey 填充的,同一个箱子中的所有内容都可能进行批处理。

如果您只是消费这些类型,那么对像 &mut RenderPhase<Transparent2d> 这样的类型的 Query 将成为 Resource,例如

mut transparent_render_phases: ResMut<ViewSortedRenderPhases<Transparent2d>>

ViewSortedRenderPhasesViewBinnedRenderPhases 用于与您尝试访问的阶段项目(排序或分箱)一致。

SortedPhaseItems 的示例 SortedPhaseItemss

  • Transmissive3d
  • Transparent2d
  • Transparent3d
  • TransparentUi

BinnedPhaseItem 的示例包括 BinnedPhaseItems

  • Opaque3d
  • Opaque3dPrepass
  • Opaque3dDeferred
  • AlphaMask3d
  • AlphaMask3dPrepass
  • AlphaMask3dDeferred
  • Shadow

如果您没有网格(例如用于 GPU 驱动的粒子或过程生成),并且想要使用新的分箱行为,则 BinnedRenderPhase 包含一个 non_mesh_items 集合,该集合与 BinnedRenderPhaseType 相对应。当 添加 项目到 BinnedRenderPhase 时,将使用此类型。

您可能还希望查看新的 custom_phase_item 示例,其中详细介绍了一些新的 API。

GPU 视锥剔除 #

对于 PhaseItemdynamic_offset: Option<NonMaxU32> 字段现在是 extra_index: PhaseItemExtraIndex,它包装了一个 u32。不要使用 None,而要使用 PhaseItemExtraIndex::NONE

此更改影响 AlphaMask3dAlphaMask3dDeferredAlphaMask3dPrepassOpaque2dOpaque3dDeferredOpaque3dPrepassShadowTransmissive3dTransparent2dTransparent3dTransparentUi

删除 DeterministicRenderingConfig #

DeterministicRenderingConfig 已被删除,因为它的唯一属性 stable_sort_z_fighting 不再需要。现在不透明项目被分箱而不是排序,因此 Z 轴抖动已基本消除。

优化 queue_material_meshes 并删除一些位操作 #

GpuMesh 上的 primitive_topology 字段现在是一个 getter 方法:GpuMesh::primitive_topology()

出于性能原因,MeshPipelineKey 已被拆分为 BaseMeshPipelineKey(位于 bevy::render 中)和 MeshPipelineKey(位于 bevy::pbr 中)。这两个可以与按位或运算符组合起来生成最终的 MeshPipelineKey

let base_pipeline_key = BaseMeshPipelineKey::all();
let pbr_pipeline_key = MeshPipelineKey::all();

let pipeline_key: u64 = base_pipeline_key.bits() | pbr_pipeline_key.bits();

默认情况下禁用 RAY_QUERYRAY_TRACING_ACCELERATION_STRUCTURE #

RAY_QUERYRAY_TRACING_ACCELERATION_STRUCTURE wgpu 特性现在默认情况下被禁用,因为一些用户在初始化时出现程序崩溃。(此问题的 wgpu 问题可以在 此处 找到。)

如果您使用这些功能,则需要通过 WgpuSettings::features 重新启用它们。

let mut settings = WgpuSettings::default();

// Enable `RAY_QUERY` and `RAY_TRACING_ACCELERATION_STRUCTURE`, along with the defaults.
settings.features |= WgpuFeatures::RAY_QUERY | WgpuFeatures::RAY_TRACING_ACCELERATION_STRUCTURE;

App::new()
    .add_plugins(DefaultPlugins.set(RenderPlugin {
        render_creation: settings.into(),
        ..default()
    }))
    .run()

请注意,WgpuSettings::default() 会自动为 Bevy 配置良好的默认标志,而 WgpuFeatures::default() 等效于 WgpuFeatures::empty()

将前一帧的 inverse_view 上传到 GPU #

PreviousViewProjection 已重命名为 PreviousViewDataPreviousViewProjectionUniformOffset 已重命名为 PreviousViewUniformOffset。此外,还重命名了几个系统

  • update_previous_view_projectionsupdate_previous_view_data
  • extract_camera_previous_view_projectionextract_camera_previous_view_data
  • prepare_previous_view_projection_uniformsprepare_previous_view_uniforms

在 GPU 上生成 MeshUniform(如果可用)。 #

自定义渲染阶段现在需要多个系统,而不仅仅是 batch_and_prepare_render_phase。以前创建自定义渲染阶段的代码现在应该根据需要添加 BinnedRenderPhasePluginSortedRenderPhasePlugin,而不是直接添加 batch_and_prepare_render_phase

将纹理坐标翻转添加到 StandardMaterial #

Quad 在 0.13 中已弃用,但它的替代品 Rectangle 没有为 Quad::flip 提供明确的替代方案。这已经得到修正:现在您可以对任何 StandardMaterial 调用 flip()

请注意,Quad::flip 专门是 *水平* 翻转,但 StandardMaterial::flip() 支持 *垂直* 和 *水平* 翻转。

重命名 ShadowFilteringMethodCastano13Jimenez14 变体 #

ShadowFilteringMethod::Castano13ShadowFilteringMethod::Jimenez14 已分别重命名为 GaussianTemporal,以便为将来扩展留出空间,但相应的作者仍在文档中被提及。

分别存储 VisibleEntities 列表 #

check_visibility()VisibleEntities 现在分别存储四种类型的可渲染实体 - 2D 网格、3D 网格、灯光和 UI 元素。如果您的自定义渲染代码检查 VisibleEntities,则现在需要使用 WithMesh2dWithMeshWithLightWithNode 类型分别指定它感兴趣的实体类型。如果您的应用程序引入了新的可渲染实体类型,则需要向主世界计划中添加一个具有适当查询过滤器的 check_visibility 系统实例,以适应您的新组件或组件。例如

struct MyCustomRenderable;

App::new()
    .add_plugins(DefaultPlugins)
    .add_systems(
        PostUpdate,
        check_visibility::<With<MyCustomRenderable>>
            .in_set(VisibilitySystems::CheckVisibility)
    )
    .run();

使 Text 需要 SpriteSource #

Text 现在需要一个 SpriteSource 标记组件才能渲染。此组件已添加到 Text2dBundle 中,如果未使用 ..default(),则可能需要指定它。

公开 desired_maximum_frame_latency #

desired_maximum_frame_latency 字段已添加到 WindowExtractedWindow 中。它是一个 Option<NonZero<u32>>,它暗示了 GPU 上允许的最大排队帧数。更高的值可能会导致更流畅的帧,并避免因 CPU-GPU 数据上传而导致的冻结,但这一切都以更高的输入延迟为代价。将 desired_maximum_frame_latency 设置为 None 将使其回退到默认值,当前为 2。

合并 VisibilitySystems 的视锥变体 #

VisibilitySystemsUpdateOrthographicFrustaUpdatePerspectiveFrustaUpdateProjectionFrusta 变体已被删除,取而代之的是新的 VisibilitySystems::UpdateFrusta 变体。

扩展颜色分级 #

ColorGrading 组件已扩展以支持单独配置阴影、中间调和高光部分。如果您以前配置了 gammapre_saturation 字段,则现在必须为所有部分设置它们。

// 0.13
let color_grading = ColorGrading {
    gamma: 2.0,
    pre_saturation: 0.8,
    ..default()
};

// 0.14
let mut color_grading = ColorGrading::default();

for section in color_grading.all_sections_mut() {
    section.gamma = 2.0;
    // `pre_saturation` has been renamed to `saturation`.
    section.saturation = 0.8;
}

此外,post_saturationexposure 字段已被专门移动到新的 global 字段,它是一个 ColorGradingGlobal,支持针对整个图像执行更多操作。

// 0.13
let color_grading = ColorGrading {
    post_saturation: 1.2,
    exposure: 0.4,
};

// 0.14
let color_grading = ColorGrading {
    global: ColorGradingGlobal {
        post_saturation: 1.2,
        exposure: 0.4,
        ..default()
    },
    ..default()
};

BufferVec 重命名为 RawBufferVec #

BufferVec 已重命名为 RawBufferVec,因为 BufferVec 的新实现已经取了它的名字。新的 BufferVec<T> 不再需要 T: Pod,而是来自 encase 库的 ShaderType

在大多数情况下,您可以简单地切换到使用 RawBufferVec,但如果您有更复杂的数据,您可能对新的 BufferVec 实现感兴趣。

实现清漆 #

如果定义了 STANDARD_MATERIAL_CLEARCOAT,则 pbr_lighting WGSL 模块中的照明函数现在具有清漆参数。此外,R 反射矢量参数已从一些照明函数中删除,因为它没有被使用。

拆分 Node2d::MainPass #

Node2d::MainPass 已被拆分为 3 个独立的阶段:StartMainPassMainTransparentPassEndMainPass。如果您以前使用 MainPass 对自己的自定义节点进行排序,则现在需要相对于 StartMainPassEndMainPass 对其进行排序。

删除对 RenderLayers 的限制 #

现在不再限制 RenderLayers 的总数,因此与之相关的常量 TOTAL_LAYERSall() 构造函数已被删除。期望在所有层上可见的实体(例如灯光)应该创建一个列出应用程序使用过的所有已知层的常量,或者在运行时计算正在使用的活动层。

RenderLayers 上不再实现 Copy 特性。相反,您应该使用 Renderlayers 仍然实现的 Clone 特性的 .clone() 函数。

修复了光晕所需的极端发射颜色 #

发射颜色和相机曝光现在可以很好地协同工作。以前,StandardMaterialemissive 属性必须非常大(以千计),才能使光晕等效果可见。这已经缩小了规模,因此您可能需要重新调整您的发射颜色。

// 0.13
StandardMaterial {
    emissive: Color::linear_rgb(23000.0, 9000.0, 3000.0),
    ..default()
}

// 0.14
StandardMaterial {
    // Much more reasonable! :)
    emissive: Color::linear_rgb(13.99, 5.32, 2.0),
    ..default()
}

您可能还会对 StandardMaterial::emissive_exposure_weight 属性感兴趣。

更具语义的 TextureAtlasBuilder #

TextureAtlasBuilder 已被修改为更符合其他构建器的风格。作为修改的一部分,大多数方法现在返回 &mut Self 而不是 Self,并且 finish() 已被重命名为 build()

// 0.13
let (texture_atlas_layout, texture) = TextureAtlasBuilder::default()
    .padding(UVec2::default())
    .format(TextureFormat::bevy_default())
    .finish()
    .unwrap();

// 0.14
let (texture_atlas_layout, texture) = TextureAtlasBuilder::default()
    .padding(UVec2::default())
    .format(TextureFormat::bevy_default());
    .build() // This is now `build()`.
    .unwrap();

矩阵命名规范化 #

所有矩阵已重命名为遵循 x_from_y 的约定,以减少混淆并提高可读性。

  • Frustumfrom_view_projectionfrom_view_projection_custom_farfrom_view_projection_no_far 方法已重命名为 from_clip_from_worldfrom_clip_from_world_custom_farfrom_clip_from_world_no_far
  • ComputedCameraValues::projection_matrix 已重命名为 clip_from_view
  • CameraProjection::get_projection_matrix 已重命名为 get_clip_from_view(这会影响 ProjectionPerspectiveProjectionOrthographicProjection 的实现)。
  • ViewRangefinder3d::from_view_matrix 已重命名为 from_world_from_view
  • PreviousViewData 的成员已重命名为 view_from_worldclip_from_world
  • ExtractedViewprojectiontransformview_projection 已重命名为 clip_from_viewworld_from_viewclip_from_world
  • ViewUniformview_projunjittered_view_projinverse_view_projviewinverse_viewprojectioninverse_projection 已重命名为 clip_from_worldunjittered_clip_from_worldworld_from_clipworld_from_viewview_from_worldclip_from_viewview_from_clip
  • GpuDirectionalCascade::view_projection 已重命名为 clip_from_world
  • MeshTransformstransformprevious_transform 已重命名为 world_from_localprevious_world_from_local
  • MeshUniformtransformprevious_transforminverse_transpose_model_ainverse_transpose_model_b 已重命名为 world_from_localprevious_world_from_locallocal_from_world_transpose_alocal_from_world_transpose_b(WGSL 中的 Mesh 类型反映了这一点,但 transformprevious_transform 被命名为 modelprevious_model)。
  • Mesh2dTransforms::transform 已重命名为 world_from_local
  • Mesh2dUniformtransforminverse_transpose_model_ainverse_transpose_model_b 已重命名为 world_from_locallocal_from_world_transpose_alocal_from_world_transpose_b(WGSL 中的 Mesh2d 类型反映了这一点)。
  • 在 WGSL 中,bevy_pbr::mesh_functionsget_model_matrixget_previous_model_matrix 已重命名为 get_world_from_localget_previous_world_from_local
  • 在 WGSL 中,bevy_sprite::mesh2d_functions::get_model_matrix 已重命名为 get_world_from_local

在聚类上下文中将 “点光源” 重命名为 “可聚类对象” #

在 PBR 着色器中,point_lights 现在被称为 clusterable_objectsPointLight 现在被称为 ClusterableObject,而 cluster_light_index_lists 现在被称为 clusterable_object_index_lists。此重命名泛化了可聚类对象,这为将来添加光探针和贴花留下了空间。

使 Mesh::merge() 接受 Mesh 的引用 #

Mesh::merge() 现在接受 &Mesh 而不是 Mesh。因此,您现在可以在多次 merge() 调用中共享相同的 Mesh,而无需克隆它。

CameraOutputMode 中存储 ClearColorConfig 而不是 LoadOp<Color> #

CameraOutputMode::Write 现在存储 ClearColorConfig 而不是 LoadOp<Color>。请使用以下表格在两个枚举之间进行转换。

LoadOp<Color>ClearColorConfig
Clear(color)Custom(color)
LoadNone

ClearColorConfig 还有一个额外的变体,Default,它继承了 ClearColor 资源的清除颜色。

wgpu 0.20 #

Bevy 现在依赖于 wgpu 0.20、naga 0.20 和 naga_oil 0.14。如果您在 Cargo.toml 中手动指定了这些 crate,请确保将它们的版本更新以防止它们被重复。

此外,编码器内的 timestamps 现在在 WebGPU 上被禁止(尽管它们在原生上仍然有效)。使用 TIMESTAMP_QUERY_INSIDE_ENCODERS 功能来检查支持情况。

弃用 SpriteSheetBundleAtlasImageBundle #

SpriteSheetBundle 已被弃用,作为向添加功能的可选组件转变的一部分,而不是大量捆绑包的激增。将 TextureAtlas 组件与 SpriteBundle 一起插入。

// 0.13
commands.spawn(SpriteSheetBundle {
    texture,
    atlas: TextureAtlas {
        layout,
        ..default()
    },
    ..default()
});
// 0.14
commands.spawn((
    SpriteBundle {
        texture,
        ..default()
    },
    TextureAtlas {
        layout,
        ..default()
    },
));

AtlasImageBundle 已被弃用。将 TextureAtlas 组件与 ImageBundle 一起插入。

// 0.13
commands.spawn(AtlasImageBundle {
    image,
    atlas: TextureAtlas {
        layout,
        ..default()
    },
    ..default()
});
// 0.14
commands.spawn((
    ImageBundle {
        image,
        ..default()
    },
    TextureAtlas {
        layout,
        ..default()
    },
));

BackgroundColorUiImage 解耦 #

BackgroundColor 组件现在在 UiImage 后面渲染一个纯色背景,而不是对其颜色进行着色。使用 UiImagecolor 字段进行着色。

// 0.13
ButtonBundle {
    image: UiImage::new(my_texture),
    background_color: my_color_tint.into(),
    ..default()
}

// 0.14
ButtonBundle {
    image: UiImage::new(my_texture).with_color(my_color_tint),
    ..default()
}

一些 UI 系统已被拆分或重命名。

  • bevy_ui::RenderUiSystem::ExtractNode 已被拆分为 ExtractBackgroundsExtractImagesExtractBordersExtractText
  • bevy_ui::extract_uinodes 已被拆分为 extract_uinode_background_colorsextract_uinode_images
  • bevy_ui::extract_text_uinodes 已被重命名为 extract_uinode_text

extract_default_ui_camera_view() 系统中删除通用相机 #

bevy::ui::render::extract_default_ui_camera_view() 系统现在已硬编码为 Camera2dCamera3d 组件,并且不再为每种类型添加两次。

此更改是为了修复在将渲染阶段移至资源后引入的错误。该系统所做的第一件事就是清除上一个帧中的所有实体。通过拥有两个独立的系统,一个系统总是会清除另一个系统,导致某些实体无法渲染。

need_new_surfaces() 系统重命名为 need_surface_configuration() #

need_new_surfaces() 系统已重命名为 need_surface_configuration(),作为修复 Bevy 应用程序在更改屏幕方向时会崩溃在 iOS 上的错误的一部分。

要求窗口系统在 WindowWrapper 中存储窗口 #

窗口系统现在需要将其窗口存储在新的 WindowWrapper 中,以便 Bevy 可以控制何时将其删除。这修复了许多与在管道渲染器完成绘制到窗口之前删除窗口相关的错误和崩溃。

任务 #

向并行迭代帮助器添加索引参数 #

作为参数传递给 par_chunk_map()par_splat_map()par_chunk_map_mut()par_splat_map_mut() 的闭包现在将接受一个额外的索引参数,指定正在处理切片的哪一部分。

// 0.13
items.par_chunk_map(&task_pool, 100, |chunk| {
    // ...
});

// 0.14
items.par_chunk_map(&task_pool, 100, |_index, chunk| {
    // ...
});

UI #

修复生成 NodeBundle 会销毁之前生成的 NodeBundle #

在 0.13.1 中,NodeBundle 会在生成时销毁之前的 NodeBundle,原始解决方法是为所有根节点添加 position_type: Absolute。此错误现已修复,因此您可以删除该解决方法。

Rect::inset() 重命名为 inflate() #

Rect::inset()IRect::inset()URect::inset() 已被重命名为 inflate() 以适应实际的行为。

将默认字体大小更新为 24px #

TextStyle 的默认字体大小已从 12px 增加到 24px。如果您更喜欢原始大小,可以使用 TextStyle::font_size 属性进行覆盖。

实用工具 #

bevy::utils/bevy::core 的重新导出 crate 分开 #

bevy::utils 不再重新导出 petgraphuuidnonmaxsmallvecthiserror。此外,bevy::core 不再重新导出 bytemuckbytes_ofcast_slicePodZeroable

如果您需要这些依赖项,可以将它们添加到您自己的 Cargo.toml 中。

窗口系统 #

重新添加 Window::fit_canvas_to_parent #

Window::fit_canvas_to_parent 是 WASM 上的一个属性,它会自动将 canvas 元素的大小调整为其父级的大小,通常是屏幕。它在 0.13 中被删除,但这最终成为了问题,因为许多用户在无法自定义 CSS 时依赖于它的行为。它现在已被重新添加以满足这种需求。

关闭窗口时将其从 WinitWindows 中删除 #

WinitWindows::get_window_entity 现在在窗口关闭后返回 None,而不是返回一个不再存在的实体。

使窗口在请求关闭后关闭帧 #

窗口现在在请求退出后关闭一帧,以修复几个回归。如果您有自定义退出逻辑,请确保它不依赖于应用程序在窗口关闭的同一帧中退出。

升级到 winit 0.30 #

自定义 UserEvent 现在已重命名为 WakeUp,用于在应用程序外部发生任何事情时唤醒循环(一个新的 custom_user_event 显示了此行为)。

内部 UpdateState 已被删除,并在内部被 AppLifecycle 替换。更改时,AppLifecycle 将作为事件发送。

UpdateMode 现在只接受两个值:ContinuousReactive,但后者公开了 3 个新属性以启用对设备、用户或窗口事件的响应。以前的 UpdateMode::Reactive 现在等效于 UpdateMode::reactive(),而 UpdateMode::ReactiveLowPower 等效于 UpdateMode::reactive_low_power()

ApplicationLifecycle 已被重命名为 AppLifecycle,现在包含事件循环中应用程序状态的可能值。

  • Idle:循环尚未开始。
  • Running(以前称为 Started):循环正在运行。
  • WillSuspend:循环将要被挂起。
  • Suspended:循环被挂起。
  • WillResume:循环将要恢复。

注意:Resumed 状态已被删除,因为恢复的应用程序正在运行。

最后,现在 winit 启用了这一点,它扩展了 WinitPlugin 以支持自定义事件。

没有区域 #

修复 Node2d 拼写错误 #

bevy::core_pipeline::core_2d::graph 中的 Node2d::ConstrastAdaptiveSharpening 已经重命名以修复拼写错误。它最初为 Constrast,现在已更名为 Contrast

// 0.13
Node2D::ConstrastAdaptiveSharpening

// 0.14
Node2D::ContrastAdaptiveSharpening

更新到 fixedbitset 0.5 #

bevy::ecs::query 中的 Access::grow 已被移除。现在许多操作会自动扩展容量。

// 0.13
let mut access = Access::new();
access.grow(1);
// Other operations...

// 0.14
let mut access = Access::new();
// Other operations...

将 WASM panic 处理程序从 LogPlugin 移动到 PanicHandlerPlugin #

LogPlugin 以前会静默覆盖 WASM 目标上的 panic 处理程序。此功能现已拆分到新的 PanicHandlerPlugin 中,该插件已添加到 DefaultPlugins 中。

如果希望在 WASM 上获得更友好的错误消息,但没有使用 DefaultPlugins,请确保手动将 PanicHandlerPlugin 添加到应用程序中。

App::new()
    .add_plugins((MinimalPlugins, PanicHandlerPlugin))
    .run()