Migration Guides

Migration Guide: 0.15 to 0.16

Accessibility #

Replace bevy_a11y::Focus with InputFocus #

Areas:Accessibility, Input, UI.
PRs:#16863

bevy_a11y::Focus has been replaced with bevy_input_focus::Focus.

Animation #

Incorporate all node weights in additive blending #

Areas:Animation.
PRs:#16279

I will write a migration guide later if this change is not included in 0.15.


Add ways to configure EasingFunction::Steps via new StepConfig #

Areas:Animation, Math.
PRs:#17752
  • EasingFunction::Steps now uses a StepConfig instead of a raw usize. You can replicate the previous behavior by replaceing EasingFunction::Steps(10) with EasingFunction::Steps(StepConfig::new(10)).

Fix EaseFunction::Exponential* to exactly hit (0, 0) and (1, 1) #

Areas:Animation, Math.
PRs:#16910

This release of bevy slightly tweaked the definitions of EaseFunction::ExponentialIn, EaseFunction::ExponentialOut, and EaseFunction::ExponentialInOut. The previous definitions had small discontinuities, while the new ones are slightly rescaled to be continuous. For the output values that changed, that change was less than 0.001, so visually you might not even notice the difference.

However, if you depended on them for determinism, you’ll need to define your own curves with the previous definitions.

App #

Headless by features #

Areas:App.
PRs:#16401

HeadlessPlugins has been removed, but the feature is still supported. bevy_window, a new default feature enables windows, etc. To replicate HeadlessPlugins, use DefaultPlugins and disable the bevy_window feature.

Assets #

Remove the meta field from LoadedAsset and ErasedLoadedAsset. #

Areas:Assets.
PRs:#15487
  • ErasedAssetLoader now takes a borrow to AssetMetaDyn instead of a Box.
  • LoadedAsset::new_with_dependencies no longer requires a meta argument.
  • LoadContext::finish no longer requires a meta argument.

Weak handle migration #

Areas:Assets.
PRs:#17695

Replace Handle::weak_from_u128 with weak_handle! and a random UUID.


Add AssetChanged query filter #

Areas:Assets, ECS.
PRs:#16810
  • The asset_events system is no longer public. Users should order their systems relative to the AssetEvents system set.

Audio #

Add ability to mute audio sinks #

Areas:Audio.
PRs:#16813
  • The AudioSinkPlayback trait now has 4 new methods to allow you to mute audio sinks: is_muted, mute, unmute and toggle_mute. You can use these methods on bevy_audio’s AudioSink and SpatialAudioSink components to manage the sink’s mute state.
  • AudioSinkPlayback’s set_volume method now takes a mutable reference instead of an immutable one. Update your code which calls set_volume on AudioSink and SpatialAudioSink components to take a mutable reference. E.g.:

Before:

fn increase_volume(sink: Single<&AudioSink>) {
    sink.set_volume(sink.volume() + 0.1);
}

After:

fn increase_volume(mut sink: Single<&mut AudioSink>) {
    let current_volume = sink.volume();
    sink.set_volume(current_volume + 0.1);
}
  • The PlaybackSettings component now has a muted field which you can use to spawn your audio in a muted state. PlaybackSettings also now has a helper method muted which you can use when building the component. E.g.:
commands.spawn((
    // ...
    AudioPlayer::new(asset_server.load("sounds/Windless Slopes.ogg")),
    PlaybackSettings::LOOP.with_spatial(true).muted(),
));

Rename AudioSinkPlayback::toggle to toggle_playback #

Areas:Audio.
PRs:#16837
  • AudioSinkPlayback’s toggle method has been renamed to toggle_playback. This was done to create consistency with the toggle_mute method added in https://github.com/bevyengine/bevy/pull/16813. Change instances of toggle to toggle_playback. E.g.:

Before:

fn pause(keyboard_input: Res<ButtonInput<KeyCode>>, sink: Single<&AudioSink>) {
    if keyboard_input.just_pressed(KeyCode::Space) {
        sink.toggle();
    }
}

After:

fn pause(keyboard_input: Res<ButtonInput<KeyCode>>, sink: Single<&AudioSink>) {
    if keyboard_input.just_pressed(KeyCode::Space) {
        sink.toggle_playback();
    }
}

Support decibels in bevy_audio::Volume #

Areas:Audio.
PRs:#17605

Audio volume can now be configured using decibel values, as well as using linear scale values. To enable this, some types and functions in bevy_audio have changed.

  • Volume is now an enum with Linear and Decibels variants.

Before:

let v = Volume(1.0);

After:

let volume = Volume::Linear(1.0);
let volume = Volume::Decibels(0.0); // or now you can deal with decibels if you prefer
  • Volume::ZERO has been renamed to the more semantically correct Volume::SILENT because Volume now supports decibels and “zero volume” in decibels actually means “normal volume”.
  • The AudioSinkPlayback trait’s volume-related methods now deal with Volume types rather than f32s. AudioSinkPlayback::volume() now returns a Volume rather than an f32. AudioSinkPlayback::set_volume now receives a Volume rather than an f32. This affects the AudioSink and SpatialAudioSink implementations of the trait. The previous f32 values are equivalent to the volume converted to linear scale so the Volume:: Linear variant should be used to migrate between f32s and Volume.
  • The GlobalVolume::new function now receives a Volume instead of an f32.

Cross-Cutting #

Add no_std support to bevy #

Areas:Cross-Cutting.
PRs:#17955
  • If you were previously relying on bevy with default features disabled, you may need to enable the std and async_executor features.
  • bevy_reflect has had its bevy feature removed. If you were relying on this feature, simply enable smallvec and smol_str instead.

Support for non-browser wasm #

Areas:Cross-Cutting.
PRs:#17499

When using Bevy crates which don’t automatically enable the web feature, please enable it when building for the browser.


Upgrade to Rust Edition 2024 #

Areas:Cross-Cutting.
PRs:#17967

The lifetimes of functions using return-position impl-trait (RPIT) are likely more conservative than they had been previously. If you encounter lifetime issues with such a function, please create an issue to investigate the addition of + use<...>.


Don't reëxport bevy_image from bevy_render #

Areas:Cross-Cutting, Rendering.
PRs:#16163

Use bevy_image instead of bevy_render::texture items.

Dev-Tools #

BRP strict field in query #

Areas:Dev-Tools.
PRs:#16725
  • BrpQueryParams now has strict boolean field. It serfs as a flag to fail when encountering an invalid component rather than skipping it. Defaults to false.

Rename track_change_detection flag to track_location #

Areas:Dev-Tools, ECS.
PRs:#17075

The track_change_detection feature flag has been renamed to track_location to better reflect its extended capabilities.


Draw the UI debug overlay using the UI renderer #

Areas:Dev-Tools, UI.
PRs:#16693

The ui_debug_overlay module has been removed from bevy_dev_tools. There is a new debug overlay implemented using the bevy_ui renderer. To use it, enable the bevy_ui_debug feature and set the enable field of the UiDebugOptions resource to true.

Diagnostics #

Allow users to customize history length in FrameTimeDiagnosticsPlugin #

Areas:Diagnostics.
PRs:#17259

FrameTimeDiagnosticsPlugin now contains two fields. Use FrameTimeDiagnosticsPlugin::default() to match Bevy’s previous behavior or, for example, FrameTimeDiagnosticsPlugin::new(60) to configure it.


Event source location tracking #

Areas:Diagnostics, ECS.
PRs:#16778
  • If you manually construct a SendEvent, use SendEvent::new()

ECS #

Improved Required Component Syntax #

Areas:ECS.
PRs:#18555

Custom-constructor requires should use the new expression-style syntax:

// before
#[derive(Component)]
#[require(A(returns_a))]
struct Foo;

// after
#[derive(Component)]
#[require(A = returns_a())]
struct Foo;

Inline-closure-constructor requires should use the inline value syntax where possible:

// before
#[derive(Component)]
#[require(A(|| A(10))]
struct Foo;

// after
#[derive(Component)]
#[require(A(10)]
struct Foo;

In cases where that is not possible, use the expression-style syntax:

// before
#[derive(Component)]
#[require(A(|| A(10))]
struct Foo;

// after
#[derive(Component)]
#[require(A = A(10)]
struct Foo;

Add EntityDoesNotExistError, replace cases of Entity as an error, do some easy Resultification #

Areas:ECS.
PRs:#17855
  • World::inspect_entity now returns Result<impl Iterator<Item = &ComponentInfo>, EntityDoesNotExistError> instead of impl Iterator<Item = &ComponentInfo>.
  • World::get_entity now returns EntityDoesNotExistError as an error instead of Entity. You can still access the entity’s ID through the error’s entity field.
  • UnsafeWorldCell::get_entity now returns Result<UnsafeEntityCell, EntityDoesNotExistError> instead of Option<UnsafeEntityCell>.

Cache systems by S instead of S::System #

Areas:ECS.
PRs:#16694

