Migration Guides

Draft Page

The page is still under development (Tracking Issue: 1188). This page and all pages under it will be hidden from search engines and menus!

Migration Guide: 0.13 to 0.14

Fix Node2d typo #

Node2d::ConstrastAdaptiveSharpening from bevy::core_pipeline::core_2d::graph has been renamed to fix a typo. It was originally Constrast, but is now Contrast.

// Before
Node2D::ConstrastAdaptiveSharpening

// After
Node2D::ContrastAdaptiveSharpening

Update to fixedbitset 0.5 #

Access::grow from bevy::ecs::query has been removed. Many operations now automatically grow the capacity.

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

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

Move WASM panic handler from LogPlugin to PanicHandlerPlugin #

LogPlugin used to silently override the panic handler on WASM targets. This functionality has now been split out into the new PanicHandlerPlugin, which was added to DefaultPlugins.

If you want nicer error messages on WASM but don't use DefaultPlugins, make sure to manually add PanicHandlerPlugin to the app.

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

AnimationClip now uses UUIDs and NoOpTypeIdHash is now NoOpHash #

Animation

AnimationClip now uses UUIDs instead of hierarchical paths based on the Name component to refer to bones. This has several consequences:

  • A new component, AnimationTarget, should be placed on each bone that you wish to animate, in order to specify its UUID and the associated AnimationPlayer. The glTF loader automatically creates these components as necessary, so most uses of glTF rigs shouldn’t need to change.
  • Moving a bone around the tree, or renaming it, no longer prevents an AnimationPlayer from affecting it.
  • Dynamically changing the AnimationPlayer component will likely require manual updating of the AnimationTarget components.

Entities with AnimationPlayer components may now possess descendants that also have AnimationPlayer components. They may not, however, animate the same bones.

Furthermore, NoOpTypeIdHash and NoOpTypeIdHasher have been renamed to NoOpHash and NoOpHasher.

Multiplying LinearRgba by f32 no longer ignores alpha channel #

Animation
Color
Math
Rendering

It was previously possible to multiply and divide a Color by an f32, which is now removed. You must now operate on a specific color space, such as LinearRgba. Furthermore, these operations used to skip the alpha channel, but that is no longer the case.

// Before
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);

// After
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);

If you need the alpha channel to remain untouched, consider creating your own helper method:

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);

If you are fine with the alpha changing, but need it to remain within the range of 0.0 to 1.0, consider clamping it:

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);

Note that in some cases, such as rendering sprites, the alpha is automatically clamped so you do not need to do it manually.

Separate SubApp from App #

App

SubApp has been separated from App, so there are a few larger changes involved when interacting with these types.

Constructing a SubApp #

SubApp no longer contains an App, so you no longer are able to convert an App into a SubApp. Furthermore, the extraction function must now be set outside of the constructor.

// Before
#[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.
}));

// After
#[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 changes #

App is not Send anymore, but SubApp still is.

Due to the separation of App and SubApp, a few other methods have been changed.

First, App::world as a property is no longer directly accessible. Instead use the getters App::world and App::world_mut.

#[derive(Component)]
struct MyComponent;

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

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

Secondly, all getters for the sub app now return a SubApp instead of an App. This includes App::sub_app, App::sub_app_mut, App::get_sub_app, and App::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>());

3rd-party traits on App #

If you implemented an extension trait on App, consider also implementing it on 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
    }
}

Make AppExit more specific about exit reason #

App

The AppExit event is now an enum that represents whether the code exited successfully or not. If you construct it, you must now specify Success or Error:

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

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

If you subscribed to this event in a system, consider matching whether it was a success or an error:

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

// After
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})"),
        }
    }
}

Furthermore, App::run now returns AppExit instead of the unit type (). Since AppExit implements Termination, you can now return it from the main function.

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

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

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

Deprecate dynamic plugins #

App

Dynamic plugins are now deprecated. If possible, remove all usage them from your code:

// Before
// 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();

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

impl Plugin for MyPlugin {
    // ...
}

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

If you are unable to do that, you may temporarily silence the deprecation warnings by annotating all usage with #[allow(deprecated)]. Please note that the current dynamic plugin system will be removed by the next major Bevy release, so you will have to migrate eventually. You may be interested in these safer, related links:

If you truly cannot go without dynamic plugins, you may copy the code from Bevy and add it to your project locally.

Move state initialization methods to bevy::state #

App
ECS

State has been moved to bevy::state. With it, App::init_state has been moved from a normal method to an extension trait. You may now need to import AppExtStates in order to use this method. (This trait is behind the bevy_app feature flag, which you may need to enable.)

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

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

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

If you import the prelude (such as with use bevy::prelude::*), you do not need to do this.

Remove the UpdateAssets and AssetEvents schedules #

Assets

The UpdateAssets schedule has been removed. If you add systems to this schedule, move them to run on PreUpdate. (You may need to configure the ordering with system.before(...) and system.after(...).)

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

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

Furthermore, AssetEvents has been changed from a ScheduleLabel to a SystemSet within the First schedule.

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

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

Use async fn in traits rather than BoxedFuture #

Assets

In Rust 1.75, async fn was stabilized for traits. Some traits have been switched from returning BoxedFuture to be an async fn, specifically:

  • AssetReader
  • AssetWriter
  • AssetLoader
  • AssetSaver
  • Process

Please update your trait implementations:

// Before
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)
        })
    }
}

// After
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)
    }
}

Because these traits now use async, they are no longer object safe. If you need to receive or store &dyn Trait, use the &dyn ErasedTrait variant instead. For instance:

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

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

Add Ignore variant to ProcessResult #

Assets

The ProcessResult enum, used in asset loading, has a new Ignore variant. You may need to update your match statements.

Removed Into<AssedId<T>> for Handle<T> #

Assets

Converting from a Handle to an AssetId using Into was removed because it was a footgun that could potentially drop the asset if the Handle was a strong reference. If you need the AssetId, please use Handle::id() instead.

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

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