The CachedSystemId resource has been changed:

// Before:
let cached_id = CachedSystemId::<S::System>(id);
assert!(id == cached_id.0);

// After:
let cached_id = CachedSystemId::<S>::new(id);
assert!(id == SystemId::from_entity(cached_id.entity));

Change World::try_despawn and World::try_insert_batch to return Result #

Areas:ECS.
PRs:#17376
  • World::try_despawn now returns a Result rather than a bool.
  • World::try_insert_batch and World::try_insert_batch_if_new now return a Result where they previously returned nothing.

Convert to fallible system in IntoSystemConfigs #

Areas:ECS.
PRs:#17051
  • IntoSystemConfigs has been removed for BoxedSystem<(), ()>. Either use InfallibleSystemWrapper before boxing or make your system return bevy::ecs::prelude::Result.

Create new NonSendMarker #

Areas:ECS.
PRs:#18301

If NonSendMarker is being used from bevy_app::prelude::*, replace it with bevy_ecs::system::NonSendMarker or use it from bevy_ecs::prelude::*. In addition to that, NonSendMarker does not need to be wrapped like so:

fn my_system(_non_send_marker: Option<NonSend<NonSendMarker>>) {
    ...
}

Instead, it can be used without any wrappers:

fn my_system(_non_send_marker: NonSendMarker) {
    ...
}

Define system param validation on a per-system parameter basis #

Areas:ECS.
PRs:#18504

Various system and system parameter validation methods (SystemParam::validate_param, System::validate_param and System::validate_param_unsafe) now return and accept a ValidationOutcome enum, rather than a bool. The previous true values map to ValidationOutcome::Valid, while false maps to ValidationOutcome::Invalid.

However, if you wrote a custom schedule executor, you should now respect the new ValidationOutcome::Skipped parameter, skipping any systems whose validation was skipped. By contrast, ValidationOutcome::Invalid systems should also be skipped, but you should call the default_error_handler on them first, which by default will result in a panic.

If you are implementing a custom SystemParam, you should consider whether failing system param validation is an error or an expected state, and choose between Invalid and Skipped accordingly. In Bevy itself, Single and Populated now once again skip the system when their conditions are not met. This is the 0.15.0 behavior, but stands in contrast to the 0.15.1 behavior, where they would panic.


Deprecate insert_or_spawn function family #

Areas:ECS.
PRs:#18147

The following functions have been deprecated:

  • Commands::insert_or_spawn_batch
  • World::insert_or_spawn_batch
  • World::insert_or_spawn_batch_with_caller

These functions, when used incorrectly, can cause major performance problems and are generally viewed as anti-patterns and foot guns. These are planned to be removed altogether in 0.17.

Instead of these functions consider doing one of the following:

Option A) Instead of despawing entities and re-spawning them at a particular id, insert the new Disabled component without despawning the entity, and use try_insert_batch or insert_batch and remove Disabled instead of re-spawning it.

Option B) Instead of giving special meaning to an entity id, simply use spawn_batch and ensure entity references are valid when despawning.


Deprecated Query::many and many_mut #

Areas:ECS.
PRs:#18183

Query::many and Query::many_mut have been deprecated to reduce panics and API duplication. Use Query::get_many and Query::get_many_mut instead, and handle the Result.


Encapsulate cfg(feature = "track_location") in a type. #

Areas:ECS.
PRs:#17602

Methods like Ref::changed_by() that return a &'static Location<'static> will now be available even when the track_location feature is disabled, but they will return a new MaybeLocation type. MaybeLocation wraps a &'static Location<'static> when the feature is enabled, and is a ZST when the feature is disabled.

Existing code that needs a &Location can call into_option().unwrap() to recover it. Many trait impls are forwarded, so if you only need Display then no changes will be necessary.

If that code was conditionally compiled, you may instead want to use the methods on MaybeLocation to remove the need for conditional compilation.

Code that constructs a Ref, Mut, Res, or ResMut will now need to provide location information unconditionally. If you are creating them from existing Bevy types, you can obtain a MaybeLocation from methods like Table::get_changed_by_slice_for() or ComponentSparseSet::get_with_ticks. Otherwise, you will need to store a MaybeLocation next to your data and use methods like as_ref() or as_mut() to obtain wrapped references.


Fallible systems #

Areas:ECS.
PRs:#16589

This change should be pretty much non-breaking, except for users who have implemented their own custom executors. Those users should use ScheduleSystem in place of BoxedSystem<(), ()> and import the System trait where needed. They can choose to do whatever they wish with the result.


Fix unsoundness in QueryIter::sort_by #

Areas:ECS.
PRs:#17826

The sort family of methods on QueryIter unsoundly gave access L::Item<'w> with the full 'w lifetime. It has been shortened to L::Item<'w> so that items cannot escape the comparer. If you get lifetime errors using these methods, you will need to make the comparer generic in the new lifetime. Often this can be done by replacing named 'w with '_, or by replacing the use of a function item with a closure.

// Before: Now fails with "error: implementation of `FnMut` is not general enough"
query.iter().sort_by::<&C>(Ord::cmp);
// After: Wrap in a closure
query.iter().sort_by::<&C>(|l, r| Ord::cmp(l, r));

query.iter().sort_by::<&C>(comparer);
// Before: Uses specific `'w` lifetime from some outer scope
// now fails with "error: implementation of `FnMut` is not general enough"
fn comparer(left: &&'w C, right: &&'w C) -> Ordering { /* ... */ }
// After: Accepts any lifetime using inferred lifetime parameter
fn comparer(left: &&C, right: &&C) -> Ordering { /* ... */ }

Flush commands after every mutation in WorldEntityMut #

Areas:ECS.
PRs:#16219

Previously EntityWorldMut triggered command queue flushes in unpredictable places, which could interfere with hooks and observers. Now the command queue is flushed always immediately after any call in EntityWorldMut that spawns or despawns an entity, or adds, removes or replaces a component. This means hooks and observers will run their commands in the correct order.

As a side effect, there is a possibility that a hook or observer could despawn the entity that is being referred to by EntityWorldMut. This could already currently happen if an observer was added while keeping an EntityWorldMut reference and would cause unsound behaviour. If the entity has been despawned, calling any methods which require the entity location will panic. This matches the behaviour that Commands will panic if called on an already despawned entity. In the extremely rare case where taking a new EntityWorldMut reference or otherwise restructuring the code so that this case does not happen is not possible, there’s a new is_despawned method that can be used to check if the referred entity has been despawned.


Generic system config #

Areas:ECS.
PRs:#17962

SystemSetConfigs -> ScheduleConfigs SystemConfigs -> ScheduleConfigs IntoSystemSetConfigs -> IntoScheduleConfigs<InternedSystemSet, M> IntoSystemConfigs -> IntoScheduleConfigs<ScheduleSystem, M>


Implement SpawnableList for Vec<Bundle> #

Areas:ECS.
PRs:#18259
  • Optional: you may use the new API to spawn Vecs of Bundle instead of using the SpawnIter approach.

Improved Command Errors #

Areas:ECS.
PRs:#17215

Improved Spawn APIs and Bundle Effects #

Areas:ECS.
PRs:#17521

Existing spawn patterns will continue to work as expected.

Manual Bundle implementations now require a BundleEffect associated type. Existing bundles would have no bundle effect, so use (). Additionally Bundle::from_components has been moved to the new BundleFromComponents trait.

// Before
unsafe impl Bundle for X {
    unsafe fn from_components<T, F>(ctx: &mut T, func: &mut F) -> Self {
    }
    /* remaining bundle impl here */
}

// After
unsafe impl Bundle for X {
    type Effect = ();
    /* remaining bundle impl here */
}

unsafe impl BundleFromComponents for X {
    unsafe fn from_components<T, F>(ctx: &mut T, func: &mut F) -> Self {
    }
}

Introduce methods on QueryState to obtain a Query #

Areas:ECS.
PRs:#15858

Query::to_readonly has been renamed to Query::as_readonly.


Isolate component registration #

Areas:ECS.
PRs:#17671
  • Remove storages from functions where it is no longer needed.
  • Note that SparseSets are no longer present for all registered sparse set components, only those that have been spawned.

Make Query::single (and friends) return a Result #

Areas:ECS.
PRs:#18082

Query::single, Query::single_mut and their QueryState equivalents now return a Result. Generally, you’ll want to:

  • Use Bevy 0.16’s system error handling to return a Result using the ? operator.
  • Use a let else Ok(data) block to early return if it’s an expected failure.
  • Use unwrap() or Ok destructuring inside of tests.

The old Query::get_single (etc) methods which did this have been deprecated.


Make system param validation rely on the unified ECS error handling via the GLOBAL_ERROR_HANDLER #

Areas:ECS.
PRs:#18454

ParamWarnPolicy and the WithParamWarnPolicy have been removed completely. Failures during system param validation are now handled via the GLOBAL_ERROR_HANDLER: please see the bevy_ecs::error module docs for more information.


Move Item and fetch to QueryData from WorldQuery #

Areas:ECS.
PRs:#17679

The WorldQuery::Item type and WorldQuery::fetch method have been moved to QueryData, as they were not useful for QueryFilter types.


Move Resource trait to its own file #

Areas:ECS.
PRs:#17469

bevy_ecs::system::Resource has been moved to bevy_ecs::resource::Resource.


Optimize Entities::entity_does_not_exist_error_details_message, remove UnsafeWorldCell from error #

Areas:ECS.
PRs:#17115

The errors EntityFetchError::NoSuchEntity and QueryEntityError::NoSuchEntity now contain an EntityDoesNotExistDetails struct instead of an UnsafeWorldCell. If you were just printing these, they should work identically.


Parent -> ChildOf #

Areas:ECS.
PRs:#17427
  • The Parent component has been renamed to ChildOf.

Queued component registration #

Areas:ECS.
PRs:#18173

Component registration can now be queued with only &World. To facilitate this, a few APIs needed to be moved around.

The following functions have moved from Components to ComponentsRegistrator:

  • register_component
  • register_component_with_descriptor
  • register_resource_with_descriptor
  • register_non_send
  • register_resource
  • register_required_components_manual

Accordingly, functions in Bundle and Component now take ComponentsRegistrator instead of Components. You can obtain ComponentsRegistrator from the new World::components_registrator. You can obtain ComponentsQueuedRegistrator from the new World::components_queue, and use it to stage component registration if desired.


Areas:ECS.
PRs:#17029

If you were queuing the structs of hierarchy-related commands or SendEvent directly, you will need to change them to the methods implemented on EntityCommands (or Commands for SendEvent): StructMethodcommands.queue(AddChild { child, parent });``commands.entity(parent).add_child(child); OR commands.entity(child).set_parent(parent);``commands.queue(AddChildren { children, parent });``commands.entity(parent).add_children(children);``commands.queue(InsertChildren { children, parent });``commands.entity(parent).insert_children(children);``commands.queue(RemoveChildren { children, parent });``commands.entity(parent).remove_children(children);``commands.queue(ReplaceChildren { children, parent });``commands.entity(parent).replace_children(children);``commands.queue(ClearChildren { parent });``commands.entity(parent).clear_children();``commands.queue(RemoveParent { child });``commands.entity(child).remove_parent()``commands.queue(DespawnRecursive { entity, warn: true });``commands.entity(entity).despawn_recursive();``commands.queue(DespawnRecursive { entity, warn: false });``commands.entity(entity).try_despawn_recursive();``commands.queue(DespawnChildrenRecursive { entity, warn: true });``commands.entity(entity).despawn_descendants();``commands.queue(DespawnChildrenRecursive { entity, warn: false});``commands.entity(entity).try_despawn_descendants();``commands.queue(SendEvent { event });``commands.send_event(event);


Refactored ComponentHook Parameters into HookContext #

Areas:ECS.
PRs:#17503

Update the function signatures for your component hooks to only take 2 arguments, world and context. Note that because HookContext is plain data with all members public, you can use de-structuring to simplify migration.

// Before
fn my_hook(
    mut world: DeferredWorld,
    entity: Entity,
    component_id: ComponentId,
) { ... }

// After
fn my_hook(
    mut world: DeferredWorld,
    HookContext { entity, component_id, caller }: HookContext,
) { ... }

Likewise, if you were discarding certain parameters, you can use .. in the de-structuring:

// Before
fn my_hook(
    mut world: DeferredWorld,
    entity: Entity,
    _: ComponentId,
) { ... }

// After
fn my_hook(
    mut world: DeferredWorld,
    HookContext { entity, .. }: HookContext,
) { ... }

Relationships (non-fragmenting, one-to-many) #

Areas:ECS.
PRs:#17398
  • Replace ChildBuilder with ChildSpawnerCommands.
  • Replace calls to .set_parent(parent_id) with .insert(Parent(parent_id)).
  • Replace calls to .replace_children() with .remove::<Children>() followed by .add_children(). Note that you’ll need to manually despawn any children that are not carried over.
  • Replace calls to .despawn_recursive() with .despawn().
  • Replace calls to .despawn_descendants() with .despawn_related::<Children>().
  • If you have any calls to .despawn() which depend on the children being preserved, you’ll need to remove the Children component first.

Remove ChildOf::get and Deref impl #

Areas:ECS.
PRs:#18080
// Before
**child_of
// After
child_of.parent

// Before
child_of.get()
// After
child_of.parent

// Before
entity.get::<ChildOf>().map(ChildOf::get)
// After
entity.get::<ChildOf>().map(|c| c.parent)

Remove Event: Component trait bound using a wrapper type which impls Component #

Areas:ECS.
PRs:#17380

The Event trait no longer requires the Component trait. If you were relying on this behavior, change your trait bounds from Event to Event + Component. If you also want your Event type to implement Component, add a derive.


Remove petgraph from bevy_ecs #

Areas:ECS.
PRs:#15519

The Dag::graph method no longer returns a petgraph DiGraph and instead returns the new DiGraph type within bevy_ecs. Edge and node iteration methods are provided so conversion to the petgraph type should be trivial if required.


Remove deprecated ECS items #

Areas:ECS.
PRs:#16853
  • The following deprecated items were removed: Events::get_reader, Events::get_reader_current, ManualEventReader, Condition::and_then, Condition::or_else, World::,many_entities, World::many_entities_mut, World::get_many_entities, World::get_many_entities_dynamic, World::get_many_entities_mut, World::get_many_entities_dynamic_mut, World::get_many_entities_from_set_mut

Remove deprecated component_reads_and_writes #

Areas:ECS.
PRs:#16348

The following methods (some removed in previous PRs) are now replaced by Access::try_iter_component_access:

  • Access::component_reads_and_writes
  • Access::component_reads
  • Access::component_writes

As try_iter_component_access returns a Result, you’ll now need to handle the failing case (e.g., unwrap()). There is currently a single failure mode, UnboundedAccess, which occurs when the Access is for all Components except certain exclusions. Since this list is infinite, there is no meaningful way for Access to provide an iterator. Instead, get a list of components (e.g., from the Components structure) and iterate over that instead, filtering using Access::has_component_read, Access::has_component_write, etc.

Additionally, you’ll need to filter_map the accesses based on which method you’re attempting to replace:

  • Access::component_reads_and_writes -> Exclusive(_) | Shared(_)
  • Access::component_reads -> Shared(_)
  • Access::component_writes -> Exclusive(_)

To ease migration, please consider the below extension trait which you can include in your project:

pub trait AccessCompatibilityExt {
    /// Returns the indices of the components this has access to.
    fn component_reads_and_writes(&self) -> impl Iterator<Item = T> + '_;

    /// Returns the indices of the components this has non-exclusive access to.
    fn component_reads(&self) -> impl Iterator<Item = T> + '_;

    /// Returns the indices of the components this has exclusive access to.
    fn component_writes(&self) -> impl Iterator<Item = T> + '_;
}

impl<T: SparseSetIndex> AccessCompatibilityExt for Access<T> {
    fn component_reads_and_writes(&self) -> impl Iterator<Item = T> + '_ {
        self
            .try_iter_component_access()
            .expect("Access is unbounded. Please refactor the usage of this method to directly use try_iter_component_access")
            .filter_map(|component_access| {
                let index = component_access.index().sparse_set_index();

                match component_access {
                    ComponentAccessKind::Archetypal(_) => None,
                    ComponentAccessKind::Shared(_) => Some(index),
                    ComponentAccessKind::Exclusive(_) => Some(index),
                }
            })
    }

    fn component_reads(&self) -> impl Iterator<Item = T> + '_ {
        self
            .try_iter_component_access()
            .expect("Access is unbounded. Please refactor the usage of this method to directly use try_iter_component_access")
            .filter_map(|component_access| {
                let index = component_access.index().sparse_set_index();

                match component_access {
                    ComponentAccessKind::Archetypal(_) => None,
                    ComponentAccessKind::Shared(_) => Some(index),
                    ComponentAccessKind::Exclusive(_) => None,
                }
            })
    }

    fn component_writes(&self) -> impl Iterator<Item = T> + '_ {
        self
            .try_iter_component_access()
            .expect("Access is unbounded. Please refactor the usage of this method to directly use try_iter_component_access")
            .filter_map(|component_access| {
                let index = component_access.index().sparse_set_index();

                match component_access {
                    ComponentAccessKind::Archetypal(_) => None,
                    ComponentAccessKind::Shared(_) => None,
                    ComponentAccessKind::Exclusive(_) => Some(index),
                }
            })
    }
}

Please take note of the use of expect(...) in these methods. You should consider using these as a starting point for a more appropriate migration based on your specific needs.


Remove flush_and_reserve_invalid_assuming_no_entities #