Add AsyncSeek trait to Reader to be able to seek inside asset loaders #

Assets

The asset loader's Reader type alias now requires the new AsyncSeek trait. Please implement AsyncSeek for any structures that must be a Reader, or use an alternative if seeking is not supported.

Add error info to LoadState::Failed #

Assets

Rust prides itself on its error handling, and Bevy has been steadily catching up. Previously, when checking if an asset was loaded using AssetServer::load_state (and variants), the only information returned on an error was the empty LoadState::Failed. Not very useful for debugging!

Now, a full AssetLoadError is included inside Failed to tell you exactly what went wrong. You may need to update your match and if let statements to handle this new value:

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

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

Furthermore, the Copy, PartialOrd, and Ord implementations have been removed from LoadState. You can explicitly call .clone() instead of copying the enum, and you can manually re-implement Ord as a helper method if required.

Make AssetMetaCheck a field of AssetPlugin #

Assets

AssetMetaCheck is used to configure how the AssetPlugin reads .meta files. It was previously a resource, but now has been changed to a field in AssetPlugin. If you use DefaultPlugins, you can use .set to configure this field.

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

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

Make LoadContext use the builder pattern #

Assets

LoadContext, used by AssetLoader, has been updated so all of its load_* methods have been merged into a builder struct.

// Before
load_context.load_direct(path);
// After
load_context.loader().direct().untyped().load(path);

// Before
load_context.load_direct_with_reader(reader, path);
// After
load_context.loader().direct().with_reader(reader).untyped().load(path);

// Before
load_context.load_untyped(path);
// After
load_context.loader().untyped().load(path);

// Before
load_context.load_with_settings(path, settings);
// After
load_context.loader().with_settings(settings).load(path);

Use RenderAssetUsages to configure gLTF meshes & materials during load #

Assets
Rendering

It is now possible configure whether meshes and materials should be loaded in the main world, the render world, or both with GltfLoaderSettings. The load_meshes field has been changed from a bool to a RenderAssetUsages bitflag, and a new load_materials field as been added.

You may need to update any gLTF .meta files:

// Before
load_meshes: true

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

If you use AssetServer::load_with_settings instead when loading gLTF files, you will also have to update:

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

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

Consolidate RenderMaterials and similar into RenderAssets, implement RenderAsset for destination type #

Assets
Rendering

RenderMaterials, RenderMaterials2d, and RenderUiMaterials have all been replaced with the RenderAssets resource. If you need access a PreparedMaterial<T> using an AssetId, use RenderAssets::get instead.

Furthermore, the RenderAsset trait should now be implemented for destination types rather than source types. If you need to access the source type, use the RenderAsset::SourceAsset associated type.

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

    // ...
}

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

    // ...
}

Fix leftover references to children when despawning audio entities #

Audio

You can configure the behavior of spawned audio with the PlaybackMode enum. One of its variants, PlaybackMode::Despawn, would despawn the entity when the audio finished playing.

There was previously a bug where this would only despawn the entity and not its children. This has been fixed, so now despawn_recursive() is called when the audio finishes.

If you relied on this behavior, consider using PlaybackMode::Remove to just remove the audio components from the entity or AudioSink::empty() to check whether any audio is finished and manually despawn() it.

Move WGSL math constants and color operations from bevy_pbr to bevy_render #

Color

Mathematical constants and color conversion functions for shaders have been moved from bevy_pbr::utils to bevy_render::maths and bevy_render::color_operations. If you depended on these in your own shaders, please update your import statements:

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

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

Overhaul Color #

Color
Gizmos
Rendering
Text
UI

Bevy's color support has received a major overhaul, and with it the new bevy::color module. Buckle up, many things have been changed!

Color space representation #

Bevy's main Color enum is used to represent color in many different color spaces (such as RGB, HSL, and more). Before, these color spaces were all represented inline as variants:

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

This has been changed so now each color space has its own dedicated struct:

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),
    // ...
}

This makes it easier to organize and manage different color spaces, and many more color spaces have been added too! To handle this change, you may need to update your match statements:

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

// After
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!
    // },
    _ => {}
}

Additionally, you must now use the From and Into implementations when converting between color spaces, as compared to the old helper methods such as as_rgba and as_hsla.

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

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

Color methods #

Any mention of RGB has been renamed to sRGB. This includes the variant Color::Rgba turning into Color::Srgba as well as methods such as Color::rgb and Color::rgb_u8 turning into Color::srgb and Color::srgb_u8.

Methods to access specific channels of Color have been removed due to causing silent, relatively expensive conversions. This includes Color::r, Color::set_r, Color::with_r, and all of the equivalents for g, b h, s and l. Convert your Color into the desired color space, perform your operation there, and then convert it back.

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

// After
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 has been moved to Srgba::hex. Call .into() or construct a Color::Srgba variant manually to convert it.

Color::rgb_linear and Color::rgba_linear have been renamed Color::linear_rgb and Color::linear_rgba to fit the naming scheme of the LinearRgba struct.

Color::as_linear_rgba_f32 and Color::as_linear_rgba_u32 have been removed. Call LinearRgba::to_f32_array and LinearRgba::to_u32 instead, converting if necessary.

Several other color conversion methods to transform LCH or HSL colors into float arrays or Vec types have been removed. Please reimplement these externally or open a PR to re-add them if you found them particularly useful.

Vector field arithmetic operations on Color (add, subtract, multiply and divide by a f32) have been removed. Instead, convert your colors into LinearRgba space and perform your operations explicitly there. This is particularly relevant when working with emissive or HDR colors, whose color channel values are routinely outside of the ordinary 0 to 1 range.

Alpha #

Alpha, also known as transparency, used to be referred to by the letter a. It is now called by its full name within structs and methods.

  • Color::set_a, Color::with_a, and Color::a are now Color::set_alpha, Color::with_alpha, and Color::alpha. These are part of the new Alpha trait.
  • Additionally, Color::is_fully_transparent is now part of the Alpha.