Areas:ECS.
PRs:#16460
  • exchange Entities::flush_and_reserve_invalid_assuming_no_entities for reserve and flush_as_invalid and notify us if that’s insufficient

Remove lifetime from QueryEntityError #

Areas:ECS.
PRs:#18157
  • QueryEntityError::QueryDoesNotMatch.1 is of type ArchetypeId instead of UnsafeWorldCell. It is up to the caller to obtain an UnsafeWorldCell now.
  • QueryEntityError no longer has a lifetime parameter, remove it from type signatures where required.

Remove unsound Clone impl for EntityMutExcept #

Areas:ECS.
PRs:#17032
  • EntityMutExcept can no-longer be cloned, as this violates Rust’s memory safety rules.

Remove unused generic in DeferredWorld::trigger #

Areas:ECS.
PRs:#16911

DeferredWorld::trigger no longer takes a generic argument. They type is inferred via impl Event instead of T: Event. If rust can not infer the type for you, consider specifying the type on the variable passed into the function rather than on the function itself.


Renamed EventWriter::send methods to write. #

Areas:ECS.
PRs:#17977
  • EventWriter::send has been renamed to EventWriter::write.
  • EventWriter::send_batch has been renamed to EventWriter::write_batch.
  • EventWriter::send_default has been renamed to EventWriter::write_default.

Replace VisitEntities with MapEntities #

Areas:ECS.
PRs:#18432

If you were previously implementing VisitEntities or VisitEntitiesMut (likely via a derive), instead use MapEntities. Those were almost certainly used in the context of Bevy Scenes or reflection via ReflectMapEntities. If you have a case that uses VisitEntities or VisitEntitiesMut directly, where MapEntities is not a viable replacement, please let us know!

// before
#[derive(VisitEntities, VisitEntitiesMut)]
struct Inventory {
  items: Vec<Entity>,
  #[visit_entities(ignore)]
  label: String,
}

// after
#[derive(MapEntities)]
struct Inventory {
  #[entities]
  items: Vec<Entity>,
  label: String,
}

Run observers before hooks for on_replace and on_remove #

Areas:ECS.
PRs:#16499

The order of hooks and observers for on_replace and on_remove has been swapped. Observers are now run before hooks. This is a more natural ordering where the removal ordering is inverted compared to the insertion ordering.


Shorten the 'world lifetime returned from QueryLens::query(). #

Areas:ECS.
PRs:#17694

Users of QueryLens::query() who were calling get_inner() or iter_inner() will need to replace the call with QueryLens::query_inner().


Split Component::register_component_hooks into individual methods #

Areas:ECS.
PRs:#17685

Component::register_component_hooks is now deprecated and will be removed in a future release. When implementing Component manually, also implement the respective hook methods on Component.

// Before
impl Component for Foo {
    // snip
    fn register_component_hooks(hooks: &mut ComponentHooks) {
            hooks.on_add(foo_on_add);
    }
}

// After
impl Component for Foo {
    // snip
    fn on_add() -> Option<ComponentHook> {
            Some(foo_on_add)
    }
}

Support non-Vec data structures in relations #

Areas:ECS.
PRs:#17447

EntityHashSet and EntityHashMap are no longer re-exported in bevy_ecs::entity directly. If you were not using bevy_ecs / bevy’s prelude, you can access them through their now-public modules, hash_set and hash_map instead.


Support using FilteredResources with ReflectResource. #

Areas:ECS.
PRs:#15624

If you are manually creating a ReflectComponentFns struct, the reflect function now takes FilteredResources instead &World, and there is a new reflect_mut function that takes FilteredResourcesMut.


Turn apply_deferred into a ZST System #

Areas:ECS.
PRs:#16642
  • If you were previously calling the special apply_deferred system via apply_deferred(world), don’t.

Use register_dynamic for merging #

Areas:ECS.
PRs:#18028

RequiredComponents::register_dynamic has been changed to RequiredComponents::register_dynamic_with.

Old:

required_components.register_dynamic(
      component_id,
      component_constructor.clone(),
      requirement_inheritance_depth,
);

New:

required_components.register_dynamic_with(
      component_id,
      requirement_inheritance_depth,
      || component_constructor.clone(),
);

This can prevent unnecessary cloning.


add Entity default to the entity set wrappers #

Areas:ECS.
PRs:#18319

Switch type parameter order for the relevant wrapper types/aliases.


impl EntityBorrow for more types #

Areas:ECS.
PRs:#16917

NormalizedWindowRef::entity has been replaced with an EntityBorrow::entity impl.


implement EntitySet and iter_many_unique methods #

Areas:ECS.
PRs:#16547

Any custom type used as a Borrow<Entity> entity list item for an iter_many method now has to implement EntityBorrow instead. Any type that implements Borrow<Entity> can trivially implement EntityBorrow.


make EntityHashMap and EntityHashSet proper types #

Areas:ECS.
PRs:#16912

Users of with_hasher and with_capacity_and_hasher on EntityHashMap/Set must now use new and with_capacity respectively. If the non-newtyped versions are required, they can be obtained via Deref, DerefMut or into_inner calls.


make various entity wrapper type modules public #

Areas:ECS.
PRs:#18248

Any mention or import of types in the affected modules have to add the respective module name to the import path. F.e.: bevy::ecs::entity::EntityIndexSet -> bevy::ecs::entity::index_set::EntityIndexSet


one shot system cleanup #

Areas:ECS.
PRs:#16516
  • Change all occurrences of World::run_system_with_input to World::run_system_with.
  • swap the order of input parameters for World::run_system_once_with such that the system comes before the input.

rename enqueue_command to queue_command for consistency #

Areas:ECS.
PRs:#16753

All instances of the enqueue_command method have been renamed to queue_command.


Make ComponentTicks field public #

Areas:ECS, Networking.
PRs:#16269
  • Instead of using ComponentTicks::last_changed_tick and ComponentTicks::added_tick methods, access fields directly.

Add Immutable Component Support #

Areas:ECS, Reflection.
PRs:#16372
  • When implementing Component manually, you must now provide a type for Mutability. The type Mutable provides equivalent behaviour to earlier versions of Component:
impl Component for Foo {
    type Mutability = Mutable;
    // ...
}
  • When working with generic components, you may need to specify that your generic parameter implements Component<Mutability = Mutable> rather than Component if you require mutable access to said component.
  • The entity entry API has had to have some changes made to minimise friction when working with immutable components. Methods which previously returned a Mut<T> will now typically return an OccupiedEntry<T> instead, requiring you to add an into_mut() to get the Mut<T> item again.

Faster entity cloning #

Areas:ECS, Reflection.
PRs:#16717
  • &EntityCloner in component clone handlers is changed to &mut ComponentCloneCtx to better separate data.
  • Changed EntityCloneHandler from enum to struct and added convenience functions to add default clone and reflect handler more easily.

FilteredResource returns a Result instead of a simple Option #

Areas:ECS, Reflection.
PRs:#18265

Users will need to handle the different return type on FilteredResource::get, FilteredResource::get_id, FilteredResource::get_mut as it is now a Result not an Option.


ReflectBundle::remove improvement #

Areas:ECS, Reflection.
PRs:#16139

If you don’t need the returned value from remove, discard it.

Input #

Gamepad improvements #

Areas:Input.
PRs:#16222
  • Gamepad fields are now public.
  • Instead of using Gamepad delegates like Gamepad::just_pressed, call these methods directly on the fields.

Scale input to account for deadzones #

Areas:Input.
PRs:#17015

GamepadButtonChangedEvent.value is now linearly rescaled to be from 0.0..=1.0 (instead of low..=high) and GamepadAxisChangedEvent.value is now linearly rescaled to be from -1.0..=0.0/0.0..=1.0 (accounting for the deadzone).


Use Name component for gamepad #

Areas:Input.
PRs:#16233
  • GamepadInfo no longer exists:
    • Name now accessible via Name component.
    • Other information available on Gamepad component directly.
    • GamepadConnection::Connected now stores all info fields directly.

Expose text field from winit in KeyboardInput #

Areas:Input, Text, UI.
PRs:#16864

The KeyboardInput event now has a new text field.

Math #

Fix atan2 docs #

Areas:Math.
PRs:#16673

I’m not sure if this counts as a breaking change, since the implementation clearly meant to use f32::atan2 directly, so it was really just the parameter names that were wrong.


Fix rounding in steps easing function #

Areas:Math.
PRs:#17743

EaseFunction::Steps now behaves like css’s default, “jump-end.” If you were relying on the old behavior, we plan on providing it. See https://github.com/bevyengine/bevy/issues/17744.


Improve cubic segment bezier functionality #

Areas:Math.
PRs:#17645

Replace CubicCurve::new_bezier with CubicCurve::new_bezier_easing.


Refactor non-core Curve methods into extension traits #

Areas:Math.
PRs:#16930