CSS Constants #

The various CSS color constants are no longer stored directly on Color. Instead, they’re defined in the Srgba color space, and accessed via bevy::color::palettes. Call .into() on them to convert them into a Color for quick debugging use.

// Before
let color = Color::BLUE;

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

let color = BLUE;

Please note that palettes::css is not necessarily 1:1 with the constants defined previously as some names and colors have been changed to conform with the CSS spec. If you need the same color as before, consult the table below or use the color values from the old constants.

0.130.14
GREENLIMEGREEN
PINKDEEP_PINK
DARK_GRAYSrgba::gray(0.25)

Switch to LinearRgba #

WireframeMaterial, ExtractedUiNode, ExtractedDirectionalLight, ExtractedPointLight, ExtractedSpotLight, and ExtractedSprite now store a LinearRgba rather than a polymorphic Color. Furthermore, Color no longer implements AsBindGroup. You should store a LinearRgba instead to avoid conversion costs.

Remove old color space utilities #

Color
Rendering

The SrgbColorSpace trait, HslRepresentation struct, and LchRepresentation struct have been removed in favor of the specific color space structs.

For SrgbColorSpace, use Srgba::gamma_function() and Srgba::gamma_function_inverse(). If you used the SrgbColorSpace implementation for u8, convert it to an f32 first:

// 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;

Note that this conversion can be costly, especially if called during the Update schedule. Consider just using f32 instead.

HslRepresentation and LchRepresentation can be replaced with the From implementations between Srgba, Hsla, and Lcha.

// Before
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]);

// After
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();

Use LinearRgba in ColorAttachment #

Color
Rendering

ColorAttachment::new() now takes Option<LinearRgba> instead of Option<Color> for the clear_color. You can use the From<Color> implementation to convert your color.

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

Remove close_on_esc #

Dev-Tools

The close_on_esc system was removed because it was too opiniated and lacked customization. If you used this system, you may copy its contents below:

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();
        }
    }
}

You may be interested in using the built-in keybinds provided by the operating system instead, such as Alt+F4 and Command+Q.

Make sysinfo diagnostic plugin optional #

Diagnostics

For users who disable default features of bevy and wish to enable the diagnostic plugin, add sysinfo_plugin to your bevy features list.

Improve tracing layer customization #

Diagnostics

The LogPlugin’s update_subscriber field has been replaced with the more flexible custom_layer.

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

LogPlugin {
    update_subscriber: Some(update_subscriber),
    ..default()
}

// in 0.14
fn custom_layer(_app: &mut App) -> Option<BoxedLayer> {
    // You can provide multiple layers like this, since Vec<Layer> is also a layer:
    Some(Box::new(vec![
        bevy::log::tracing_subscriber::fmt::layer()
            .with_file(true)
            .boxed(),
        CustomLayer.boxed(),
    ]))
}
LogPlugin {
    custom_layer,
    ..default()
}

Immediately apply deferred system params in System::run #

ECS

System::run will now always run System::apply_deferred immediately after running the system now. If you were running systems and then applying their deferred buffers at a later point in time, you can eliminate the latter.

// in 0.13
system.run(world);
// .. sometime later ...
system.apply_deferred(world);

// in 0.14
system.run(world);

Move commands module into bevy::ecs::world #

ECS

Command and CommandQueue have been moved from bevy::ecs::system to bevy::ecs::world.

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

Remove ComponentStorage and associated types #

ECS

If you were manually implementing Component instead of using the derive macro, replace the associated Storage associated type with the STORAGE_TYPE const:

// in Bevy 0.13
impl Component for MyComponent {
    type Storage = TableStorage;
}
// in Bevy 0.14
impl Component for MyComponent {
    const STORAGE_TYPE: StorageType = StorageType::Table;
}

Component is no longer object safe. If you were relying on &dyn Component, Box<dyn Component>, etc. please file an issue to get this change reverted.

Clean up type registrations #

ECS
Reflection

External types are no longer registered automatically unless they are used by other Bevy types. If you were depending on std, glam or similar types being in the type registry you need to manually register them with .register_type().

Remove archetype_component_access from QueryState #

ECS

QueryState::archetype_component_access has been removed. This can be worked around by accessing the surrounding SystemState’s instead. If you needed this explicitly for QueryState, please file an issue.

Remove WorldCell #

ECS

WorldCell has been removed. If you were using it to fetch multiple distinct values from a &mut World, use SystemState by calling SystemState::get instead. Alternatively, if SystemState cannot be used, UnsafeWorldCell can instead be used in unsafe contexts.

Allow Commands to register systems #

ECS
  • Changed SystemId fields from tuple struct to a normal struct If you want to access the entity field, you should use SystemId::entity instead of SystemId::0

Store only the IDs needed for Query iteration #

ECS

QueryState::matched_tables and QueryState::matched_archetypes does not return a reference to a slice, but an iterator instead. You may need to use iterator combinators or collect them into a Vec to use it as a slice.

Remove stepping from default features #

ECS

The system-by-system stepping feature is now disabled by default; to use it, enable the bevy_debug_stepping feature explicitly:

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

Code using Stepping will still compile with the feature disabled, but will print a runtime error message to the console if the application attempts to enable stepping.

Optimize Event Updates #

ECS

TODO

Make SystemParam::new_archetype and QueryState::new_archetype unsafe functions #

ECS

QueryState::new_archetype and SystemParam::new_archetype are now an unsafe functions that must be sure that the provided Archetype is from the same World that the state was initialized from. Callers may need to add additional assertions or propagate the safety invariant upwards through the callstack to ensure safety.

Better SystemId to Entity conversions #

ECS
let system_id = world.register_system(my_sys);

// old
let entity = Entity::from(system_id);

// new
let entity = system_id.entity();

Computed State & Sub States #

ECS

NextState is now an enum. If you were constructing it manually or matching on its value, you will need to use the equivalent enum variants.