Curve has been refactored so that much of its functionality is now in extension traits. Adaptors such as map, reparametrize, reverse, and so on now require importing CurveExt, while the resampling methods resample_* require importing CurveResampleExt. Both of these new traits are exported through bevy::math::curve and through bevy::math::prelude.


Rename Rot2::angle_between to Rot2::angle_to #

Areas:Math.
PRs:#16327

Rot2::angle_between has been deprecated, use Rot2::angle_to instead, the semantics of Rot2::angle_between will change in the future.


Reworked Segment types into their cartesian forms #

Areas:Math.
PRs:#17404

The segment type constructors changed so if someone previously created a Segment2d with a direction and length they would now need to use the from_direction constructor


Use IntoIterator instead of Into<Vec<..>> in cubic splines interfaces #

Areas:Math.
PRs:#16402

The cubic splines API now uses IntoIterator in places where it used Into<Vec<..>>. For most users, this will have little to no effect (it is largely more permissive). However, in case you were using some unusual input type that implements Into<Vec<..>> without implementing IntoIterator, you can migrate by converting the input to a Vec<..> before passing it into the interface.


[math] Add SmoothStep and SmootherStep easing functions #

Areas:Math.
PRs:#16957

This version of bevy marks EaseFunction as #[non_exhaustive] to that future changes to add more easing functions will be non-breaking. If you were exhaustively matching that enum – which you probably weren’t – you’ll need to add a catch-all (_ =>) arm to cover unknown easing functions.


Make bevy_reflect feature of bevy_math non-default #

Areas:Math, Reflection.
PRs:#16938

bevy_reflect has been made a non-default feature of bevy_math. (It is still enabled when bevy_math is used through bevy.) You may need to enable this feature if you are using bevy_math on its own and desire for the types it exports to implement Reflect and other reflection traits.

Picking #

Add flags to SpritePlugin and UiPlugin to allow disabling their picking backend (without needing to disable features). #

Areas:Picking.
PRs:#16473
  • UiPlugin now contains an extra add_picking field if bevy_ui_picking_backend is enabled.
  • SpritePlugin is no longer a unit struct, and has one field if bevy_sprite_picking_backend is enabled (otherwise no fields).

Add optional transparency passthrough for sprite backend with bevy_picking #

Areas:Picking.
PRs:#16388

Sprite picking now ignores transparent regions (with an alpha value less than or equal to 0.1). To configure this, modify the SpriteBackendSettings resource.


Allow users to easily use bevy_sprite and bevy_ui without picking #

Areas:Picking.
PRs:#17175

bevy_sprite_picking_backend is no longer included by default when using the bevy_sprite feature. If you are using Bevy without default features and relied on sprite picking, add this feature to your Cargo.toml.

bevy_ui_picking_backend is no longer included by default when using the bevy_ui feature. If you are using Bevy without default features and relied on sprite picking, add this feature to your Cargo.toml.


Fix bevy_picking plugin suffixes #

Areas:Picking.
PRs:#16082
  • MeshPickingBackend is now named MeshPickingPlugin.
  • MeshPickingBackendSettings is now named MeshPickingSettings.
  • SpritePickingBackend is now named SpritePickingPlugin.
  • UiPickingBackendPlugin is now named UiPickingPlugin.
  • DefaultPickingPlugins is now a a PluginGroup instead of a Plugin.

Flattened PointerAction::Pressed into Press and Release. #

Areas:Picking.
PRs:#17424
  • PointerAction::Pressed has been separated into two variants, PointerAction::Press and PointerAction::Release.
  • PointerAction::Moved has been renamed to PointerAction::Move.
  • PointerAction::Canceled has been renamed to PointerAction::Cancel.

If there is no movement, DragStart is not triggered. #

Areas:Picking.
PRs:#17233

Fix the missing part of Drag https://github.com/bevyengine/bevy/pull/16950


Make RayMap map public #

Areas:Picking.
PRs:#18544

The bevy_picking::backend::ray::RayMap::map method is removed as redundant, In systems using Res<RayMap> replace ray_map.map() with &ray_map.map


Make sprite picking opt-in #

Areas:Picking.
PRs:#17225

The sprite picking backend is now strictly opt-in using the SpritePickingCamera and Pickable components. You should add the Pickable component any entities that you want sprite picking to be enabled for, and mark their respective cameras with SpritePickingCamera.


Make sprite picking opt-in #

Areas:Picking.
PRs:#17842
  • Sprite picking are now opt-in, make sure you insert Pickable component when using sprite picking.