0.130.14
NextState(Some(S))NextState::Pending(S)
NextState(None)NextState::Unchanged

If you were manually adding apply_state_transition to your app, add an exclusive system that runs the StateTransition schedule instead.

Separate state crate #

ECS

States were moved to a separate crate which is gated behind the bevy_state feature. Projects that use state but don't use Bevy's default-features will need to add that feature to their Cargo.toml.

Projects that use bevy_ecs directly and use states will need to add the bevy_state crate as a dependency.

Projects that use bevy_app directly and use states will need to add the bevy_state feature.

If you do not use DefaultPlugins, you will need to add the StatesPlugin manually to your app.

Users should update imports that referenced the old location.

// 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;

constrain WorldQuery::get_state to only use &Components #

ECS

Users of WorldQuery::get_state or transmute, transmute_filtered, join and join_filtered methods on QueryState now need to pass &Components instead of &World. &Components can be trivially obtained from either components method on &World or UnsafeWorldCell. For implementors of WorldQuery::get_state that were accessing more than the Components inside &World and its methods, this is no longer allowed.

constrain WorldQuery::init_state argument to ComponentInitializer #

ECS

Instead of passing &mut World to WorldQuery::init_state directly, pass in a mutable reference to the struct returned from World::component_initializer.

Unify transition names to exited and entered #

ECS
  • StateTransitionEvent<S> and OnTransition<S> schedule had their fields renamed to exited and entered to match schedules.

Combine transition systems of Substates #

ECS
  • apply_state_transition is no longer publicly available, run the StateTransition schedule instead.

Replace FromWorld requirement on ReflectResource and reflect Resource for State<S> #

ECS
Reflection
  • Users of #[reflect(Resource)] will need to also implement/derive FromReflect (should already be the default).
  • Users of #[reflect(Resource)] may now want to also add FromWorld to the list of reflected traits in case their FromReflect implementation may fail.
  • Users of ReflectResource will now need to pass a &TypeRegistry to its insert, apply_or_insert and copy methods.

Make some ReflectComponent/ReflectBundle methods work with EntityMut too #

ECS
Reflection
  • ReflectComponentFns’s apply and reflect_mut fields now take EntityMut instead of &mut EntityWorldMut
  • ReflectBundleFns’s apply field now takes EntityMut instead of &mut EntityWorldMut

Make from_reflect_or_world also try ReflectDefault and improve some comments and panic messages #

ECS
Reflection

ReflectBundle::insert now requires an additional &TypeRegistry parameter.

Generalize component reflection to operate on FilteredEntityRef and FilteredEntityMut, not EntityRef and EntityMut. #

ECS
Reflection
  • ReflectComponent::contains, ReflectComponent::reflect, and ReflectComponent::reflect_mut now take FilteredEntityRef (in the case of contains() and reflect()) and FilteredEntityMut (in the case of reflect_mut()) parameters. FilteredEntityRef and FilteredEntityMut have very similar APIs to EntityRef and EntityMut respectively, but optionally restrict the components that can be accessed.

multi_threaded feature rename #

ECS
Tasks

The multi-threaded feature was renamed to multi_threaded for bevy, bevy_asset, bevy_ecs, bevy_render, bevy_tasks, and bevy_internal. Please update your Cargo.toml if you manually specify Bevy features.

Moves intern and label modules into bevy_ecs #

ECS
Utils
  • Replace bevy_utils::define_label imports with bevy_ecs::define_label imports.
  • Replace bevy_utils::label::DynEq imports with bevy_ecs::label::DynEq imports.
  • Replace bevy_utils::label::DynHash imports with bevy_ecs::label::DynHash imports.
  • Replace bevy_utils::intern::Interned imports with bevy_ecs::intern::Interned imports.
  • Replace bevy_utils::intern::Internable imports with bevy_ecs::intern::Internable imports.
  • Replace bevy_utils::intern::Interner imports with bevy_ecs::intern::Interner imports.

Implement the AnimationGraph, allowing for multiple animations to be blended together. #

Editor
  • AnimationPlayers can no longer play animations by themselves and need to be paired with a Handle<AnimationGraph>. Code that was using AnimationPlayer to play animations will need to create an AnimationGraph asset first, add a node for the clip (or clips) you want to play, and then supply the index of that node to the AnimationPlayer’s play method.

  • The AnimationPlayer::play_with_transition() method has been removed and replaced with the AnimationTransitions component. If you were previously using AnimationPlayer::play_with_transition(), add all animations that you were playing to the AnimationGraph, and create an AnimationTransitions component to manage the blending between them.

Gizmo line joints #

Gizmos

Any manually created GizmoConfigs must now set the .line_joints field.

Gizmo line styles #

Gizmos

Any manually created GizmoConfig must now include the line_style attribute

Inconsistent segments/resolution naming #

Gizmos

When working with gizmos, replace all calls to .segments(...) with .resolution(...)

Make gizmos take primitives by ref #

Gizmos
  • Any usages of gizmos.primitive_2d() and/or gizmos.primitive_3d() need to be updated to pass the primitive in by reference.

More gizmos builders #

Gizmos
  • Some gizmos.primitive_nd methods now return some or different builders. You may need to adjust types and match statements
  • Replace any calls to circle_segments() with .segments()

rename touchpad to gesture, and add new gestures #

Input
  • TouchpadMagnify has been renamed to PinchGesture
  • TouchpadRotate has been renamed to RotationGesture

Deprecate ReceivedCharacter #

Input
Windowing

ReceivedCharacter is now deprecated, use KeyboardInput instead.

// 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 before.
    // Instead, check for them with `Key::Space` and `Key::Tab`.
    match &event.logical_key {
        Key::Character(character) => {
            info!("{} pressed.", character);
        }
        Key::Space => {
            info!("Space pressed.");
        }
        _ => {}
    }
  }
}

flush key_input cache when Bevy loses focus (Adopted) #

Input
Windowing

WinitEvent has a new enum variant: WinitEvent::KeyboardFocusLost.

Add Direction3dA and move direction types out of primitives #

Math

The Direction2d, Direction3d, and InvalidDirectionError types have been moved out of bevy::math::primitives.

Before:

use bevy::math::primitives::Direction3d;

After:

use bevy::math::Direction3d;

Rename Direction2d/3d to Dir2/3 #

Math

The Direction2d and Direction3d types have been renamed to Dir2 and Dir3.

Make cardinal splines include endpoints #

Math

Any users relying on the old behavior of CubicCardinalSpline will have to truncate any parametrizations they used in order to access a curve identical to the one they had previously. This would be done by chopping off a unit-distance segment from each end of the parametrizing interval. For instance, if a user’s existing code looks as follows

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)
}

then in order to obtain similar behavior, t will need to be shifted up by 1, since the output of CubicCardinalSpline::to_curve has introduced a new segment in the interval [0,1], displacing the old segment from [0,1] to [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();
    my_curve.position(t+1)
}

(Note that this does not provide identical output for values of t outside of the interval [0,1].)

On the other hand, any user who was specifying additional endpoint tangents simply to get the curve to pass through the right points (i.e. not requiring exactly the same output) can simply omit the endpoints that were being supplied only for control purposes.

Move Point out of cubic splines module and expand it #

Math

Since Point no longer exists, any projects using it must switch to bevy::math::VectorSpace. Additionally, third-party implementations of this trait now require the Neg trait; the constant VectorSpace::ZERO must be provided as well.

Remove VectorSpace impl on Quat #

Math

Quat no longer implements VectorSpace as unit quaternions don’t actually form proper vector spaces. If you’re absolutely certain that what you’re doing is correct, convert the Quat into a Vec4 and perform the operations before converting back.

Meshing for Triangle3d primitive #

Math

The UV-mapping of Triangle2d has changed with this PR; the main difference is that the UVs are no longer dependent on the triangle’s absolute coordinates, but instead follow translations of the triangle itself in its definition. If you depended on the old UV-coordinates for Triangle2d, then you will have to update affected areas to use the new ones which, briefly, can be described as follows:

  • The first coordinate is parallel to the line between the first two vertices of the triangle.
  • The second coordinate is orthogonal to this, pointing in the direction of the third point.

Generally speaking, this means that the first two points will have coordinates [_, 0.], while the third coordinate will be [_, 1.], with the exact values depending on the position of the third point relative to the first two. For acute triangles, the first two vertices always have UV-coordinates [0., 0.] and [1., 0.] respectively. For obtuse triangles, the third point will have coordinate [0., 1.] or [1., 1.], with the coordinate of one of the two other points shifting to maintain proportionality.

For example:

  • The default Triangle2d has UV-coordinates [0., 0.], [0., 1.], [0.5, 1.].
  • The triangle with vertices vec2(0., 0.), vec2(1., 0.), vec2(2., 1.) has UV-coordinates [0., 0.], [0.5, 0.], [1., 1.].
  • The triangle with vertices vec2(0., 0.), vec2(1., 0.), vec2(-2., 1.) has UV-coordinates [2./3., 0.], [1., 0.], [0., 1.].

Use Vec3A for 3D bounding volumes and raycasts #

Math

3D bounding volumes now use Vec3A types internally, return values from methods on them now return Vec3A instead of Vec3

Update glam version requirement from 0.25 to 0.27 #

Math

When using glam exports, keep in mind that vector fract() method now matches Rust implementation (that is self - self.trunc() instead of self - self.floor()). If you want to use the GLSL implementation you should now use fract_gl().

Common MeshBuilder trait #

Math
Rendering

When calling .build() you need to import bevy_render::mesh::primitives::MeshBuilder

Additional options to mesh primitives #

Math
Rendering

Add subdivisions to PlaneMeshBuilder #

Math
Rendering

If you were using Plane subdivisions, you now need to use Plane3d::default().mesh().subdivisions(10)

fixes https://github.com/bevyengine/bevy/issues/13258

Make Transform::rotate_axis and Transform::rotate_local_axis use Dir3 #

Math
Transform

All calls to Transform::rotate_axis and Transform::rotate_local_axis will need to be updated to use a Dir3 for the axis parameter rather than a Vec3. For a general input, this means calling Dir3::new and handling the Result, but if the previous vector is already known to be normalized, Dir3::new_unchecked can be called instead. Note that literals like Vec3::X also have corresponding Dir3 literals; e.g. Dir3::X, Dir3::NEG_Y and so on.

Use Dir3 for local axis methods in GlobalTransform #

Math
Transform

The GlobalTransform component’s directional axis methods (e.g., right(), left(), up(), down(), back(), forward()) have been updated from returning Vec3 to Dir3.

Fix Ord and PartialOrd differing for FloatOrd and optimize implementation #

Math
Utils

If you were depending on the PartialOrd behaviour of FloatOrd, it has changed from matching f32 to matching FloatOrd’s Ord ordering, never returning None.

Move FloatOrd into bevy_math #

Math
Utils

FloatOrd has been moved to bevy_math.

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

reflect: treat proxy types correctly when serializing #

Reflection