-commands.spawn(Sprite { .. } );
+commands.spawn((Sprite { .. }, Pickable::default());

Rename "focus" in bevy_picking to "hover" #

Areas:Picking.
PRs:#16872

Various terms related to “focus” in bevy_picking have been renamed to refer to “hover” to avoid confusion with bevy_input_focus. In particular:

  • The update_focus system has been renamed to generate_hovermap
  • PickSet::Focus and PostFocus have been renamed to Hover and PostHover
  • The bevy_picking::focus module has been renamed to bevy_picking::hover
  • The is_focus_enabled field on PickingPlugin has been renamed to is_hover_enabled
  • The focus_should_run run condition has been renamed to hover_should_run

Rename Pointer<Down/Up> -> Pointer<Pressed/Released> in bevy_picking. #

Areas:Picking.
PRs:#16331

bevy_picking/src/pointer.rs:

enum PressDirection:

PressDirection::Down changes to PressDirection::Pressed.

PressDirection::Up changes to PressDirection::Released.

These changes are also relevant when working with enum PointerAction

bevy_picking/src/events.rs:

Clicking and pressing Events in events.rs categories change from [Down], [Up], [Click] to [Pressed], [Released], [Click].

  • struct Down changes to struct Pressed - fires when a pointer button is pressed over the ‘target’ entity.
  • struct Up changes to struct Released - fires when a pointer button is released over the ‘target’ entity.
  • struct Click now fires when a pointer sends a Pressed event followed by a Released event on the same ‘target’.
  • struct DragStart now fires when the ‘target’ entity receives a pointer Pressed event followed by a pointer Move event.
  • struct DragEnd now fires when the ‘target’ entity is being dragged and receives a pointer Released event.
  • PickingEventWriters<'w>::down_events: EventWriter<'w, Pointer<Down>> changes to PickingEventWriters<'w>::pressed_events: EventWriter<'w, Pointer<Pressed>>.
  • PickingEventWriters<'w>::up_events changes to PickingEventWriters<'w>::released_events.

Rename PickingBehavior to Pickable #

Areas:Picking.
PRs:#17266

Change all instances of PickingBehavior to Pickable.


Rename RayCastSettings to MeshRayCastSettings #

Areas:Picking.
PRs:#16703

RayCastSettings has been renamed to MeshRayCastSettings to avoid naming conflicts with other ray casting backends and types.


Unify picking backends #

Areas:Picking.
PRs:#17348

UiPickingPlugin and SpritePickingPlugin are no longer included in DefaultPlugins. They must be explicitly added.

RayCastPickable has been replaced in favor of the MeshPickingCamera and Pickable components. You should add them to cameras and entities, respectively, if you have MeshPickingSettings::require_markers set to true.

Reflection #

Include ReflectFromReflect in all dynamic data types. #

Areas:Reflection.
PRs:#17453

The hasher in reflected HashMaps and HashSets now have to implement Default. This is the case for the ones provided by Bevy already, and is generally a sensible thing to do.


Make bevy_remote feature enable serialize feature #

Areas:Reflection.
PRs:#17260

The bevy_remote feature of bevy now enables the serialize feature automatically. If you wish to use bevy_remote without enabling the serialize feature for Bevy subcrates, you must import bevy_remote on its own.


Rename ArgList::push methods to with and add new push methods which take &mut self #

Areas:Reflection.
PRs:#16567

Uses of the ArgList::push methods should be replaced with the with counterpart.

oldnewpush_argwith_argpush_refwith_refpush_mutwith_mutpush_ownedwith_ownedpush_boxedwith_boxed

bevy_reflect: Deprecate PartialReflect::clone_value #

Areas:Reflection.
PRs:#18284

PartialReflect::clone_value is being deprecated. Instead, use PartialReflect::to_dynamic if wanting to create a new dynamic instance of the reflected value. Alternatively, use PartialReflect::reflect_clone to attempt to create a true clone of the underlying value.

Similarly, the following methods have been deprecated and should be replaced with these alternatives:

  • Array::clone_dynamicArray::to_dynamic_array
  • Enum::clone_dynamicEnum::to_dynamic_enum
  • List::clone_dynamicList::to_dynamic_list
  • Map::clone_dynamicMap::to_dynamic_map
  • Set::clone_dynamicSet::to_dynamic_set
  • Struct::clone_dynamicStruct::to_dynamic_struct
  • Tuple::clone_dynamicTuple::to_dynamic_tuple
  • TupleStruct::clone_dynamicTupleStruct::to_dynamic_tuple_struct

bevy_reflect: Remove PartialReflect::serializable #

Areas:Reflection.
PRs:#16576

PartialReflect::serializable has been removed. If you were using this to pass on serialization information, use ReflectSerialize instead or create custom type data to generate the Serializable.


Remove unnecessary PartialReflect bound on DeserializeWithRegistry #

Areas:Reflection, Scenes.
PRs:#17560

DeserializeWithRegistry types are no longer guaranteed to be PartialReflect as well. If you were relying on this type bound, you should add it to your own bounds manually.

- impl<T: DeserializeWithRegistry> Foo for T { .. }
+ impl<T: DeserializeWithRegistry + PartialReflect> Foo for T { .. }

Rendering #

Add uv_transform to ColorMaterial #

Areas:Rendering.
PRs:#17879

Add uv_transform field to constructors of ColorMaterial


Add a bindless mode to AsBindGroup. #

Areas:Rendering.
PRs:#16368
  • RenderAssets::prepare_asset now takes an AssetId parameter.
  • Bin keys now have Bevy-specific material bind group indices instead of wgpu material bind group IDs, as part of the bindless change. Use the new MaterialBindGroupAllocator to map from bind group index to bind group ID.

Add bevy_anti_aliasing #

Areas:Rendering.
PRs:#18323

When using anti aliasing features, you now need to import them from bevy::anti_aliasing instead of bevy::core_pipeline


Adding alpha_threshold to OrderIndependentTransparencySettings for user-level optimization #

Areas:Rendering.
PRs:#16090

If you previously explicitly initialized OrderIndependentTransparencySettings with your own layer_count, you will now have to add either a ..default() statement or an explicit alpha_threshold value:

fn setup(mut commands: Commands) {
    commands.spawn((
        Camera3d::default(),
        OrderIndependentTransparencySettings {
            layer_count: 16,
            ..default()
        },
    ));
}

Allowed creating uninitialized images (for use as storage textures) #

Areas:Rendering.
PRs:#17760

Code that directly access Image data will now need to use unwrap or handle the case where no data is provided. Behaviour of new_fill slightly changed, but not in a way that is likely to affect anything. It no longer panics and will fill the whole texture instead of leaving black pixels if the data provided is not a nice factor of the size of the image.


Bind only the written parts of storage buffers. #

Areas:Rendering.
PRs:#16405
  • Fixed a bug with StorageBuffer and DynamicStorageBuffer binding data from the previous frame(s) due to caching GPU buffers between frames.

Change GpuImage::size from UVec2 to Extent3d #

Areas:Rendering.
PRs:#16815
  • GpuImage::size is now an Extent3d. To easily get 2D size, use size_2d().

Cold Specialization #

Areas:Rendering.
PRs:#17567

TODO

  • AssetEvents has been moved into the PostUpdate schedule.

Expose Pipeline Compilation Zero Initialize Workgroup Memory Option #

Areas:Rendering.
PRs:#16301
  • add zero_initialize_workgroup_memory: false, to ComputePipelineDescriptor or RenderPipelineDescriptor structs to preserve 0.14 functionality, add zero_initialize_workgroup_memory: true, to restore bevy 0.13 functionality.

Fix sprite performance regression since retained render world #

Areas:Rendering.
PRs:#17078
  • ExtractedSprites is now using MainEntityHashMap for storage, which is keyed on MainEntity.
  • The render world entity corresponding to an ExtractedSprite is now stored in the render_entity member of it.

Fix the texture_binding_array, specialized_mesh_pipeline, and custom_shader_instancing examples after the bindless change. #

Areas:Rendering.
PRs:#16641
  • Bevy will now unconditionally call AsBindGroup::unprepared_bind_group for your materials, so you must no longer panic in that function. Instead, return the new AsBindGroupError::CreateBindGroupDirectly error, and Bevy will fall back to calling AsBindGroup::as_bind_group as before.

Implement bindless lightmaps. #

Areas:Rendering.
PRs:#16653
  • The Opaque3dBinKey::lightmap_image field is now Opaque3dBinKey::lightmap_slab, which is a lightweight identifier for an entire binding array of lightmaps.

Implement experimental GPU two-phase occlusion culling for the standard 3D mesh pipeline. #

Areas:Rendering.
PRs:#17413
  • When enqueuing a custom mesh pipeline, work item buffers are now created with bevy::render::batching::gpu_preprocessing::get_or_create_work_item_buffer, not PreprocessWorkItemBuffers::new. See the specialized_mesh_pipeline example.

Introduce support for mixed lighting by allowing lights to opt out of contributing diffuse light to lightmapped objects. #

Areas:Rendering.
PRs:#16761
  • The AmbientLight resource, the IrradianceVolume component, and the EnvironmentMapLight component now have affects_lightmapped_meshes fields. If you don’t need to use that field (for example, if you aren’t using lightmaps), you can safely set the field to true.
  • DirectionalLight, PointLight, and SpotLight now have affects_lightmapped_mesh_diffuse fields. If you don’t need to use that field (for example, if you aren’t using lightmaps), you can safely set the field to true.

Introduce two-level bins for multidrawable meshes. #

Areas:Rendering.
PRs:#16898
  • The batch set key is now separate from the bin key in BinnedPhaseItem. The batch set key is used to collect multidrawable meshes together. If you aren’t using the multidraw feature, you can safely set the batch set key to ().

Key render phases off the main world view entity, not the render world view entity. #

Areas:Rendering.
PRs:#16942

Make indirect drawing opt-out instead of opt-in, enabling multidraw by default. #

Areas:Rendering.
PRs:#16757
  • Indirect drawing (GPU culling) is now enabled by default, so the GpuCulling component is no longer available. To disable indirect mode, which may be useful with custom render nodes, add the new NoIndirectDrawing component to your camera.

Make the get function on InstanceInputUniformBuffer less error prone #

Areas:Rendering.
PRs:#17131

InstanceInputUniformBuffer::get now returns Option<BDI> instead of BDI to reduce panics. If you require the old functionality of InstanceInputUniformBuffer::get consider using InstanceInputUniformBuffer::get_unchecked.


Make the default directional light shadow cascade settings similar to those of other engines. #

Areas:Rendering.
PRs:#17552
  • The default shadow cascade far distance has been changed from 1000 to 150, and the default first cascade far bound has been changed from 5 to 10, in order to be similar to the defaults of other engines.

Mesh::merge to return a Result #

Areas:Rendering.
PRs:#17475
  • Mesh::merge now returns a Result<(), MeshMergeError>.

Move TextureAtlas and friends into bevy_image #

Areas:Rendering.
PRs:#17219

The following types have been moved from bevy_sprite to bevy_image: TextureAtlas, TextureAtlasBuilder, TextureAtlasSources, TextureAtlasLayout and DynamicTextureAtlasBuilder.

If you are using the bevy crate, and were importing these types directly (e.g. before use bevy::sprite::TextureAtlas), be sure to update your import paths (e.g. after use bevy::image::TextureAtlas)

If you are using the bevy prelude to import these types (e.g. use bevy::prelude::*), you don’t need to change anything.

If you are using the bevy_sprite subcrate, be sure to add bevy_image as a dependency if you do not already have it, and be sure to update your import paths.


Move non-generic parts of the PrepassPipeline to internal field #

Areas:Rendering.
PRs:#18322

If you were using a field of the PrepassPipeline, most of them have now been move to PrepassPipeline::internal.


Native unclipped depth on supported platforms #

Areas:Rendering.
PRs:#16095
  • MeshPipelineKey::DEPTH_CLAMP_ORTHO is now MeshPipelineKey::UNCLIPPED_DEPTH_ORTHO
  • The DEPTH_CLAMP_ORTHO shaderdef has been renamed to UNCLIPPED_DEPTH_ORTHO_EMULATION
  • clip_position_unclamped: vec4<f32> is now unclipped_depth: f32

Newtype Anchor #

Areas:Rendering.
PRs:#18439

The anchor component has been changed from an enum to a struct newtyping a Vec2. The Custom variant has been removed, instead to construct a custom Anchor use its tuple constructor:

Sprite {
     anchor: Anchor(Vec2::new(0.25, 0.4)),
     ..default()
}

The other enum variants have been replaced with corresponding constants:

  • Anchor::BottomLeft to Anchor::BOTTOM_LEFT
  • Anchor::Center to Anchor::CENTER
  • Anchor::TopRight to Anchor::TOP_RIGHT
  • .. and so on for the remaining variants

Only use the AABB center for mesh visibility range testing if specified. #

Areas:Rendering.
PRs:#16468
  • The VisibilityRange component now has an extra field, use_aabb. Generally, you can safely set it to false.

Reduce the clusterable object UBO size below 16384 for WebGL 2. #

Areas:Rendering.
PRs:#16069

MAX_UNIFORM_BUFFER_CLUSTERABLE_OBJECTS has been reduced from 256 to 204.


Refactor and simplify custom projections #

Areas:Rendering.
PRs:#17063
  • PerspectiveProjection and OrthographicProjection are no longer components. Use Projection instead.
  • Custom projections should no longer be inserted as a component. Instead, simply set the custom projection as a value of Projection with Projection::custom().

Remove the type parameter from check_visibility, and only invoke it once. #

Areas:Rendering.
PRs:#16812
  • check_visibility no longer takes a QueryFilter, and there’s no need to add it manually to your app schedule anymore for custom rendering items. Instead, entities with custom renderable components should add the appropriate type IDs to VisibilityClass. See custom_phase_item for an example.

Support scale factor for image render targets #

Areas:Rendering.
PRs:#16796

RenderTarget::Image now takes an ImageRenderTarget instead of a Handle<Image>. You can call handle.into() to construct an ImageRenderTarget using the same settings as before.


Upgrade to wgpu v24 #

Areas:Rendering.
PRs:#17542
  • Bevy has upgraded to wgpu v24.
  • When using the DirectX 12 rendering backend, the new priority system for choosing a shader compiler is as follows:
    • If the WGPU_DX12_COMPILER environment variable is set at runtime, it is used
    • Else if the new statically-linked-dxc feature is enabled, a custom version of DXC will be statically linked into your app at compile time.
    • Else Bevy will look in the app’s working directory for dxcompiler.dll and dxil.dll at runtime.
    • Else if they are missing, Bevy will fall back to FXC (not recommended)

Use multi_draw_indirect_count where available, in preparation for two-phase occlusion culling. #

Areas:Rendering.
PRs:#17211
  • Systems that add custom phase items now need to populate the indirect drawing-related buffers. See the specialized_mesh_pipeline example for an example of how this is done.

Use unchecked shaders for better performance #

Areas:Rendering.
PRs:#17767
  • Bevy no longer turns on wgpu’s runtime safety checks https://docs.rs/wgpu/latest/wgpu/struct.ShaderRuntimeChecks.html. If you were using Bevy with untrusted shaders, please file an issue.

cleanup bevy_render/lib.rs #

Areas:Rendering.
PRs:#16481

RenderCreation::Manual variant fields are now wrapped in a struct called RenderResources


Areas:Rendering.
PRs:#16827

This section is optional. If there are no breaking changes, you can delete this section.

  • If this PR is a breaking change (relative to the last release of Bevy), describe how a user might need to migrate their code to support these changes
  • Simply adding new functionality is not a breaking change.
  • Fixing behavior that was definitely a bug, rather than a questionable design choice is not a breaking change.

ExtractedSprites slice buffer #

Areas:Rendering, Text.
PRs:#17041
  • ExtractedSprite has a new kind: ExtractedSpriteKind field with variants Single and Slices.

    • Single represents a single sprite. ExtractedSprite’s anchor, rect, scaling_mode and custom_size fields have been moved into Single.
    • Slices contains a range that indexes into a new resource ExtractedSlices. Slices are used to draw elements composed from multiple sprites such as text or nine-patched borders.
  • ComputedTextureSlices::extract_sprites has been renamed to extract_slices. Its transform and original_entity parameters have been removed.


Improved UiImage and Sprite scaling and slicing APIs #

Areas:Rendering, UI.
PRs:#16088

The ImageScaleMode component has been removed. Instead, SpriteImageMode and NodeImageMode have been created for a new field image_mode on both Sprite and UiImage

In most cases, this means code that spawns an entity with

(
    UiImage::new(image.clone()),
    ImageScaleMode::Sliced(slicer.clone()),
)

should be converted to:

(
    UiImage::new(image.clone())
        .with_mode(NodeImageMode::Sliced(slicer.clone())),
)

Rename DefaultCameraView #

Areas:Rendering, UI.
PRs:#17235

DefaultCameraView has been renamed to UiCameraView


Rename TargetCamera to UiTargetCamera #

Areas:Rendering, UI.
PRs:#17403

TargetCamera has been renamed to UiTargetCamera.


BorderRect maintenance #

Areas:Rendering, UI.
PRs:#16727

The square and rectangle functions belonging to BorderRect have been renamed to all and axes.

Scenes #

Only despawn scene entities still in the hierarchy #

Areas:Scenes.
PRs:#17938

If you previously relied on scene entities no longer in the hierarchy being despawned when the scene root is despawned , use SceneSpawner::despawn_instance() instead.

Tasks #

Support on_thread_spawn and on_thread_destroy for TaskPoolPlugin #

Areas:Tasks.
PRs:#13045
  • TaskPooolThreadAssignmentPolicy now has two additional fields: on_thread_spawn and on_thread_destroy. Please consider defaulting them to None.

Text #

Add byte information to PositionedGlyph #

Areas:Text.
PRs:#17900

PositionedGlyph::new() has been removed as there is no longer an unused field. Create new PositionedGlyphs directly.


Remove the atlas_scaling field from ExtractedUiItem::Gylphs. #

Areas:Text.
PRs:#17047

The atlas_scaling field from ExtractedUiItem::Gylphs has been removed. This shouldn’t affect any existing code as it wasn’t used for anything.


add line height to TextFont #

Areas:Text.
PRs:#16614

TextFont now has a line_height field. Any instantiation of TextFont that doesn’t have ..default() will need to add this field.

UI #

Fixing ValArithmeticError typo and unused variant #

Areas:UI.
PRs:#17597
  • ValArithmeticError::NonEvaluateable has been renamed to NonEvaluateable::NonEvaluable
  • ValArithmeticError::NonIdenticalVariants has been removed

Move TextureAtlas into UiImage and remove impl Component for TextureAtlas #

Areas:UI.
PRs:#16072

Before:

commands.spawn((
  UiImage::new(image),
  TextureAtlas { index, layout },
));

After:

commands.spawn(UiImage::from_atlas_image(image, TextureAtlas { index, layout }));

Before:

commands.spawn(UiImage {
    texture: some_image,
    ..default()
})

After:

commands.spawn(UiImage {
    image: some_image,
    ..default()
})

Multiple box shadow support #

Areas:UI.
PRs:#16502

Bevy UI now supports multiple shadows per node. A new struct ShadowStyle is used to set the style for each shadow. And the BoxShadow component is changed to a tuple struct wrapping a vector containing a list of ShadowStyles. To spawn a node with a single shadow you can use the new constructor function:

commands.spawn((
    Node::default(),
    BoxShadow::new(
        Color::BLACK.with_alpha(0.8),
        Val::Percent(offset.x),
        Val::Percent(offset.y),
        Val::Percent(spread),
        Val::Px(blur),
    )
));

Only use physical coords internally in bevy_ui #

Areas:UI.
PRs:#16375

ComputedNode’s fields and methods now use physical coordinates. ComputedNode has a new field inverse_scale_factor. Multiplying the physical coordinates by the inverse_scale_factor will give the logical values.


Remove custom rounding #

Areas:UI.
PRs:#16097

UiSurface::get_layout now also returns the final sizes before rounding. Call .0 on the Ok result to get the previously returned taffy::Layout value.


Remove the min and max fields from LayoutContext. #

Areas:UI.
PRs:#16459

The min and max fields have been removed from LayoutContext. To retrieve these values call min_element and max_element on LayoutContent::physical_size instead.


Rename UiBoxShadowSamples to BoxShadowSamples. #

Areas:UI.
PRs:#16505

UiBoxShadowSamples has been renamed to BoxShadowSamples


UiImage -> ImageNode, UiImageSize -> ImageNodeSize #

Areas:UI.
PRs:#16271

Before:

commands.spawn(UiImage::new(image));

After:

commands.spawn(ImageNode::new(image));

Windowing #

Make CustomCursor variants CustomCursorImage/CustomCursorUrl structs #

Areas:Windowing.
PRs:#17518

The CustomCursor enum’s variants now hold instances of CustomCursorImage or CustomCursorUrl. Update your uses of CustomCursor accordingly.


Make RawHandleWrapper fields private to save users from themselves #

Areas:Windowing.
PRs:#16968

The window_handle and display_handle fields on RawHandleWrapper are no longer public. Use the newly added getters and setters to manipulate them instead.


Rework WindowMode::Fullscreen API #

Areas:Windowing.
PRs:#17525

WindowMode::SizedFullscreen(MonitorSelection) and WindowMode::Fullscreen(MonitorSelection) has become WindowMode::Fullscreen(MonitorSelection, VideoModeSelection). Previously, the VideoMode was selected based on the closest resolution to the current window size for SizedFullscreen and the largest resolution for Fullscreen. It is possible to replicate that behaviour by searching Monitor::video_modes and selecting it with VideoModeSelection::Specific(VideoMode) but it is recommended to use VideoModeSelection::Current as the default video mode when entering fullscreen.


Support texture atlases in CustomCursor::Image #

Areas:Windowing.
PRs:#17121

The CustomCursor::Image enum variant has some new fields. Update your code to set them.

Before:

CustomCursor::Image {
    handle: asset_server.load("branding/icon.png"),
    hotspot: (128, 128),
}

After:

CustomCursor::Image {
    handle: asset_server.load("branding/icon.png"),
    texture_atlas: None,
    flip_x: false,
    flip_y: false,
    rect: None,
    hotspot: (128, 128),
}

Without area #

:pencil2: Fix typos across bevy #

Areas:
PRs:#16702

This section is optional. If there are no breaking changes, you can delete this section.

(kept in case I include the reparameterize change here)

  • If this PR is a breaking change (relative to the last release of Bevy), describe how a user might need to migrate their code to support these changes
  • Simply adding new functionality is not a breaking change.
  • Fixing behavior that was definitely a bug, rather than a questionable design choice is not a breaking change.

Areas:
PRs:#14780

If you have been building your application for iOS:

Previously, the #[bevy_main] attribute created a main_rs entry point that most Xcode templates were using to run your Rust code from C. This was found to be unnecessary, as you can simply let Rust build your application as a binary, and run that directly.

You have two options for dealing with this:

If you’ve added further C code and Xcode customizations, or it makes sense for your use-case to continue link with Xcode, you can revert to the old behaviour by adding #[no_mangle] extern "C" main_rs() { main() } to your main.rs. Note that the old approach of linking a static library prevents the Rust standard library from doing runtime initialization, so certain functionality provided by std might be unavailable (stack overflow handlers, stdout/stderr flushing and other such functionality provided by the initialization routines).

The other, preferred option is to remove your “compile” and “link” build phases, and instead replace it with a “run script” phase that invokes cargo build --bin ..., and moves the built binary to the Xcode path $TARGET_BUILD_DIR/$EXECUTABLE_PATH. An example of how to do this can be viewed at [INSERT LINK TO UPDATED EXAMPLE PROJECT]. To make the debugging experience in Xcode nicer after this, you might also want to consider either enabling panic = "abort" or to set a breakpoint on the rust_panic symbol.


Remove Image::from_buffer name argument (only present in debug "dds" builds) #

Areas:
PRs:#18538
  • Image::from_buffer() no longer has a name argument that’s only present in debug builds when the "dds" feature is enabled. If you happen to pass a name, remove it.

Remove bevy_core #

Areas:

bevy_core has been removed and its items moved into more appropriate locations. Below are some tables showing where items have been moved to

Structs

Item0.15 Path0.16 Path
FrameCountbevy_corebevy_diagnostic
FrameCountPluginbevy_corebevy_diagnostic
Namebevy_corebevy_ecs::name
NameOrEntitybevy_corebevy_ecs::name
NameOrEntityItembevy_corebevy_ecs::name
NonSendMarkerbevy_corebevy_app
TaskPoolOptionsbevy_corebevy_app
TaskPoolPluginbevy_corebevy_app
TaskPoolThreadAssignmentPolicybevy_corebevy_app
TypeRegistrationPluginbevy_coreRemoved

Functions

Item0.15 Path0.16 Path
update_frame_countbevy_corebevy_diagnostic

Removed

  • TypeRegistrationPlugin no longer exists. If you can’t use a default App but still need Name registered, do so manually.

    // Before
    app.add_plugins(TypeRegistrationPlugin);
    
    // After
    app.register_type::<Name>();
    ```.
    
    
    
    
    
    
    
    
    
    
    
    

Rename trigger.entity() to trigger.target() #

Areas:
PRs:#16716
  • Rename Trigger::entity() to Trigger::target().
  • Rename ObserverTrigger::entity to ObserverTrigger::target

Use 4-byte LightmapSlabIndex for batching instead of 16-byte AssetId<Image> #

Areas:
PRs:#18326
  • Changed: RenderLightmap::new() no longer takes an AssetId<Image> argument for the asset id of the lightmap image.

Use target_abi = "sim" instead of ios_simulator feature #

Areas:
PRs:#17702

If you’re using a project that builds upon the mobile example, remove the ios_simulator feature from your Cargo.toml (Bevy now handles this internally).

Utils #

bevy_utils Refactor #

In 0.16 bevy_utils (and by extension bevy::utils) was significantly reduced with many of its items either being removed, spun-out into their own crates, or just moved into more appropriate existing crates. Below is a series of tables for all items that were in bevy_utils 0.15 that have since been moved or removed in 0.16.

Note that certain items have been completely removed, see below for further details.

Re-Exports

Item0.15 Path0.16 Path
hashbrownbevy_utilsRemoved
tracingbevy_utilsbevy_log

Structs

Item0.15 Path0.16 Path
AHasherbevy_utilsahash
Durationbevy_utilscore::time
FixedStatebevy_utilsbevy_platform_support::hash
Hashedbevy_utilsbevy_platform_support::hash
Instantbevy_utilsbevy_platform_support::time
NoOpHashbevy_utilsbevy_platform_support::time
PassHashbevy_utilsbevy_platform_support::time
PassHasherbevy_utilsbevy_platform_support::time
RandomStatebevy_utilsbevy_platform_support::time
SystemTimebevy_utilsstd::time
SystemTimeErrorbevy_utilsstd::time
TryFromFloatSecsErrorbevy_utilscore::time

Traits

Item0.15 Path0.16 Path
ConditionalSendbevy_utilsbevy_tasks
ConditionalSendFuturebevy_utilsbevy_tasks

Macros

Item0.15 Path0.16 Path
all_tuplesbevy_utilsvariadics_please
all_tuples_with_sizebevy_utilsvariadics_please
debug_oncebevy_utilsbevy_log
detailed_tracebevy_utilsRemoved
error_oncebevy_utilsbevy_log
info_oncebevy_utilsbevy_log
trace_oncebevy_utilsbevy_log
warn_oncebevy_utilsbevy_log

Functions

Item0.15 Path0.16 Path
assert_object_safebevy_utilsRemoved
dbgbevy_utilsRemoved
errorbevy_utilsRemoved
check_readybevy_utils::futuresbevy_tasks::futures
now_or_neverbevy_utils::futuresbevy_tasks::futures
infobevy_utilsRemoved
warnbevy_utilsRemoved

Type Aliases

Item0.15 Path0.16 Path
BoxedFuturebevy_utilsbevy_tasks
Entrybevy_utilsbevy_platform_support::collections::hash_map
HashMapbevy_utilsbevy_platform_support::collections
HashSetbevy_utilsbevy_platform_support::collections
StableHashMapbevy_utilsRemoved
StableHashSetbevy_utilsRemoved

Removed Items

  • assert_object_safe was removed in part because the term is now outdated (replaced with dyn compatibility) and otherwise because it is trivial to inline.

    // Before
    const _: () = assert_object_safe::<dyn MyTrait>();
    
    // After
    const _: Option<Box<dyn MyTrait>> = None;
    
  • hashbrown was removed from bevy_utils as a re-export due to its significant API change from hashbrown 0.14 to 0.15. Instead of exposing a large public API out of our direct control, we've taken a more explicit subset and moved it into bevy_platform_support::collections, mimicking the layout of the standard library. If you need access to hashbrown, take a direct dependency instead.

  • detailed_trace was removed due to its minimal use within the engine. If you still wish to use it, make sure you have taken a direct dependency on tracing and have a feature name detailed_trace defined in your Cargo.toml. You can use the below as a replacement:

    macro_rules! detailed_trace {
        ($($tts:tt)*) => {
            if cfg!(feature = "detailed_trace") {
                ::tracing::trace!($($tts)*);
            }
        }
    }
    
  • dbg, info, warn, and error were all removed due to minimal use within the engine. If you still wish to use them, make sure you have taken a direct dependency on tracing. You can use the below as a replacement:

    /// Calls the [`tracing::info!`] macro on a value.
    pub fn info<T: core::fmt::Debug>(data: T) {
        ::tracing::info!("{:?}", data);
    }
    
    /// Calls the [`tracing::debug!`] macro on a value.
    pub fn dbg<T: core::fmt::Debug>(data: T) {
        ::tracing::debug!("{:?}", data);
    }
    
    /// Processes a [`Result`] by calling the [`tracing::warn!`] macro in case of an [`Err`] value.
    pub fn warn<E: core::fmt::Debug>(result: Result<(), E>) {
        if let Err(warn) = result {
            ::tracing::warn!("{:?}", warn);
        }
    }
    
    /// Processes a [`Result`] by calling the [`tracing::error!`] macro in case of an [`Err`] value.
    pub fn error<E: core::fmt::Debug>(result: Result<(), E>) {
        if let Err(error) = result {
            ::tracing::error!("{:?}", error);
        }
    }
    
  • StableHashMap and StableHashSet were removed due to minimal use within the engine. You can use the below as a replacement:

    /// A stable hash-map.
    pub type StableHashMap<K, V> = bevy::platform_support::collections::HashMap<K, V, bevy::platform_support::hash::FixedState>;
    
    /// A stable hash-set.
    pub type StableHashSet<K> = bevy::platform_support::collections::HashSet<K, bevy::platform_support::hash::FixedState>;