If ReflectSerialize is registered on a type, but TypePath or FromReflect implementations are omitted (perhaps by #[reflect(type_path = false) or #[reflect(from_reflect = false)]), the traits must now be implemented.

bevy_reflect: Recursive registration #

Reflection

All types that derive Reflect will now automatically add GetTypeRegistration as a bound on all (unignored) fields. This means that all reflected fields will need to also implement GetTypeRegistration.

If all fields derive Reflect or are implemented in bevy_reflect, this should not cause any issues. However, manual implementations of Reflect that excluded a GetTypeRegistration impl for their type will need to add one.

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

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

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

bevy_reflect: Rename UntypedReflectDeserializer to ReflectDeserializer #

Reflection

UntypedReflectDeserializer has been renamed to ReflectDeserializer. Usages will need to be updated accordingly.

- let reflect_deserializer = UntypedReflectDeserializer::new(&registry);
+ let reflect_deserializer = ReflectDeserializer::new(&registry);

Implement Reflect for Result<T, E> as enum #

Reflection

Result<T, E> has had its Reflect implementation changed to align it with Option<T> and its intended semantics: A carrier of either an Ok or Err value, and the ability to access it. To achieve this it is no longer a ReflectKind::Value but rather a ReflectKind::Enum and as such carries these changes with it:

For Result<T, E>

  • Both T and E no longer require to be Clone and now require to be FromReflect
  • <Result<T, E> as Reflect>::reflect_* now returns a ReflectKind::Enum, so any code that previously relied on it being a Value kind will have to be adapted.
  • Result<T, E> now implements Enum

Since the migration is highly dependent on the previous usage, no automatic upgrade path can be given.

Fix TypeRegistry use in dynamic scene #

Reflection
Scenes
  • SceneSerializer and all related serializing helper types now take a &TypeRegistry instead of a &TypeRegistryArc. You can upgrade by getting the former from the latter with TypeRegistryArc::read(), e.g.
  let registry_arc: TypeRegistryArc = [...];
- let serializer = SceneSerializer(&scene, &registry_arc);
+ let registry = registry_arc.read();
+ let serializer = SceneSerializer(&scene, &registry);
  • Rename DynamicScene::serialize_ron() to serialize().

rename Camera3dBundle's 'dither' field to 'deband_dither' to align with Camera2dBundle #

Rendering

use the new deband_dither field name with Camera3dBundle, rather than the old field name, dither

Add support for KHR_texture_transform #

Rendering

Rename affine_to_square to affine3_to_square

Move AlphaMode into bevy_render #

Rendering

In the present implementation, external consumers of AlphaMode will have to access it through bevy_render rather than through bevy_pbr, changing their import from bevy_pbr::AlphaMode to bevy_render::alpha::AlphaMode (or the corresponding glob import from bevy_pbr::prelude::* to bevy_render::prelude::*).

Prefer UVec2 when working with texture dimensions #

Rendering

Change floating point types (Vec2, Rect) to their respective unsigned integer versions (UVec2, URect) when using GpuImage, TextureAtlasLayout, TextureAtlasBuilder, DynamicAtlasTextureBuilder or FontAtlas.

Fix CameraProjectionPlugin not implementing Plugin in some cases #

Rendering

CameraProjectionPlugin<T>’s trait bounds now require T to implement CameraProjection, Component, and GetTypeRegistration. This shouldn’t affect most existing code as CameraProjectionPlugin<T> never implemented Plugin unless those bounds were met.

Add random shader utils, fix cluster_debug_visualization #

Rendering

The bevy_pbr::utils::random1D shader function has been replaced by the similar bevy_pbr::utils::rand_f.

Intern mesh vertex buffer layouts so that we don't have to compare them over and over. #

Rendering

Duplicate MeshVertexBufferLayouts are now combined into a single object, MeshVertexBufferLayoutRef, which contains an atomically-reference-counted pointer to the layout. Code that was using MeshVertexBufferLayout may need to be updated to use MeshVertexBufferLayoutRef instead.

Batching: replace GpuArrayBufferIndex::index with a u32 #

Rendering

GpuArrayBufferIndex::index is now a u32 instead of a NonMaxU32. Remove any calls to NonMaxU32::get on the member.

Solve some oklaba inconsistencies #

Rendering

If you were creating a Oklaba instance directly, instead of using L, you should use lightness

// Before
let oklaba = Oklaba { l: 1., ..Default::default() };

// Now
let oklaba = Oklaba { lightness: 1., ..Default::default() };

if you were using the function Oklaba::lch, now the method is named Oklaba::lab

Add setting to enable/disable shadows to MaterialPlugin #

Rendering

MaterialPlugin now has a shadows_enabled setting, if you didn’t spawn the plugin using ::default() or ..default(), you’ll need to set it. shadows_enabled: true is the same behavior as the previous version, and also the default value.

Implement maths and Animatable for Srgba #

Rendering

The previously existing implementation of mul/div for Srgba did not modify alpha but these operations do modify alpha now. Users need to be aware of this change.

Remove needless color specializaion for SpritePipeline #

Rendering

The raw values for the HDR, TONEMAP_IN_SHADER and DEBAND_DITHER flags have changed, so if you were constructing the pipeline key from raw u32s you’ll have to account for that.

Improve performance by binning together opaque items instead of sorting them. #

Rendering

PhaseItem has been split into BinnedPhaseItem and SortedPhaseItem. If your code has custom PhaseItems, you will need to migrate them to one of these two types. SortedPhaseItem requires the fewest code changes, but you may want to pick BinnedPhaseItem if your phase doesn’t require sorting, as that enables higher performance.

remove DeterministicRenderingConfig #

Rendering

Removed DeterministicRenderingConfig. There shouldn’t be any z fighting anymore in the rendering even without setting stable_sort_z_fighting

Micro-optimize queue_material_meshes, primarily to remove bit manipulation. #

Rendering
  • The primitive_topology field on GpuMesh is now an accessor method: GpuMesh::primitive_topology().
  • For performance reasons, MeshPipelineKey has been split into BaseMeshPipelineKey, which lives in bevy_render, and MeshPipelineKey, which lives in bevy_pbr. These two should be combined with bitwise-or to produce the final MeshPipelineKey.

Disable RAY_QUERY and RAY_TRACING_ACCELERATION_STRUCTURE by default #

Rendering

If you need wgpu::Features::RAY_QUERY or wgpu::Features::RAY_TRACING_ACCELERATION_STRUCTURE, enable them explicitly using WgpuSettings::features

Add previous_view_uniforms.inverse_view #

Rendering
  • Renamed prepass_bindings::previous_view_proj to prepass_bindings::previous_view_uniforms.view_proj.
  • Renamed PreviousViewProjectionUniformOffset to PreviousViewUniformOffset.
  • Renamed PreviousViewProjection to PreviousViewData.

Generate MeshUniforms on the GPU via compute shader where available. #

Rendering

Custom render phases now need multiple systems beyond just batch_and_prepare_render_phase. Code that was previously creating custom render phases should now add a BinnedRenderPhasePlugin or SortedRenderPhasePlugin as appropriate instead of directly adding batch_and_prepare_render_phase.

flipping texture coords methods has been added to the StandardMaterial #

Rendering

Instead of using Quad::flip field, call flipped(true, false) method on the StandardMaterial instance when adding the mesh.

Implement percentage-closer filtering (PCF) for point lights. #

Rendering

ShadowFilteringMethod::Castano13 and ShadowFilteringMethod::Jimenez14 have been renamed to ShadowFilteringMethod::Gaussian and ShadowFilteringMethod::Temporal respectively.

Divide the single VisibleEntities list into separate lists for 2D meshes, 3D meshes, lights, and UI elements, for performance. #

Rendering

check_visibility and VisibleEntities now store the four types of renderable entities–2D meshes, 3D meshes, lights, and UI elements–separately. If your custom rendering code examines VisibleEntities, it will now need to specify which type of entity it’s interested in using the WithMesh2d, WithMesh, WithLight, and WithNode types respectively. If your app introduces a new type of renderable entity, you’ll need to add an instance of the check_visibility system with the appropriate query filter to the main world schedule to accommodate your new component or components. For example:

app
    .add_systems(
        PostUpdate,
        check_visibility::<With<MyCustomRenderable>>
            .in_set(VisibilitySystems::CheckVisibility)
    );

Fix rendering of sprites, text, and meshlets after #12582. #

Rendering

Text now requires a SpriteSource marker component in order to appear. This component has been added to Text2dBundle.

Expose desired_maximum_frame_latency through window creation #

Rendering

The desired_maximum_frame_latency field must be added to instances of Window and ExtractedWindow where all fields are explicitly specified.

Fix CameraProjection panic and improve CameraProjectionPlugin #

Rendering

VisibilitySystems’s UpdateOrthographicFrusta, UpdatePerspectiveFrusta, and UpdateProjectionFrusta variants were removed, they were replaced with VisibilitySystems::UpdateFrusta

Implement filmic color grading. #

Rendering
  • ColorGrading::gamma and ColorGrading::pre_saturation are now set separately for the shadows, midtones, and highlights sections. You can migrate code with the ColorGrading::all_sections and ColorGrading::all_sections_mut functions, which access and/or update all sections at once.
  • ColorGrading::post_saturation and ColorGrading::exposure are now fields of ColorGrading::global.

Add BufferVec, an higher-performance alternative to StorageBuffer, and make GpuArrayBuffer use it. #

Rendering

BufferVec has been renamed to RawBufferVec and a new similar type has taken the BufferVec name.

Implement clearcoat per the Filament and the KHR_materials_clearcoat specifications. #

Rendering
  • The lighting functions in the pbr_lighting WGSL module now have clearcoat parameters, if STANDARD_MATERIAL_CLEARCOAT is defined.

  • The R reflection vector parameter has been removed from some lighting functions, as it was unused.

Clean up 2d render phases #

Rendering

If you were using Node2d::MainPass to order your own custom render node. You now need to order it relative to Node2d::StartMainPass or Node2d::EndMainPass.

#12502 Remove limit on RenderLayers. #

Rendering

RenderLayers::all() no longer exists. Entities expecting to be visible on all layers, e.g. lights, should compute the active layers that are in use.

Add emissive_exposure_weight to the StandardMaterial #

Rendering

Make render phases render world resources instead of components. #

Rendering

The BinnedRenderPhase and SortedRenderPhase render world components have been replaced with ViewBinnedRenderPhases and ViewSortedRenderPhases resources. Instead of querying for the components, look the camera entity up in the ViewBinnedRenderPhases/ViewSortedRenderPhases tables.

More idiomatic texture atlas builder #

Rendering
- let mut texture_atlas_builder = TextureAtlasBuilder::default().padding(UVec2::default()).format(..);
+ let mut texture_atlas_builder = TextureAtlasBuilder::default();
+ texture_atlas_builder.padding(UVec2::default()).format(..);

- let (texture_atlas_layout, texture) = texture_atlas_builder.finish().unwrap();
+ let (texture_atlas_layout, texture) = texture_atlas_builder.build().unwrap();
Rendering
  • Clustering-related types and functions (e.g. assign_lights_to_clusters) have moved under bevy_pbr::cluster, in preparation for the ability to cluster objects other than lights.

Normalise matrix naming #

Rendering
  • Frustum’s from_view_projection, from_view_projection_custom_far and from_view_projection_no_far were renamed to from_clip_from_world, from_clip_from_world_custom_far and from_clip_from_world_no_far.
  • ComputedCameraValues::projection_matrix was renamed to clip_from_view.
  • CameraProjection::get_projection_matrix was renamed to get_clip_from_view (this affects implementations on Projection, PerspectiveProjection and OrthographicProjection).
  • ViewRangefinder3d::from_view_matrix was renamed to from_world_from_view.
  • PreviousViewData’s members were renamed to view_from_world and clip_from_world.
  • ExtractedView’s projection, transform and view_projection were renamed to clip_from_view, world_from_view and clip_from_world.
  • ViewUniform’s view_proj, unjittered_view_proj, inverse_view_proj, view, inverse_view, projection and inverse_projection were renamed to clip_from_world, unjittered_clip_from_world, world_from_clip, world_from_view, view_from_world, clip_from_view and view_from_clip.
  • GpuDirectionalCascade::view_projection was renamed to clip_from_world.
  • MeshTransformstransform and previous_transform were renamed to world_from_local and previous_world_from_local.
  • MeshUniform’s transform, previous_transform, inverse_transpose_model_a and inverse_transpose_model_b were renamed to world_from_local, previous_world_from_local, local_from_world_transpose_a and local_from_world_transpose_b (the Mesh type in WGSL mirrors this, however transform and previous_transform were named model and previous_model).
  • Mesh2dTransforms::transform was renamed to world_from_local.
  • Mesh2dUniform’s transform, inverse_transpose_model_a and inverse_transpose_model_b were renamed to world_from_local, local_from_world_transpose_a and local_from_world_transpose_b (the Mesh2d type in WGSL mirrors this).
  • In WGSL, in bevy_pbr::mesh_functions, get_model_matrix and get_previous_model_matrix were renamed to get_world_from_local and get_previous_world_from_local.
  • In WGSL, bevy_sprite::mesh2d_functions::get_model_matrix was renamed to get_world_from_local.

Rename "point light" to "clusterable object" in cluster contexts. #

Rendering
  • In the PBR shaders, point_lights is now known as clusterable_objects, PointLight is now known as ClusterableObject, and cluster_light_index_lists is now known as clusterable_object_index_lists.

Deprecate SpriteSheetBundle and AtlasImageBundle #

Rendering
UI
  • SpriteSheetBundle has been deprecated. Use TextureAtlas alongside a SpriteBundle instead.
  • AtlasImageBundle has been deprecated. Use TextureAtlas alongside an ImageBundle instead.

Decouple BackgroundColor from UiImage #

Rendering
UI

The BackgroundColor component now renders a solid-color background behind UiImage instead of tinting its color. Use the color field of UiImage for tinting.

// 0.13
ButtonBundle {
    background_color: my_color.into(),
    ..default()
}
// 0.14
ButtonBundle {
    image: UiImage::default().with_color(my_color),
    ..default()
}
// 0.13
fn button_system(
    mut query: Query<(&Interaction, &mut BackgroundColor), (Changed<Interaction>, With<Button>)>,
) {
    for (interaction, mut color) in &mut query {
        match *interaction {
            Interaction::Pressed => {
                *color = my_color.into();
            }
            // ...
        }
    }
}
// 0.14
fn button_system(
    mut query: Query<(&Interaction, &mut UiImage), (Changed<Interaction>, With<Button>)>,
) {
    for (interaction, mut image) in &mut query {
        match *interaction {
            Interaction::Pressed => {
                image.color = my_color;
            }
            // ...
        }
    }
}

Some UI systems have been split or renamed.

  • bevy_ui::RenderUiSystem::ExtractNode has been split into ExtractBackgrounds, ExtractImages, ExtractBorders, and ExtractText.
  • bevy_ui::extract_uinodes has been split into bevy_ui::extract_uinode_background_colors and bevy_ui::extract_uinode_images.
  • bevy_ui::extract_text_uinodes has been renamed to extract_uinode_text.

Fix UI elements randomly not appearing after #13277. #

Rendering
UI

The bevy_ui::render::extract_default_ui_camera_view system is no longer parameterized over the specific type of camera and is hard-wired to either Camera2d or Camera3d components.

FIX12527: Changes to make serde optional for bevy_color #

Rendering
Utils

If user wants color data structures to be serializable, then application needs to be build with flag ‘serialize’

configure_surface needs to be on the main thread on iOS #

Rendering
Windowing

System need_new_surfaces has been renamed need_surface_configuration and now also configure the surfaces on window creation or resizing

Introduce a WindowWrapper to extend the lifetime of the window when using pipelined rendering #

Rendering
Windowing

Windowing backends now need to store their window in the new WindowWrapper.

Add an index argument to parallel iteration helpers in bevy_tasks #

Tasks

Functions passed as arguments to par_chunk_map, par_splat_map, par_chunk_map_mut, and par_splat_map_mut must now take an additional index argument.

Restore pre 0.13.1 Root Node Layout behavior #

UI

If you were affected by the 0.13.1 regression and added position_type: Absolute to all your root nodes you might be able to reclaim some LOC by removing them now that the 0.13 behavior is restored.

Rename Rect inset() method to inflate() #

UI

Replace Rect::inset(), IRect::inset() and URect::inset() calls with inflate().

Updates default Text font size to 24px #

UI
  • The default font size has been increased to 24px from 12px. Make sure you set the font to the appropriate values in places you were using Default text style.

Disentangle bevy_utils/bevy_core's reexported dependencies #

Utils

bevy_utils’ reexports of petgraph, uuid, nonmax, smallvec, and thiserror have been removed.

bevy_core’ reexports of bytemuck’s types has been removed.

Add them as dependencies in your own crate instead.

Fix fit_canvas_to_parent #

Windowing

Cancels the migration from https://github.com/bevyengine/bevy/pull/11057

Clean up WinitWindows::remove_window #

Windowing

WinitWindows::get_window_entity now returns None after a window is closed, instead of a dead entity.

Ensure clean exit #

Windowing

Ensure custom exit logic does not rely on the app exiting the same frame as a window is closed.

fix: upgrade to winit v0.30 #

Windowing

The custom UserEvent is now renamed as WakeUp, used to wake up the loop if anything happens outside the app (a new custom_user_event shows this behavior.

The internal UpdateState has been removed and replaced internally by the AppLifecycle. When changed, the AppLifecycle is sent as an event.

The UpdateMode now accepts only two values: Continuous and Reactive, but the latter exposes 3 new properties to enable reactive to device, user or window events. The previous UpdateMode::Reactive is now equivalent to UpdateMode::reactive(), while UpdateMode::ReactiveLowPower to UpdateMode::reactive_low_power().

The ApplicationLifecycle has been renamed as AppLifecycle, and now contains the possible values of the application state inside the event loop:

  • Idle: the loop has not started yet
  • Running (previously called Started): the loop is running
  • WillSuspend: the loop is going to be suspended
  • Suspended: the loop is suspended
  • WillResume: the loop is going to be resumed

Note: the Resumed state has been removed since the resumed app is just running.

Finally, now that winit enables this, it extends the WinitPlugin to support custom events.