- 0.18 to 0.19
- Resources as Components
- map_handle_to_font_id and get_font_id have been removed from TextPipeline
- Methods of Affine3 to_transpose and inverse_transpose_3x3 are now part of an extension trait
- AssetPath::resolve and resolve_embed now take &AssetPath
- IndirectParametersBuffers settings split
- The old bevy_scene is now bevy_world_serialization
- The experimental_bevy_feathers feature is no longer experimental
- Rename Font::try_from_bytes to Font::from_bytes
- Skybox image is now optional
- Resources MeshPipelineViewLayouts, MeshPipeline and RenderDebugOverlayPipeline are now created in RenderStartup systems
- RenderMeshInstance becomes atomic
- RenderSystems::ManageViews has been split into three system sets
- TextRoot, TextSpanAccess and TextSpanComponent are replaced by TextSection
- audio feature is now no longer implied by the 3d, 2d, or ui features
- WgpuSettingsPriority::Compatibility renamed to WgpuSettingsPriority::WebGPU
- shadow_pass has been split into per_view_shadow_pass and shared_shadow_pass
- Image::pixel_bytes and Image::pixel_data_offset now return Result
- New crate bevy_material
- Atmosphere is now an entity
- Invert bevy_gltf dependency with bevy_pbr
- New fields added to UiDebugOptions
- Rename System::type_id to System::system_type
- Ref now directly implements Clone and Copy
- Post Processing Split
- Reflect Struct QOL
- PositionedGlyph::span_index is now section_index
- DynamicSceneBuilder and DynamicScene::from_scene now require a &TypeRegistry
- Dropping Tasks in Web Builds
- Return type of Access::archetypal changed
- bevy_reflect reorganized to de-clutter the crate root
- Align bevy_transform's feature flags
- UiWidgetsPlugins and InputDispatchPlugin are now in DefaultPlugins
- Atmosphere has been moved to bevy_light
- bevy_window, bevy_input_focus, custom_cursor features moved to alternate feature collections
- extra_buffer_usages moved from MeshAllocator to MeshAllocatorSettings
- Avoiding unnecessary AssetEvent::Modified events that lead to rendering performance costs
- Interned is now reflectable but requires additional trait bounds
- EnvironmentMapUniform is removed
- bevy_shader cleanups
- new_with_ prefix removed from TextLayout constructors
- Changes to TextFont's font_size and font fields
- experimental_ui_widgets feature is no longer experimental
- Camera TextureFormat rework
- Rodio 0.22 Update
- InputFocus fields are no longer public
- PlaneMeshBuilder, allow for different number of subdivisions in X and Z directions
- EasyScreenRecordPlugin output_dir
- Measure changes
- FullscreenMaterial API changes
- set_executor replaced ExecutorKind
- New ComputedTextBlock::needs_rerender parameters
- MorphWeights and MeshMorphWeights have been restructured
- ViewportNode's camera is now an Option<Entity>
- WindowPlugin exit systems moved to Last
- rand, glam & uuid updated to latest versions
- DefaultErrorHandler renamed to FallbackErrorHandler
- Some bevy_camera primitives moved to bevy_math
- define_atomic_id now lives in bevy_utils
- World::entities_allocator is now World::entity_allocator
- Transmission has been moved to bevy_pbr
- New Node::direction field
- Feathers widgets moving to BSN
- Hdr moved to bevy_camera
- get_full_extension now returns Option<&str>.
- Mesh pipeline key requires strip index format bits
- Core prefix removed from UI widget components
- Lifecycle observers include old and new archetypes
- RelationshipAccessor improvements
- Change lists
- bevy_picking feature flag no longer includes bevy_input_focus
- PositionedGlyph's byte_index and byte_length fields have been removed
- Light gizmos have been moved from bevy_gizmos to bevy_light
- DataFormat renamed to TextureChannelLayout
- Contact Shadows
- Implementations of Reader now must implement Reader::seekable, and AsyncSeekForward is deleted.
- AnimationTargetId algorithm changes
- Bloom luma calculation now in linear space
- Render Graph as Systems
- DespawnOnEnter / DespawnOnExit can now trigger during same state transitions
- Lifecycle event changes
- ui feature is now no longer implied by the 3d or 2d features
- Skybox moved to bevy_light
- Command error handling has been simplified
- Added mouse panning to PanCamera component
- The validate_parent_has_component is superseded by ValidateParentHasComponentPlugin
- SystemParam validation is now done when fetching the data
- ExtractComponent refactor
- Nested query access
- StaticTransformOptimizations no longer stores a threshold for dynamic toggling
- Mesh view bind group layout is changed
- Occlusion culling is no longer experimental
- Morph targets are now stored in meshes.
- Advanced AssetServer load variants are now exposed through a builder pattern.
- PipelineCacheError renamed to ShaderCacheError
- ViewTarget's output accessors now return Option
- SystemBuffer requires queue() to be implemented
- FontAtlas changes
- ComputedNode::stack_index has been replaced by ComputedStackIndex
- Rename FeathersPlugin to FeathersCorePlugin
- Remove android game activity from default
- SavedAsset now contains two lifetimes, and AssetSaver now takes an AssetPath.
- ShaderStorageBuffer renamed to ShaderBuffer
- bevy_text migration from Cosmic Text to Parley
Migration Guide: 0.18 to 0.19
Like every major Bevy release, Bevy 0.19 comes with its own set of breaking changes. The most important changes to be aware of are listed below:
- Resources-as-components: resources are now stored as components on dedicated abstract entities
- Render-graph-as-systems: rendering now uses systems too!
- Parley text overhaul: we've moved from
cosmic-texttoparleyand revamped our text internals - Cargo feature collection changes: now more granular. Check here if your audio isn't working!
bevy_scenehas been renamed tobevy_world_serialization: making space forBSN- Bloom now uses linear color: more correct, looks subtly different
This list prioritizes changes which are sweeping overhauls (1-3) or break in confusing or quiet ways (4-6).
For a full list of changes that may require migration, please see below. We recommend keeping this page open as you migrate your code base, and searching within it for any issues you encounter.
If you run into a problem while migrating that was not covered by this migration guide, please open an issue on the bevy-website repo or file a PR directly with the correct migration advice.
Resources as Components #
#[derive(Resource)] implements the Component trait #
In 0.19, Resource is a subtrait of Component and #[derive(Resource)] implements both Resource as well as Component. This means it's no longer possible to doubly derive both Component and Resource.
Types can no longer meaningfully be used as both resources and components. This was a deliberate change: ensuring that "resource" always ensures uniqueness, regardless of how the type is used.
Inserting new copies of any type which implements Resource, even as a component, will despawn other copies of that component on other entities. Similarly, data of type T stored as a resource will show up in queries for e.g. &T, leading to confusing new bugs.
To migrate, you should split these types into distinct resource and component types:
// 0.18
#[derive(Component, Resource)]
struct MyData(f32);
becomes
// 0.19
#[derive(Component)]
struct MyDataComp(f32);
#[derive(Resource)]
struct MyDataRes(f32);
We had a few types that were both resources and components inside of Bevy itself, for a "global fallback" pattern. UiDebugOverlay is split into GlobalUiDebugOverlay (resource) and UiDebugOverlay (component), and UiDebugOptions is split into GlobalUiDebugOptions (resource) and UiDebugOptions (component).
#[reflect(Resource)] Changes #
In 0.19, the ReflectResource is a ZST (zero-sized type) and only functions to signify that the trait is reflected. Instead, #[reflect(Resource)] also reflects the Component trait, so use ReflectComponent instead. This is likely to show up in code that uses reflection, like BRP (Bevy Remote Protocol) and bevy_world_serialization.
Broad Queries and System Conflicts #
Now that resources are components, they can be queried using 'broad' queries. These are queries that query all entities. Examples include:
Query<()>Query<Entity>Query<EntityMut>Query<EntityRef>Query<EntityMutExcept>Query<EntityRefExcept>Query<Option<&T>>
These should rarely come up in real games, but if they do, they might conflict with resource access, i.e.
fn system(entity_query: Query<EntityMut>, some_resource: Res<MyResource>) {} // err! entity_query conflicts with some_resource
To fix this, you can narrow down the query by using either the Without<MyResource> or Without<IsResource> filter. The IsResource marker is attached to all resource entities, so it always filters them out.
The same is true for non-send data:
fn system(entity_query: Query<EntityMut>, some_non_send: NonSend<MyNonSend>) {} // err! entity_query conflicts with some_resource
This can be fixed by adding a Without<MyNonSend> filter to the query.
Renaming Non-Send Resources to Non-Send Data #
Previously there were two types of resources: Send resources and !Send resources. Now that Send resources are stored as components, !Send resources have little in common with their Send counterparts. This is why non-send resources are being renamed to non-send data. The following APIs are affected:
App::init_non_send_resourceis deprecated in favor ofApp::init_non_send.App::insert_non_send_resourceis deprecated in favor ofApp::insert_non_send.DeferredWorld::non_send_resource_mutis deprecated in favor ofDeferredWorld::non_send_mut.DeferredWorld::get_non_send_resource_mutis deprecated in favor ofDeferredWorld::get_non_send_mut.ResourceData<SEND: true>is removed, whileResourceData<SEND: false>is renamed toNonSendData.Resources<SEND: true>is removed andResources<Send: false>is renamed toNonSends.UnsafeWorldCell::get_non_send_resourceis deprecated in favor ofUnsafeWorldCell::get_non_send.UnsafeWorldCell::get_non_send_resource_by_idis deprecated in favor ofUnsafeWorldCell::get_non_send_by_id.UnsafeWorldCell::get_non_send_resource_mutis deprecated in favor ofUnsafeWorldCell::get_non_send_mut.UnsafeWorldCell::get_non_send_resource_mut_by_idis deprecated in favor ofUnsafeWorldCell::get_non_send_mut_by_id.World::init_non_send_resourceis deprecated in favor ofWorld::init_non_send.World::insert_non_send_resourceis deprecated in favor ofWorld::insert_non_send.World::remove_non_send_resourceis deprecated in favor ofWorld::remove_non_send.World::non_send_resourceis deprecated in favor ofWorld::non_send.World::non_send_resource_mutis deprecated in favor ofWorld::non_send_mut.World::get_non_send_resourceis deprecated in favor ofWorld::get_non_send.World::get_non_send_resource_mutis deprecated in favor ofWorld::get_non_send_mut.
Component Registration #
Before using components and resources they must be registered to a world. The registration process for components and resources is very similar and now that Send resources are components, we're able to simplify some of the code; removing / deprecating some methods.
Components::register_resource_uncheckedis renamed toComponents::register_non_send_unchecked.Components::get_valid_resource_idwas deprecated in favor ofComponents::get_valid_id.Components::valid_resource_idwas deprecated in favor ofComponents::valid_component_id.Components::resource_idwas deprecated in favor ofComponents::component_id.ComponentsRegistrator::register_resourceis deprecated in favor ofComponentsRegistrator::register_component.ComponentsRegistrator::register_resource_withis renamed toComponentsRegistrator::register_non_send_with.ComponentsRegistrator::register_resource_with_descriptoris removed in favor ofComponentsRegistrator::register_component_with_descriptor.ComponentsQueuedRegistrator::queue_register_resource_with_descriptorwas removed in favor ofComponentsQueuedRegistrator::queue_register_component_with_descriptor.ComponentsQueuedRegistrator::queue_register_resourcewas deprecated in favor ofComponentsQueuedRegistrator::queue_register_component.ComponentDescriptor::new_resourcewas deprecated in favor ofComponentDescriptor::newWorld::register_resource_with_descriptorwas renamed toWorld::register_non_send_with_descriptor.
Access #
Resources were also removed from Access, which keeps track what data any given query / system has access to.
Access::add_component_readandAccess::add_resource_readwere deprecated in favor ofAccess::add_read.Access::add_component_writeandAccess::add_resource_writewere deprecated in favor ofAccess::add_write.Access::remove_component_readwas deprecated in favor ofAccess::remove_read.Access::remove_component_writewas deprecated in favor ofAccess::remove_write.Access::has_component_readandAccess::has_resource_readwere deprecated in favor ofAccess::has_read.Access::has_any_component_readandAccess::has_any_resource_readwere deprecated in favor ofAccess::has_any_read.Access::has_component_writeandAccess::has_resource_writewere deprecated in favor ofAccess::has_write.Access::has_any_component_writeandAccess::has_any_resource_writewere deprecated in favor ofAccess::has_any_write.Access::read_all_componentswas deprecated in favor ofAccess::read_all.Access::write_all_componentswas deprecated in favor ofAccess::write_all.Access::read_all_resourcesandAccess::write_all_resourceswere removed.Access::has_read_all_componentswas deprecated in favor ofAccess::has_read_all.Access::has_write_all_componentswas deprecated in favor ofAccess::has_write_all.Access::has_read_all_resourcesandAccess::has_write_all_resourceswere removed.Access::is_components_compatiblewas deprecated in favor ofAccess::is_compatible.Access::is_resources_compatiblewas removed.Access::is_subset_componentswas deprecated in favor ofAccess::is_subset.Access::is_subset_resourceswas removed.Access::resource_reads_and_writes,Access::resource_reads,Access::resource_writeswere removed.Access::try_iter_component_accesswas deprecated in favor ofAccess::try_iter_access.FilteredAccess::add_component_readwas deprecated in favor ofFilteredAccess::add_read.FilteredAccess::add_component_writewas deprecated in favor ofFilteredAccess::add_write.FilteredAccess::add_resource_readandFilteredAccess::add_resource_writewere removed.FilteredAccess::read_all_componentswas deprecated in favor ofFilteredAccess::read_all.FilteredAccess::write_all_componentswas deprecated in favor ofFilteredAccess::write_all.FilteredAccessSet::add_unfiltered_resource_readwas deprecated in favor ofFilteredAccessSet::add_resource_read.FilteredAccessSet::add_unfiltered_resource_writewas deprecated in favor ofFilteredAccessSet::add_resource_write.
Due to the split storage it used to be possible to both access an entity and a resource in a WorldQuery implementor. This is no longer valid. In order to access multiple different entities for a WorldQuery implementation, use WorldQuery::init_nested_access. See the implementation of WorldQuery for AssetChanged for an example of how this can be done correctly.
Immutable Resources #
Since resources may now be immutable, the following now carry a Mutability = Mutable bound:
ResMutWorld::resource_mut,World::get_resource_mutUnsafeWorldCell::get_resource_mutEntityWorldMut::resource_mut,EntityWorldMut::get_resource_mutDeferredWorld::resource_mut,DeferredWorld::get_resource_mutTemplateContext::resource_mut- as well as
ExtractResourcePlugin
If you are calling these in a generic context and the resource is always mutable, you need to add this bound to your type parameter:
// 0.18
fn my_generic_system<R: Resource>(mut res: ResMut<R>) {
…
}
// 0.19
fn my_generic_system<R: Resource<Mutability = Mutable>>(mut res: ResMut<R>) {
…
}
If the bound cannot be added, there are a couple of options:
- If the resource is only sometimes mutable OR the API should not be unsafe, use
World::modify_resourceandWorld::modify_resource_by_id. They behave exactly like their component counterparts. - If your API can be made unsafe, use the
UnsafeWorldCell::*_assume_mutablemethods and make sure that the safety conditions are satisfied.UnsafeWorldCell::get_resource_mut_assume_mutablehas been provided for this explicit purpose. It is also possible to use the*_assume_mutablecomponent methods, but you will first have to retrieve the resource entity fromResourceEntities.
Miscellaneous #
Since MapEntities is implemented by default for components, it's no longer necessary to add derive(MapEntities) to a resource.
// 0.18
#[derive(Resource, MapEntities)]
struct EntityStruct(#[entities] Entity);
// 0.19
#[derive(Resource)]
struct EntityStruct(#[entities] Entity);
Next, World::clear_entities now also clears all resources, and World::clear_all now clears all entities, resources, and non-send data.
Lastly, World::remove_resource_by_id now returns bool instead of Option<()>.
map_handle_to_font_id and get_font_id have been removed from TextPipeline #
map_handle_to_font_id and get_font_id have been removed from TextPipeline.
The ID and family name of Font assets can be retrieved from the asset itself.
Methods of Affine3 to_transpose and inverse_transpose_3x3 are now part of an extension trait #
With the addition of Affine3 on glam, Bevy's version was removed. To keep the functionality that Bevy's version provided we created the extension trait Affine3Ext. Locations that accessed Affine3::to_transpose or Affine3::inverse_transpose_3x3 will now need the extension trait to be in scope.
AssetPath::resolve and resolve_embed now take &AssetPath #
AssetPath::resolve and AssetPath::resolve_embed no longer accept &str and now take &AssetPath directly. The previous string-based APIs have been renamed to resolve_str and resolve_embed_str.
This change avoids unnecessary string allocation and parsing when an AssetPath is already available. To migrate, pass an AssetPath directly to resolve or resolve_embed; when working with strings, use the corresponding *_str methods instead.
IndirectParametersBuffers settings split #
allow_copies_from_indirect_parameter_buffers has been moved from IndirectParametersBuffers to a new IndirectParametersBuffersSettings resource.
The old bevy_scene is now bevy_world_serialization #
In Bevy 0.19 we landed a subset of Bevy's Next Generation Scene system (known as BSN), which now lives in the bevy_scene / bevy::scene crate. However the old bevy_scene system still needs to stick around for a bit longer, as it provides some features that Bevy's Next Generation Scene system doesn't (yet!):
- It is not yet possible to write a World to BSN, so the old system is still necessary for "round trip World serialization".
- The GLTF scene loader has not yet been ported to BSN, so the old system is still necessary to spawn GLTF scenes in Bevy.
For this reason, we have renamed the old bevy_scene crate to bevy_world_serialization. If you were referencing bevy_scene::* or bevy::scene::* types, rename those paths to bevy_world_serialization::* and bevy::world_serialization::* respectively.
Additionally, to avoid confusion / conflicts with the new scene system, all "scene" terminology / types have been reframed as "world serialization":
Scene->WorldAsset(as this was always just a World wrapper)SceneRoot->WorldAssetRootDynamicScene->DynamicWorldDynamicScene::from_scene->DynamicWorld::from_world_assetDynamicSceneBuilder->DynamicWorldBuilderDynamicSceneRoot->DynamicWorldRootSceneInstanceReady->WorldInstanceReadySceneLoader->WorldAssetLoaderScenePlugin->WorldSerializationPluginSceneRootTemplate->WorldAssetRootTemplateSceneSpawner->WorldInstanceSpawnerSceneFilter->WorldFilterSceneLoaderError->WorldAssetLoaderErrorSceneSpawnError->WorldInstanceSpawnError
GLTF scene spawning is the most likely source of breakage for most people, as round trip world serialization is a relatively niche use case. For most people, the migration should be as simple as:
// before
commands.spawn(SceneRoot(asset_server.load("scene.gltf#Scene0")));
// after
commands.spawn(WorldAssetRoot(asset_server.load("scene.gltf#Scene0")));
We know this naming is a bit awkward. Once we port GLTF loading over to BSN (hopefully in the next release), you will be able to do cool stuff like this:
bsn! {
:"scene.gltf#Scene0"
Transform { position: Vec3 { x: 10. } }
}
This would set just the x position in the GLTF scene root to x, patching on top of the position defined in the gltf scene. Cool!
The experimental_bevy_feathers feature is no longer experimental #
The feature flag experimental_bevy_feathers is now bevy_feathers.
With the introduction of bsn! and another cycle of bug fixes and features, the Feathers UI framework is now stable enough for broad use in tooling. While it remains incomplete and is subject to breaking changes, it is no longer substantially more experimental than the rest of Bevy.
Rename Font::try_from_bytes to Font::from_bytes #
Font::try_from_bytes has been renamed to Font::from_bytes to reflect that it no longer returns Result.
// 0.18
let font = Font::try_from_bytes(bytes.to_vec()).unwrap();
// 0.19
let font = Font::from_bytes(bytes.to_vec());
Skybox image is now optional #
The image field of the Skybox component now has the type Option<Handle<Image>> instead of Handle<Image>. A Skybox component without an image will not draw anything, just as though it isn't present.
If you were creating a skybox with an image, wrap the image handle in Some:
// 0.18
Skybox {
image: my_skybox,
brightness: 1000.0,
..default()
}
// 0.19
Skybox {
image: Some(my_skybox),
brightness: 1000.0,
..default()
}
If you were previously creating a Skybox component with a placeholder image to be changed later, you can now remove the placeholder:
// 0.18
Skybox {
image: cubemap_image_that_will_not_actually_be_seen,
brightness: 1000.0,
..default()
}
// 0.19
Skybox {
brightness: 1000.0,
..default()
}
Resources MeshPipelineViewLayouts, MeshPipeline and RenderDebugOverlayPipeline are now created in RenderStartup systems #
Systems using the MeshPipelineViewLayouts, MeshPipeline and RenderDebugOverlayPipeline resources in the RenderStartup schedule now need to be run after the MeshPipelineSystems system set.
RenderMeshInstance becomes atomic #
In order to enhance the performance and scalability of RenderMeshInstance, its fields have been made atomic. Code that accessed fields like:
let instance: &RenderMeshInstance = ...;
... instance.mesh_asset_id ...
Should now use the accessor methods like this:
let instance: &RenderMeshInstance = ...;
... instance.mesh_asset_id() ...
There are associated setter methods with a set_ prefix as well. Note that, because RenderMeshInstance fields are atomic, you don't need an &mut reference to call them. However, it's now your responsibility to avoid data races.
RenderSystems::ManageViews has been split into three system sets #
ManageViews was previously somewhat overloaded with responsibility, and made resolving render system order ambiguities difficult. To fix this, ManageViews has been split into three phases: CreateViews, Specialize, and PrepareViews. It is very likely whatever you were ordering against ManageViews can now be ordered against PrepareViews and have identical behavior. If you are creating additional views, for example for cubemap rendering, please do so in CreateViews.
TextRoot, TextSpanAccess and TextSpanComponent are replaced by TextSection #
The TextRoot, TextSpanAccess and TextSpanComponent traits have been consolidated into a single trait TextSection.
The methods read_span and write_span have been renamed to get_text and get_text_mut, respectively.
audio feature is now no longer implied by the 3d, 2d, or ui features #
Our default features used to be
2d3dui
where each of these features enabled the audio feature among others.
Since Cargo doesn't allow selectively disabling features, if you wished to disable bevy_audio, either because you don't need audio or because you use an alternative such as bevy_seedling, you had a problem. You needed to essentially enable all features enabled by the above except audio, leading to a big features soup. To avoid this, audio is now no longer enabled by the above features, but instead enabled by default, bumping our default features to:
2d3duiaudio
Now what does this mean for you?
- If you used all default features before, nothing changes for you.
- If you want to opt out of using
bevy_audio, it is now as simple as disabling default features, and manually opting into3d,2d, and/orui. - If you already opted into non-default features and want to continue using
bevy_audio, you will now have to add theaudiofeature.
WgpuSettingsPriority::Compatibility renamed to WgpuSettingsPriority::WebGPU #
WgpuSettingsPriority::Compatibility has been renamed to WgpuSettingsPriority::WebGPU. To set this using an environment variable, update WGPU_SETTINGS_PRIO to webgpu from compatibility.
shadow_pass has been split into per_view_shadow_pass and shared_shadow_pass #
shadow_pass has been split into per_view_shadow_pass (for rendering DirectionalLight shadow maps) and shared_shadow_pass (for rendering PointLight and SpotLight shadow maps).
Image::pixel_bytes and Image::pixel_data_offset now return Result #
Image::pixel_bytes, Image::pixel_bytes_mut, and Image::pixel_data_offset now return Result<..., TextureAccessError>.
Previously, these methods returned Option and would silently fail for both out-of-bounds access and unsupported texture formats (such as compressed textures). This caused error information to be lost, making it impossible for callers to distinguish between these different failure cases.
Now, these methods properly propagate TextureAccessError:
TextureAccessError::OutOfBoundsfor coordinates outside the image boundsTextureAccessError::UnsupportedTextureFormatfor compressed or unsupported texture formatsTextureAccessError::Uninitializedif the image data is not initialized
Update any code using these methods to handle the Result return type:
// 0.18
if let Some(bytes) = image.pixel_bytes(coords) {
// use bytes
}
// 0.19
match image.pixel_bytes(coords) {
Ok(bytes) => {
// use bytes
}
Err(TextureAccessError::Uninitialized) => {
// handle missing image data
}
Err(TextureAccessError::OutOfBounds { .. }) => {
// handle out of bounds
}
Err(TextureAccessError::UnsupportedTextureFormat(format)) => {
// handle compressed/unsupported format
}
}
New crate bevy_material #
Various material-related machinery was extracted from bevy_pbr and bevy_render into a new crate called bevy_material.
The following were moved from bevy_render to bevy_material:
AlphaModeSpecializedMeshPipelineError
The following were moved from bevy_pbr to bevy_material:
OpaqueRendererMethodErasedMeshPipelineKey,ErasedMaterialPipelineKey,ErasedMaterialKey,ErasedMaterialKeyVTable,RenderPhaseTypeMaterialProperties
The following were moved from bevy_render to bevy_material but do not require migration thanks to re-exports:
BindGroupLayoutDescriptor,RenderPipelineDescriptor,NoFragmentStateError,VertexState,FragmentState,ComputePipelineDescriptorShaderLabel,DrawFunctionLabel,DrawFunctionIdBindGroupLayoutEntryBuilder,BindGroupLayoutEntries,DynamicBindGroupLayoutEntries,IntoBindGroupLayoutEntryBuilder,IntoIndexedBindGroupLayoutEntryBuilderArray
Atmosphere is now an entity #
Previously, the Atmosphere component was added to the camera. In 0.19, the Atmosphere is spawned as an entity. The nearest atmosphere will be chosen for rendering.
AtmosphereSettings still belongs to the camera. This is the component that enables atmosphere rendering for that view.
The scene_units_to_m field has been removed from AtmosphereSettings. Use Transform on the Atmosphere entity for scale. It is inversely proportional to the old scene_units_to_m factor. For example, to treat one unit as 1 km (as with scene_units_to_m: 1000.0), set scale to 0.001.
// 0.18
commands.spawn((
Camera3d::default(),
Atmosphere::earth(earth_medium),
AtmosphereSettings {
scene_units_to_m: 1000.0,
..default()
},
));
// 0.19
let earth = Atmosphere::earth(earth_medium);
let scale = 0.001;
commands.spawn((
earth,
Transform::from_scale(Vec3::splat(scale)).with_translation(-Vec3::Y * earth.inner_radius * scale),
));
commands.spawn((
Camera3d::default(),
AtmosphereSettings::default(),
));
If you don't need to customize the scale, a default Transform component is added that positions the atmosphere such that the horizon lines up with the camera's default Y-up direction.
commands.spawn(Atmosphere::earth(earth_medium));
The bottom_radius and top_radius fields on the Atmosphere component have been renamed to inner_radius and outer_radius respectively to reflect their new meaning.
See the updated atmosphere example and documentation for details.
There have been some renames:
Atmosphere::earthlike->Atmosphere::earthbevy::pbr::Atmosphere->bevy::light::Atmospherebevy::pbr::ScatteringMedium->bevy::light::atmosphere::ScatteringMedium
Invert bevy_gltf dependency with bevy_pbr #
Previously, bevy_gltf depended on bevy_pbr. This meant scene definition was tightly coupled to rendering. This dependency has been inverted, to allow bevy_gltf to function without any of the rendering stack present.
bevy_gltf is now also an optional dependency.
In 0.18, loading a material sub-asset would return a Handle<StandardMaterial>.
let handle: Handle<StandardMaterial> = asset_server.load("models/animated/Fox.glb#Material0");
In 0.19, loading a material sub-asset loads a GltfMaterial to accurately represent the data in the glTF file. To load the StandardMaterial, use the /std suffix when the bevy_pbr feature is turned on (the feature is on by default).
let handle: Handle<GltfMaterial> = asset_server.load("models/animated/Fox.glb#Material0");
let handle_std: Handle<StandardMaterial> = asset_server.load("models/animated/Fox.glb#Material0/std");
You can disable PBR rendering by initializing PbrPlugin as follows:
PbrPlugin {
gltf_enable_standard_materials: false,
..Default::default()
}
GltfExtensionHandler trait's methods have been updated:
on_materialpasses in thematerial_asset : &GltfMaterialandmaterial_label: &stron_spawn_mesh_and_materialalso passes in thematerial_label: &str
UvChannel has moved from bevy_pbr to bevy_mesh.
New fields added to UiDebugOptions #
UiDebugOptions has new bool fields: outline_border_box, outline_padding_box, outline_content_box,outline_scrollbars, and ignore_border_radius. To match the previous behavior of UiDebugOptions, where only the border box outline was rendered, use the default values with outline_border_box: true and the rest of the new fields set to false.
Rename System::type_id to System::system_type #
System::type_id has been renamed to System::system_type to avoid shadowing Any::type_id.
The old System::type_id method is now deprecated and will be removed in a future release. Replace all calls to System::type_id with System::system_type:
// 0.18
let id = my_system.type_id();
// 0.19
let id = my_system.system_type();
If you have a custom System implementation that overrides type_id, rename it to system_type.
Ref now directly implements Clone and Copy #
Ref now implements Clone and Copy, which means calling ref.clone() now returns another Ref<T> rather than a cloned inner T. To continue cloning the inner T, use ref.as_ref().clone(), ref.deref().clone(), or ref.into_inner().clone().
Post Processing Split #
For both Core2dSystems and Core3dSystems, the PostProcess system set has been split into EarlyPostProcess and PostProcess. 2D now also has a Prepass. Both systems have the following sets:
PrepassMainPassEarlyPostProcessPostProcess
Reflect Struct QOL #
bevy_reflect::DynamicStruct::index_of was moved to the bevy_reflect::Struct trait and is now bevy_reflect::Struct::index_of_name. Most utility methods already existed on Struct, except for the method to get a fields index by name.
The bevy_reflect::FieldIter iterator had its items changed, from &dyn PartialReflect to a tuple of (&str, &dyn PartialReflect), so that you can iterate the names alongside the fields.
PositionedGlyph::span_index is now section_index #
PositionedGlyph::span_index has been renamed to section_index, because only a TextSpan entity should be referred to as a "span". We use "section" when an entity could be either a text root or a TextSpan.
DynamicSceneBuilder and DynamicScene::from_scene now require a &TypeRegistry #
Previously, DynamicSceneBuilder and DynamicScene (now DynamicWorldBuilder and DynamicWorld respectively) would get the type registry out of the world being extracted. However, when building a world from scratch just for serialization, this required artificially cloning the registry and putting it in the world being saved.
In 0.19 DynamicWorldBuilder and DynamicWorld::from_world_asset require an existing type registry in Bevy. For example, before:
// 0.18
let world: &World = ...;
let scene = DynamicSceneBuilder::from_world(world)
.extract_entity(e1)
.extract_entity(e2)
.extract_resources()
.build();
Becomes:
// 0.19
let world: &World = ...;
let dynamic_world = {
let type_registry = world.resource::<AppTypeRegistry>().read();
DynamicWorldBuilder::from_world(world, &type_registry)
.extract_entity(e1)
.extract_entity(e2)
.extract_resources()
.build()
};
For DynamicScene::from_scene:
// 0.18
let type_registry: AppTypeRegistry = get_from_main_world();
let mut scene: Scene = ...;
// Previously the scene world needed the type registry.
scene.world.insert_resource(type_registry);
let dynamic_scene = DynamicScene::from_scene(scene);
Becomes:
// 0.19
let type_registry: AppTypeRegistry = get_from_main_world();
let world_asset: WorldAsset = ...; // Scene was renamed to WorldAsset in 0.19
// No need to insert into the world asset!
let dynamic_world = DynamicWorld::from_world_asset(&world_asset, &type_registry.read());
Dropping Tasks in Web Builds #
Dropping a Task<T> in a web build now cancels it, call Task<T>::detach() to keep the old behavior.
Return type of Access::archetypal changed #
The return type of Access::archetypal has changed from impl Iterator to a new &ComponentIdSet type. That type does implement IntoIterator, but callers may need to call the iter() method to get an Iterator.
bevy_reflect reorganized to de-clutter the crate root #
bevy_reflect has undergone a large reorganization. Many modules have been exposed in the crate root, each containing items relevant to their "kind" of reflected type:
arrayenumslistmapsetstructstupletuple_struct
For example, the structs module now contains the Struct trait, as well as related items like DynamicStruct or StructInfo.
This change was made to de-clutter the crate root of bevy_reflect, hopefully making it easier to find what traits and types you need for your use of reflection.
Migrating should only require editing your use statements. The Rust compiler will give hints at the new type paths, should you need any assistance.
Align bevy_transform's feature flags #
bevy_transform no longer depends on bevy_log. The bevy_log feature flag has been removed.
Tracing instrumentation is now gated on the new trace feature (using tracing directly, matching bevy_ecs):
# 0.18
bevy_transform = { features = ["bevy_log"] }
# 0.19
bevy_transform = { features = ["trace"] }
Parallel transform propagation is no longer tied to the std feature. It now requires the explicit multi_threaded feature:
# 0.18 — parallel was enabled implicitly via std
bevy_transform = { features = ["std"] }
# 0.19 — opt in explicitly
bevy_transform = { features = ["std", "multi_threaded"] }
UiWidgetsPlugins and InputDispatchPlugin are now in DefaultPlugins #
UiWidgetsPlugins and InputDispatchPlugin are now part of DefaultPlugins.
These plugins are now mature enough to be included as part of the default Bevy experience.
Remove UiWidgetsPlugins if you have DefaultPlugins
// 0.18
fn main() {
App::new()
.add_plugins(DefaultPlugins, UiWidgetsPlugins)
.add_plugins((my_ambitious_game::game_plugin))
.run();
}
// 0.19
fn main() {
App::new()
.add_plugins(DefaultPlugins) // Puff!
.add_plugins((my_ambitious_game::game_plugin))
.run();
}
Remove InputDispatchPlugin if you have DefaultPlugins
// 0.18
fn main() {
App::new()
.add_plugins(DefaultPlugins, UiWidgetsPlugins, InputDispatchPlugin)
.add_plugins((my_sequel_game::game_plugin))
.run();
}
// 0.19
fn main() {
App::new()
.add_plugins(DefaultPlugins) // Puff!
.add_plugins((my_sequel_game::game_plugin))
.run();
}
Atmosphere has been moved to bevy_light #
Atmosphere, ScatteringMedium, ScatteringTerm, PhaseFunction, and Falloff have been moved from bevy_pbr to bevy_light. Atmosphere is available at bevy::light::Atmosphere, the rest are under bevy::light::atmosphere.
bevy_window, bevy_input_focus, custom_cursor features moved to alternate feature collections #
In Bevy 0.18, feature collections were introduced. The bevy_window, bevy_input_focus, & custom_cursor features were included in the default_app collection.
In Bevy 0.19, these have been moved from default_app:
| Feature | is included in... |
|---|---|
bevy_window | common_api |
bevy_input_focus | ui_api |
custom_cursor | default_platform |
This change was made because:
- the
default_appcollection is for core functionality that most apps will need. Scene definition for windowing is not usually required, and - apps that don't use windowing (ex: command line tools, servers, etc) can compile fewer dependencies.
If you were relying on these being included in default_app, you can cherry-pick them into your Cargo.toml feature list:
# 0.18
bevy = { version = "0.18", default-features = false, features = [ "default_app" ] }
# 0.19
bevy = { version = "0.19", default-features = false, features = [
"default_app",
"bevy_window",
"bevy_input_focus",
"custom_cursor"
] }
If you already depend on a high-level profile (2d, 3d, ui), or a mid-level collection ending in '_render' or '_api', then you do not need to make any changes.
extra_buffer_usages moved from MeshAllocator to MeshAllocatorSettings #
extra_buffer_usages has been moved from MeshAllocator to MeshAllocatorSettings. If you were accessing it on MeshAllocator, please do so on MeshAllocatorSettings now.
Avoiding unnecessary AssetEvent::Modified events that lead to rendering performance costs #
Assets::get_mut will now return AssetMut<A: Asset> instead of &mut Asset. Similar to Mut/ResMut, the new implementation will trigger an AssetEvent::Modified event only when the asset is actually mutated.
In some cases (like materials), triggering the AssetEvent::Modified event might lead to measurable performance costs. To avoid this, you can check if the Asset will change before mutating it:
fn update(
query: Query<MeshMaterial3d<StandardMaterial>>,
materials: ResMut<Assets<StandardMaterial>>,
time: Res<Time>,
) {
for material_handle in query.iter_mut() {
// material variable now needs to be marked as mut
let Some(mut material) = materials.get_mut(material_handle) else {
continue;
};
let new_color = compute_new_color(&time);
if material.base_color != new_color {
// material will be marked as changed and extracted down the line
// only if the color has actually changed
material.base_color = new_color;
}
}
}
Interned is now reflectable but requires additional trait bounds #
Interned<T> now requires all instances with T to implement Internable, where previously only the PartialEq, Eq, and Hash implementations required it. Implement Internable for T to fix this.
EnvironmentMapUniform is removed #
EnvironmentMapUniform has been removed. It previously stored the rotation transformation matrix of view environment maps. Now the rotation is stored as a quaternion in LightProbesUniform::view_rotation.
bevy_shader cleanups #
ShaderReflectError has been deleted, as it was unused.
ShaderCache::new now accepts a RenderDevice, and ShaderCache::get does not. This is to reflect the fact that a ShaderCache must only be used with one RenderDevice for it to be valid.
The set_import_path, with_import_path, import_path, and imports methods on Shader have been removed. Just access the fields directly, these were superfluous getter methods.
new_with_ prefix removed from TextLayout constructors #
Constructor functions for the TextLayout type were simplified:
TextLayout::new_with_justify->TextLayout::justifyTextLayout::new_with_linebreak->TextLayout::linebreakTextLayout::new_with_no_wrap->TextLayout::no_wrap
Changes to TextFont's font_size and font fields #
TextFont's font field has been changed from a Handle<Font> to a FontSource, and its font_size field was changed from an f32 to a FontSize.
FontSource has two variants: Handle, which identifies a font by asset handle, and Family, which selects a font by its family name.
FontSource implements From<Handle<Font>>. Migration of existing code should only require calling into() on the handle.
Font texture atlases are no longer automatically cleared when the font asset they were generated from is removed. Even after the asset is removed, the font is still accessible using the family name with FontSource::family. Removing the text atlases naively could cause a panic as rendering expects them to be present.
For font_size, wrap the f32 value in a FontSize::Px(...).
Concretely:
// 0.18
TextFont {
font: asset_server.load("FiraMono-medium.ttf"),
font_size: 35.,
..default()
}
becomes
// 0.19
TextFont {
font: asset_server.load("FiraMono-medium.ttf").into(),
font_size: FontSize::Px(35.),
..default()
}
experimental_ui_widgets feature is no longer experimental #
The experimental_bevy_ui_widgets feature has been renamed to bevy_ui_widgets.
The bevy_ui_widgets feature has been added to the ui feature collection (and thus bevy's default features) for ease of use.
This crate remains immature, and is subject to heavy breaking changes, even relative to Bevy's pre-1.0 standards. However, it is useful enough to see wider adoption, and this change substantially improves the user experience when setting up new projects and running Bevy examples.
Camera TextureFormat rework #
ExtractedView::hdr has been moved to ExtractedCamera::hdr. Views do not have a notion of HDR, which is a camera-specific property. TextureFormat::bevy_default() and ViewTargets::TEXTURE_FORMAT_HDR are deprecated, please source your texture format from ExtractedView::target_format instead, and plumb it through your specialization keys. Similarly, ViewTarget::is_hdr was removed. Use ExtractedCamera::hdr to check this instead.
Rodio 0.22 Update #
rodio was updated to 0.22 and cpal to 0.17. The following sections will guide you through the necessary changes to ensure compatibility.
Audio Feature Flags #
Audio format related features were reworked with this update.
By default, Bevy will enable the vorbis feature, which supports OGG/VORBIS files through lewton.
If you are not using Bevy's default features, here's a list you can use for reference:
vorbis: OGG/VORBIS audio format support (throughlewton).wav: WAV audio format support (throughhound).mp3: MP3 audio format support (throughsymphonia).mp4: MP4 audio format support (throughsymphonia). It also enables AAC support.flac: FLAC audio format support (throughclaxon).aac: AAC audio format support (throughsymphonia).
There are also specific symphonia backend flags you can use for certain formats instead of the default flags:
symphonia-flacsymphonia-vorbissymphonia-wav
Notice that OGG/VORBIS support through symphonia is currently subject to issues with buffering, reverb, looping and spatial audio. Check the following issues/PRs for additional context:
The audio-all-formats feature collection was added for convenience. It will enable bevy_audio and all the available audio formats through their default backends.
Audio Traits #
type DecoderItem was removed from the Decodable trait. Now rodio::Sample is an alias for f32.
Android Related Features #
The android_shared_stdcxx feature was removed, as cpal's oboe-shared-stdcxx feature was also removed in favor of Android NDK audio APIs.
Keep in mind that if you are using bevy_audio the minimum supported Android API version is now 26 (Android 8/Oreo).
InputFocus fields are no longer public #
The .0 field on InputFocus is no longer public. Use the getter and setter methods instead.
In 0.18:
let focused_entity = input_focus.0;
input_focus.0 = Some(entity);
input_focus.0 = None;
In 0.19:
let focused_entity = input_focus.get();
input_focus.set(entity);
input_focus.clear();
Additionally, the core setup of InputFocus and related resources now occurs in InputFocusPlugin, rather than InputDispatchPlugin. This is part of DefaultPlugins, so most users won't need to make any changes.
PlaneMeshBuilder, allow for different number of subdivisions in X and Z directions #
It is now possible to assign a different number of subdivisions on the X and Z axis.
The subdivisions field of PlaneMeshBuilder has been split into subdivisions_x and subdivisions_z.
// 0.18:
builder.subdivisions = 4
// 0.19:
builder.subdivisions(4)
EasyScreenRecordPlugin output_dir #
EasyScreenRecordPlugin has a new public field output_dir: Option<PathBuf>. If you are constructing this struct manually, you must now include the output_dir field. If you are using ..default(), no changes are needed.
// 0.18
let plugin = EasyScreenRecordPlugin {
toggle: KeyCode::Space,
preset: Preset::Medium,
tune: Tune::Animation,
frame_time: Duration::from_millis(33),
};
// 0.19
let plugin = EasyScreenRecordPlugin {
toggle: KeyCode::Space,
preset: Preset::Medium,
tune: Tune::Animation,
frame_time: Duration::from_millis(33),
output_dir: Some("recordings".into()),
};
Measure changes #
Measure::measure no longer takes a separate style: &taffy::Style parameter. Instead the taffy Style is now accessible via a new style field on MeasureArgs.
The width and height fields of MeasureArgs have been renamed to known_width and known_height, respectively.
FullscreenMaterial API changes #
FullscreenMaterial::run_in, FullscreenMaterial::run_after and FullscreenMaterial::run_before are replaced by FullscreenMaterial::schedule_configs to configure the system order.
// 0.18
impl FullscreenMaterial for FullscreenEffect {
fn fragment_shader() -> ShaderRef {
"shaders/fullscreen_effect.wgsl".into()
}
fn run_in() -> impl SystemSet {
Core3dSystems::PostProcess
}
fn run_after() -> Option<Core3dSystems> {
None
}
fn run_before() -> Option<Core3dSystems> {
None
}
}
// 0.19
impl FullscreenMaterial for FullscreenEffect {
fn fragment_shader() -> ShaderRef {
"shaders/fullscreen_effect.wgsl".into()
}
fn schedule_configs(system: ScheduleConfigs<BoxedSystem>) -> ScheduleConfigs<BoxedSystem> {
system
.in_set(Core3dSystems::PostProcess)
.before(tonemapping)
}
}
set_executor replaced ExecutorKind #
ExecutorKind has been removed. Schedules are now configured by passing an executor instance directly via Schedule::set_executor.
Schedule::set_executor_kindhas been removed. UseSchedule::set_executorinstead.Schedule::get_executor_kindhas been removed. There is no replacement; executors are no longer identified by an enum variant.SystemExecutor::kindhas been removed from the trait.SystemExecutoris now a public trait. You can implement it to provide a fully custom executor.SystemSchedule::systemsis nowpub.
// 0.18
use bevy::ecs::schedule::ExecutorKind;
schedule.set_executor_kind(ExecutorKind::SingleThreaded);
schedule.set_executor_kind(ExecutorKind::MultiThreaded);
schedule.set_executor_kind(ExecutorKind::default());
// 0.19
use bevy::ecs::schedule::{SingleThreadedExecutor, MultiThreadedExecutor, default_executor};
schedule.set_executor(SingleThreadedExecutor::new());
schedule.set_executor(MultiThreadedExecutor::new());
schedule.set_executor(default_executor());
New ComputedTextBlock::needs_rerender parameters #
ComputedTextBlock::needs_rerender takes two new boolean parameters: is_viewport_size_changed and is_rem_size_changed.
is_viewport_size_changed should be true if the local viewport size has changed this frame, and is_rem_size_changed should be true if the rem size (probably corresponding to the value of the RemSize resource) has changed this frame.
MorphWeights and MeshMorphWeights have been restructured #
Mesh morph target weights have been restructured to improve flexibility and performance. Users who manually create MeshMorphWeights or MorphWeights components may need to make changes.
In Bevy 0.18, entities with a Mesh3d component could have a MeshMorphWeights component containing morph weight values. In addition, if a parent of the mesh entity had a MorphWeights component then its values would be automatically copied to the MeshMorphWeights component - this allowed multiple meshes to share a single set of weight values.
In Bevy 0.19, MeshMorphWeights has been changed. It can now be either a set of weight values as before, or a reference to an entity containing a MorphWeights component. Referencing replaces the previous automatic copying.
// 0.18
struct MeshMorphWeights { weights: Vec<f32> }
// 0.19
enum MeshMorphWeights {
Value { weights: Vec<f32> },
Reference(Entity),
}
If you were using MeshMorphWeights on its own, then you just need to use MeshMorphWeights::Value.
If you were using MorphWeights and MeshMorphWeights and relying on the automatic copying, then you need to use MeshMorphWeights::Reference and point it to the entity with MorphWeights.
// 0.18
parent_entity.insert(MorphWeights::new(...));
mesh_entity.insert((mesh, MeshMorphWeights::new(...)));
// 0.19
parent_entity.insert(MorphWeights::new(...));
mesh_entity.insert((mesh, MeshMorphWeights::Reference(parent_entity)));
These changes improve performance due to less copying. They also add flexibility - a MeshMorphWeights component can reference a MorphWeights component on any entity, not just its parent.
As a result of these changes, MorphPlugin was no longer needed and has been removed.
ViewportNode's camera is now an Option<Entity> #
The ViewportNode's camera field is now an Option<Entity>. ViewportNodes can now exist without a valid camera.
WindowPlugin exit systems moved to Last #
bevy::window::close_when_requested, bevy::window::exit_on_all_closed and bevy::window::exit_on_primary_closed have all been moved into the Last schedule to prevent systems that run after Update and rely on windows existing from panicking on the last frame of the application.
exit_on_all_closed and exit_on_primary_closed have also been added to a new SystemSet, ExitSystems.
rand, glam & uuid updated to latest versions #
For rand/rand_core, the RngCore trait is now Rng. The Rng trait is now RngExt, update imports as needed. For the full extent of the changes to rand v0.10, consult the rand book here.
DefaultErrorHandler renamed to FallbackErrorHandler #
DefaultErrorHandler has been renamed to FallbackErrorHandler to better reflect its role as the handler of last resort when no specific error handling is performed.
A deprecated type alias is provided for one release to ease migration. To update your code:
// 0.18
world.insert_resource(DefaultErrorHandler(my_error_handler));
// 0.19
world.insert_resource(FallbackErrorHandler(my_error_handler));
The default_error_handler method has similarly been renamed to fallback_error_handler.
Some bevy_camera primitives moved to bevy_math #
bevy_camera::primitives::HalfSpace has moved to bevy_math::primitives::HalfSpace. Some parts of bevy_camera::primitives::Frustum have moved to bevy_math::primitives::ViewFrustum.
bevy_camera has some rendering primitives that can be extracted to be more generally useful. To expose them for others to use, some of these primitives and/or functionality have moved to bevy_math.
// 0.18
use bevy_camera::primitives::{Frustum, HalfSpace}
let half_spaces: [HalfSpace; 6] = ...;
let frustum_one: Frustum = Frustum {
half_spaces
};
let frustum_two: Frustum = Frustum::from_clip_from_world(...);
// 0.19
use bevy_math::primitives::{HalfSpace, ViewFrustum}
use bevy_camera::primitives::Frustum
let half_spaces: [HalfSpace; 6] = ...;
let frustum_one: Frustum = Frustum(
ViewFrustum {
half_spaces
});
let frustum_two: Frustum = Frustum(ViewFrustum::from_clip_from_world(...));
define_atomic_id now lives in bevy_utils #
bevy_render::define_atomic_id was moved out of bevy_render and into bevy_utils. If you were using bevy::render::define_atomic_id, update to bevy::utils::define_atomic_id.
World::entities_allocator is now World::entity_allocator #
World::entities_allocator() has been renamed to World::entity_allocator() to match the type returned (EntityAllocator). Likewise, World::entities_allocator_mut() has been renamed to World::entity_allocator_mut().
Transmission has been moved to bevy_pbr #
Camera3d::screen_space_specular_transmission_steps and Camera3d::screen_space_specular_transmission_quality have been pulled out into a separate component, ScreenSpaceTransmission, and put in bevy_pbr. The field names have been shortened to steps and quality.
Additionally:
ScreenSpaceTransmissionQualityhas been moved frombevy_cameratobevy_pbr.ScreenSpaceTransmissionQualityis no longer aResource.ViewTransmissionTextureandTransmissive3dhas been moved frombevy_core_pipelinestobevy_pbr.Node3d::MainTransmissivePassis now initialized byPbrPlugin.screen_space_specular_transmission_pipeline_keyhas becomeScreenSpaceTransmissionQuality::pipeline_key.
New Node::direction field #
Node has a new field direction, which can be used to set the inline axis direction used for layout. By default it uses InlineDirection::Ltr, which matches the layout behavior before this change.
Feathers widgets moving to BSN #
Going forward, BSN will be the primary means to create Feathers widgets. The old spawning functions have been renamed (button is now button_bundle), and will be removed in a future release.
Some of the BSN widgets have changed slightly:
buttonno longer automatically includesflex_grow. This was originally added due to the difficulty of overriding node styles when spawning, but in BSN that's no longer a problem.button,checkboxandradionow accept acaptionparameter which lets you specify the label directly instead of appending them viaChildren.
Hdr moved to bevy_camera #
Hdr has been moved from bevy_render to bevy_camera.
Furthermore, it is no longer extracted to the render world. If you were relying on its presence in the render world, consider using ExtractedCamera::hdr instead.
get_full_extension now returns Option<&str>. #
Previously, AssetPath::get_full_extension returned Option<String>. Now it returns Option<&str>. To keep the original behavior, change the following:
// 0.18
asset_path.get_full_extension()
To:
// 0.19
asset_path.get_full_extension().map(ToString::to_string)
Mesh pipeline key requires strip index format bits #
BaseMeshPipelineKey and Mesh2dPipelineKey now have STRIP_INDEX_FORMAT_* bits because strip_index_format will be required by wgpu and primitive restart is always enabled.
The strip index format bits in the mesh pipeline key must match the mesh index format for indexed strip topologies (For non-indexed strip topologies, the bits don't matter), and must be STRIP_INDEX_FORMAT_NONE for non-strip topologies. The from_primitive_topology method of mesh pipeline key has been changed to from_primitive_topology_and_strip_index to handle it and RenderMesh now has an index_format method.
In 0.18:
let key = MeshPipelineKey::from_primitive_topology(render_mesh.primitive_topology());
In 0.19:
let key = MeshPipelineKey::from_primitive_topology_and_strip_index(
render_mesh.primitive_topology(),
render_mesh.index_format(),
);
Core prefix removed from UI widget components #
CoreScrollbarThumbhas been renamed toScrollbarThumb.CoreScrollbarDragStatehas been renamed toScrollbarDragState.CoreSliderDragStatehas been renamed toSliderDragState.
Additionally, ScrollbarThumb nodes are now laid out after ui_layout_system by update_scrollbar_thumb. ScrollbarThumb entities do not have a Node component. The only layout options are for borders, which can be set using ScrollbarThumb's new border and border_radius fields.
Lifecycle observers include old and new archetypes #
Lifecycle observers now include information about the old and new archetypes during a change in the EntityComponentsTrigger. As all of the fields for this struct are pub, adding new ones is a breaking change.
If you were pattern matching the components field on EntityComponentsTrigger, you will need to add .. to the pattern.
// 18.0
let EntityComponentsTrigger { components } = e.trigger();
// 19.0
let EntityComponentsTrigger { components, .. } = e.trigger();
If you were constructing an EntityComponentsTrigger manually, you will need to supply values for old_archetype and new_archetype.
// 18.0
world.trigger_with(
event,
EntityComponentsTrigger {
components: &[component_a],
},
);
// 19.0
world.trigger_with(
event,
EntityComponentsTrigger {
components: &[component_a],
old_archetype: None,
new_archetype: None,
},
);
RelationshipAccessor improvements #
RelationshipAccessor now has additional fields:
allow_self_referentialthat stores the value ofRelationship::ALLOW_SELF_REFERENTIALrelationship_targetforRelationshipAccessor::RelationshipandrelationshipforRelationshipAccessor::RelationshipTargetwhich store theComponentIdof the counterpart component.
ComponentDescriptor::new_with_layout now takes Option<RelationshipAccessorInitializer> instead of Option<RelationshipAccessor>, which requires providing a way to get ComponentId of the counterpart component.
Change lists #
Previously, Bevy required rendering phases to iterate over all visible entities to determine which objects changed via ticks. This became a bottleneck, so Bevy now uses change lists instead. This change affects custom render phases; if you aren't creating your own render phases, you shouldn't have to update any code.
In the render world, the list of changed items can now be accessed in the new DirtySpecializations resource. In specialize systems, use code like the following:
// First, remove meshes that need to be respecialized, and those that were removed, from the bins.
for &main_entity in dirty_specializations
.iter_to_dequeue(view.retained_view_entity, render_visible_mesh_entities)
{
opaque_phase.remove(main_entity);
}
// Specialize new meshes.
for (render_entity, visible_entity) in dirty_specializations.iter_to_queue(
view.retained_view_entity,
render_visible_mesh_entities,
&view_pending_mesh_queues.prev_frame,
) {
...
}
In queue systems, use code like this:
// First, remove meshes that need to be respecialized, and those that were removed, from the bins.
for &main_entity in dirty_specializations
.iter_to_dequeue(view.retained_view_entity, render_visible_mesh_entities)
{
my_phase.remove(Entity::PLACEHOLDER, main_entity);
}
// Now bin new items.
for (render_entity, visible_entity) in dirty_specializations.iter_to_queue(
view.retained_view_entity,
render_visible_mesh_entities,
&view_pending_mesh_queues.prev_frame,
) {
...
}
If you need to handle the case in which a mesh might not be able to be specialized and/or queued right away because its dependencies (e.g. materials) haven't loaded yet, there's a new type PendingQueues that can help with this.
Additionally, sorted render phases now use an IndexMap instead of a Vec, so that entities can be added and removed incrementally instead of having to reconstruct the list every frame. This is incompatible with some exotic sorting algorithms that were commonly in use before (e.g. radix sort), so you may need to switch to the built-in sort_unstable method on IndexMap.
The add method on SortedRenderPhase has been split in two. The old behavior of clearing every frame is available as add_transient, while add_retained should be used with the change list system.
See examples/shader_advanced/specialized_mesh_pipeline.rs for a comprehensive example.
bevy_picking feature flag no longer includes bevy_input_focus #
The bevy/bevy_picking feature flag no longer enables bevy_input_focus picking functionality. For context, bevy_input_focus is inherently a bevy_ui related feature, allowing users to select UI elements to focus using their mouse.
Instead, this functionality is now tied to the existing bevy/ui_picking feature, which is itself part of the ui feature collection. In most cases, you should add the ui feature collection to your project if you are using bevy_ui.
If you want to enable bevy_input_focus's picking functionality, but do not want to use bevy_ui, add a separate dependency to the same version of bevy_input_focus in your project and enable the optional bevy_picking feature there.
This change means it now possible to enable bevy_picking without any assumptions about which backend in particular will be used.
PositionedGlyph's byte_index and byte_length fields have been removed #
PositionedGlyph's byte_index and byte_length fields have been removed. Unlike Cosmic Text, Parley doesn't expose these values in its GlyphRuns.
If needed, these range can be retrieved using visual_clusters by mapping each cluster's text_range to its corresponding Glyph(s). However, this approach is quite fragile.
Light gizmos have been moved from bevy_gizmos to bevy_light #
LightGizmoPlugin, LightGizmoColor, LightGizmoConfigGroup, and ShowLightGizmo have been moved from bevy_gizmos::light to bevy_light.
DataFormat renamed to TextureChannelLayout #
bevy_image::DataFormat is renamed to bevy_image::TextureChannelLayout. Replace all references and imports.
Contact Shadows #
The shadows_enabled field on PointLight, DirectionalLight, and SpotLight has changed to shadow_maps_enabled.
This was changed because these lights now support contact shadows, and have a contact_shadows_enabled field. The old shadows_enabled field only configures shadow maps, making the old name misleading.
Implementations of Reader now must implement Reader::seekable, and AsyncSeekForward is deleted. #
The Reader trait no longer requires implementing AsyncSeekForward and instead requires implementing Reader::seekable, which will cast the Reader to &mut dyn SeekableReader if it supports AsyncSeek (SeekableReader: Reader + AsyncSeek).
// If MyReader implements `AsyncSeek`
impl Reader for MyReader {
fn seekable(&mut self) -> Result<&mut dyn SeekableReader, ReaderNotSeekableError> {
Ok(self)
}
}
// If MyReader does not implement `AsyncSeek`
impl Reader for MyReader {
fn seekable(&mut self) -> Result<&mut dyn SeekableReader, ReaderNotSeekableError> {
Err(ReaderNotSeekableError)
}
}
Since we now just use the AsyncSeek trait, we've deleted the AsyncSeekForward trait. Users of this trait can migrate by calling the AsyncSeek::poll_seek method with SeekFrom::Current(offset), or the AsyncSeekExt::seek method.
AnimationTargetId algorithm changes #
The algorithm used to calculate AnimationTargetId has changed. This fixes a bug where different joint hierarchies could mistakenly be assigned the same id.
If you have serialized data containing AnimationTargetId values then these will need to be recalculated.
Bloom luma calculation now in linear space #
The luma calculation for bloom's Karis average (used for downsampling) has been corrected to use linear color space instead of non-linear sRGB space.
As a result, the intensity of the bloom effect may appear reduced, especially for colors with high saturation or those that were significantly affected by the previous non-linear calculation.
If your scene's bloom now appears too dim, you can:
- Increase the
intensityfield on theBloomcomponent. - Increase the
emissivestrength of your materials. - Adjust the
prefiltersettings in theBloomcomponent.
Render Graph as Systems #
The RenderGraph API has been removed. Render passes are now systems that run in Core3d or Core2d schedules.
In 0.18:
impl ViewNode for MyNode {
type ViewQuery = (&'static ExtractedCamera, &'static ViewTarget);
fn run<'w>(
&self,
_graph: &mut RenderGraphContext,
render_context: &mut RenderContext<'w>,
(camera, target): QueryItem<'w, Self::ViewQuery>,
world: &'w World,
) -> Result<(), NodeRunError> {
// ...
}
}
render_app
.add_render_graph_node::<ViewNodeRunner<MyNode>>(Core3d, MyLabel)
.add_render_graph_edges(Core3d, (Node3d::Foo, MyLabel, Node3d::Bar));
In 0.19:
pub fn my_render_pass(
world: &World,
view: ViewQuery<(&ExtractedCamera, &ViewTarget)>,
mut ctx: RenderContext,
) {
let (camera, target) = view.into_inner();
// ...
}
render_app.add_systems(
Core3d,
my_render_pass
.after(foo_pass)
.before(bar_pass)
.in_set(Core3dSystems::MainPass),
);
The ViewNode trait is replaced by a regular system using the ViewQuery parameter. RenderContext is now a system parameter instead of being passed as &mut. Use .before() / .after() with the actual system functions (e.g., main_opaque_pass_3d) rather than Node3d labels.
System sets Core3dSystems::Prepass, MainPass, and PostProcess are available for coarse ordering. The RenderGraph schedule, available as bevy::render::renderer::RenderGraph, remains as the top-level schedule for non-camera rendering.
DespawnOnEnter / DespawnOnExit can now trigger during same state transitions #
In bevy_state, you can define states and transition between them. For example, this can be used to transition between AppState::Menu and AppState::InGame, and run different logic depending on the state the App is in. In 0.18, it became possible to transition from a state to itself: A 'same state transition'. However, there was a bug that made it so that DespawnOnEnter and DespawnOnExit did not trigger for same state transition when they should have.
In 0.19, this bug is fixed. If your application transitions between states using NextState::set(), your application will trigger DespawnOnEnter and DespawnOnExit, even in same state transitions.
If this is undesired, use NextState::set_if_neq() to transition between states. set_if_neq() does not run any state transition schedules if the target state is the same as the current one.
Lifecycle event changes #
Replace has been renamed to Discard and ComponentHooks::on_replace has been renamed to ComponentHooks::on_discard. The #[component(on_replace = ...)] derive attribute is now #[component(on_discard = ...)]. Replace all references and imports.
ui feature is now no longer implied by the 3d or 2d features #
Swapping the UI framework for your Bevy project is a common form of customization. We think that users should be able to do this easily, without having to give up the ease of use (and updates!) that come with our top-level feature collections.
To achieve this, the ui feature collection is now no longer implied by the 3d or 2d feature collection.
To migrate:
- If you used all default features before, nothing changes for you.
- If you want to opt out of using
bevy_ui, it is now as simple as disabling default features, and manually opting into3dor2d(and optionallyaudio). - If you already opted into non-default features and want to continue using
bevy_ui, you will now have to add theuifeature.
Skybox moved to bevy_light #
Skybox has been moved from bevy_core_pipelines to bevy_light.
Command error handling has been simplified #
The Command trait now takes Out as an associated type rather than as a generic parameter. For function-style commands that return a Result, the code changes as follows:
// 0.18
fn my_command() -> impl Command<Result> {
move |world: &mut World| -> Result {
// ...
}
}
// 0.19
fn my_command() -> impl Command {
move |world: &mut World| -> Result {
// ...
}
}
Implementors of the Command trait must now fill in the Out associated type:
// 0.18
impl Command for Foo {
fn apply(self, world: &mut World) {
// ...
}
}
// 0.19
impl Command for Foo {
type Out = ();
fn apply(self, world: &mut World) {
// ...
}
}
For commands that return Result:
// 0.18
impl Command<Result> for Foo {
fn apply(self, world: &mut World) -> Result {
// ...
}
}
// 0.19
impl Command for Foo {
type Out = Result;
fn apply(self, world: &mut World) -> Result {
// ...
}
}
The functionality of the HandleError and CommandWithEntity traits have been folded into Command and EntityCommand, respectively. Use the latter traits as the sole trait bound, if needed.
Added mouse panning to PanCamera component #
By default PanCamera now includes mouse panning.
To go back to the keyboard only panning, you need to set the enabled field of the PanCamera's mouse_pan_settings field to false.
The validate_parent_has_component is superseded by ValidateParentHasComponentPlugin #
The validate_parent_has_component insert hook has been replaced by a plugin: ValidateParentHasComponentPlugin. This uses an observer, a resource, and a system to achieve a more robust (and less spurious) warning for invalid configuration of entities.
SystemParam validation is now done when fetching the data #
In an effort to improve performance by reducing redundant data fetches and simplifying internals, system parameter validation is now done as part of fetching the data for those system parameters. To be more precise:
SystemParam::get_paramnow returns aResult<Self::Item<'world, 'state>, SystemParamValidationError>, instead of simply aSelf::Item<'world, 'state>.- If validation fails, an appropriate
SystemParamValidationErrorshould be returned. - If validation passes, the item should be returned wrapped in
Ok.
- If validation fails, an appropriate
SystemParam::validate_paramhas been removed.- All logic that was done in this method should be moved to the
get_parammethod of that type.
- All logic that was done in this method should be moved to the
SystemState::validate_paramhas been removed.- Validation now happens automatically when calling
get,get_mut, orget_unchecked.
- Validation now happens automatically when calling
SystemState::fetch,get_unchecked,getandget_mutnow return aResult<..., SystemParamValidationError>. Callers that previously destructured the result directly will need to add.unwrap()or handle theResult:
// 0.18
let (res, query) = system_state.get(&world);
// 0.19
let (res, query) = system_state.get(&world).unwrap();
When executing systems, we no longer check for system validation before running the systems. As a result of these changes, System::validate_param and System::validate_param_unsafe have been removed. Instead, validation has been moved to be part of the trait implementation for System::run_unsafe. All implementations of the System trait should validate that their parameters are valid during this method, bubbling up any errors originating in SystemParam::get_param.
Custom SystemParam implementations #
If you have a custom SystemParam implementation, you need to:
- Remove the
validate_parammethod. - Move any validation logic into
get_param. - Change
get_paramto returnResult<Self::Item<'world, 'state>, SystemParamValidationError>.
// 0.18
unsafe impl SystemParam for MyParam<'_> {
// ...
unsafe fn validate_param(
state: &Self::State,
system_meta: &SystemMeta,
world: UnsafeWorldCell,
) -> Result<(), SystemParamValidationError> {
// validation logic
if !is_valid(state, world) {
return Err(SystemParamValidationError::invalid::<Self>("not valid"));
}
Ok(())
}
unsafe fn get_param<'w, 's>(
state: &'s mut Self::State,
system_meta: &SystemMeta,
world: UnsafeWorldCell<'w>,
change_tick: Tick,
) -> Self::Item<'w, 's> {
// fetch logic
MyParam { /* ... */ }
}
}
// 0.19
unsafe impl SystemParam for MyParam<'_> {
// ...
unsafe fn get_param<'w, 's>(
state: &'s mut Self::State,
system_meta: &SystemMeta,
world: UnsafeWorldCell<'w>,
change_tick: Tick,
) -> Result<Self::Item<'w, 's>, SystemParamValidationError> {
// validation logic merged into get_param
if !is_valid(state, world) {
return Err(SystemParamValidationError::invalid::<Self>("not valid"));
}
// fetch logic
Ok(MyParam { /* ... */ })
}
}
Custom ExclusiveSystemParam implementations #
Similarly, ExclusiveSystemParam::get_param now returns a Result<Self::Item<'s>, SystemParamValidationError> instead of Self::Item<'s>. Existing implementations should wrap their return value in Ok(...) and return an appropriate SystemParamValidationError if validation fails.
// 0.18
impl ExclusiveSystemParam for MyExclusiveParam {
// ...
fn get_param<'s>(
state: &'s mut Self::State,
system_meta: &SystemMeta,
) -> Self::Item<'s> {
MyExclusiveParam { /* ... */ }
}
}
// 0.19
impl ExclusiveSystemParam for MyExclusiveParam {
// ...
fn get_param<'s>(
state: &'s mut Self::State,
system_meta: &SystemMeta,
) -> Result<Self::Item<'s>, SystemParamValidationError> {
Ok(MyExclusiveParam { /* ... */ })
}
}
Custom System implementations #
If you have a custom System implementation, remove the validate_param_unsafe method. Parameter validation should now occur inside run_unsafe by propagating errors from SystemParam::get_param.
MultithreadedExecutor performance changes #
For the parallel MultithreadedExecutor, validation was previously done as a cheap pre-validation step, while checking run conditions. Now, tasks will be spawned for systems which would fail or are skipped during validation.
In most cases, avoiding the extra overhead of looking up the required data twice should dominate. However, this change may negatively affect systems which are frequently skipped (e.g. due to Single). If you find that this is a significant performance overhead for your use case, the previous behavior can be recovered by adding run conditions.
ExtractComponent refactor #
Previously, SyncComponentPlugin/ExtractComponentPlugin would despawn the render entity if the synced component was removed. This removes all the derived components too. In 0.19, the render entity is no longer despawned and only the Target components of the SyncComponent trait are removed.
SyncComponent is a subtrait of ExtractComponent and you must implement it to clean up extracted and derived components.
impl SyncComponent for MyComponent {
type Target = (Self, OtherDerivedComponents);
}
impl ExtractComponent for MyComponent {
type QueryData = ();
type QueryFilter = ();
type Out = Self;
fn extract_component(
item: QueryItem<'_, '_, Self::QueryData>,
) -> Option<Self::Out> {
Some(*item)
}
}
You can also specify the sync target (default to Self) using extract_component_sync_target attribute in derive macros.
#[derive(Component, ExtractComponent)]
#[extract_component_sync_target((Self, OtherDerivedComponents))]
struct MyComponent;
Both SyncComponent and ExtractComponent have also gotten an optional marker type that can be used to bypass orphan rules, see the docs for details.
Nested query access #
Queries are now able to access data from multiple entities in the same query item. This will be used to support richer querying across relations, such as by querying components from an entity's parent.
However, some query operations are not sound for queries that access multiple entities, and need additional trait bounds to ensure they are only used soundly.
An IterQueryData bound has been added to iteration methods on Query:
into_iteriter_many_unique_mut/iter_many_unique_unsafe/iter_many_unique_innerget_many_mut/get_many_inner/get_many_unique_mut/get_many_unique_innerpar_iter_mut/par_iter_inner/par_iter_many_unique_mutsingle_mut/single_inneriter_combinations_mut/iter_combinations_inner/iter_combinations_unsafe
iter_mut, iter_unsafe, and iter_inner may be called with non-iterable data, but the resulting QueryIter will not impl Iterator. It may be iterated using streaming or lending iteration by calling the new fetch_next method.
iter, iter_many, par_iter, single, and iter_combinations have no extra bounds, since read-only queries are always sound to iterate. iter_many_mut and iter_many_inner methods have no extra bounds, either, since they already prohibit concurrent access to multiple entities.
In addition, a SingleEntityQueryData bound has been added to
- The
EntityRef::get_componentsfamily of methods - The
Traversaltrait - The
Query::transmuteandQuery::joinfamilies of methods - The
QueryIter::sortfamily of methods
All existing query types will satisfy those bounds, but generic code may need to add bounds.
// 0.18
fn generic_func<D: QueryData>(query: Query<D>) {
for item in &mut query { ... }
}
// 0.19
fn generic_func<D: IterQueryData>(query: Query<D>) {
for item in &mut query { ... }
}
// 0.19, but with support for non-iterable query types
fn generic_func<D: QueryData>(mut query: Query<D>) {
let mut iter = query.iter_mut();
while let Some(item) = iter.fetch_next() { ... }
}
Conversely, manual implementations of QueryData may want to implement IterQueryData and SingleEntityQueryData if appropriate.
Finally, two new methods have been added to WorldQuery: init_nested_access and update_archetypes. Manual implementations of WorldQuery should implement those methods as appropriate. Queries that only access the current entity may leave them empty, but queries that delegate to other implementations, especially generic ones, should delegate the new methods as well.
StaticTransformOptimizations no longer stores a threshold for dynamic toggling #
The threshold has been removed completely from StaticTransformOptimizations: the optimization is always either enabled or disabled. As a result this is now a simple enum, and some method calls will need to be updated.
If you want to toggle this dynamically, you can count the entities in a system and dynamically enable or disable this. Performing this check can be slow however, so you probably should not perform this check each frame.
Mesh view bind group layout is changed #
MeshPipelineViewLayouts no longer stores all possible view layouts. Now it only stores necessary parameters for creating bind group layouts on demand. And MeshPipelineViewLayouts::get_view_layout returns MeshPipelineViewLayout by value instead of by reference.
generate_view_layouts is removed and layout_entries is private now. Please use MeshPipelineViewLayouts::get_view_layout.
Mesh view bind group layout has more variants now and some dynamic uniforms such as distance fog, ssr, contact shadows, and environment map are not guaranteed to exist. Please use MeshViewBindGroup::main_offsets to get the dynamic offsets.
In 0.18:
let mut offsets: SmallVec<[u32; 8]> = smallvec![
view_uniform_offset.offset,
view_lights_offset.offset,
view_fog_offset.offset,
**view_light_probes_offset,
**view_ssr_offset,
**view_contact_shadows_offset,
**view_environment_map_offset,
];
if let Some(oit_settings_offset) = maybe_oit_settings_offset {
offsets.push(oit_settings_offset.offset);
}
pass.set_bind_group(I, &mesh_view_bind_group.main, &offsets);
In 0.19:
pass.set_bind_group(
I,
&mesh_view_bind_group.main,
&mesh_view_bind_group.main_offsets,
);
Occlusion culling is no longer experimental #
Occlusion culling is no longer experimental, as all known issues that caused Bevy to cull meshes incorrectly are fixed. Consequently, the bevy::render::experimental::occlusion_culling module has been renamed to simply bevy::render::occlusion_culling.
Morph targets are now stored in meshes. #
Previously, morph targets were stored as a Handle<Image> in a Mesh. Now, morph targets are stored inside the Mesh itself.
As a consequence, Gltf assets no longer provide a GltfAssetLabel::MorphTarget subasset. This subasset can be replaced with the corresponding GltfAssetLabel::Primitive to look up the correct Mesh, followed by Mesh::get_morph_targets.
Advanced AssetServer load variants are now exposed through a builder pattern. #
In previous versions of Bevy, there were many different ways to load an asset:
AssetServer::loadAssetServer::load_acquireAssetServer::load_untypedAssetServer::load_acquire_override_with_settings- etc.
All these variants have been simplified to only two variants:
AssetServer::load(): This is a convenience method and just calls the load builder internally.AssetServer::load_builder(): allows for constructing more complex loads like untyped loads, loads including guards, loads with settings, etc.
Every load variant above can be reimplemented using load_builder, and each one of these methods has deprecation messages on them explaining their new equivalent. For example, load_with_settings_override can now be replaced with:
asset_server
.load_builder()
.with_settings(settings)
.override_unapproved()
.load(path)
NestedLoader #
To match this change, NestedLoader has been replaced with NestedLoadBuilder. Similarly, LoadContext::loader has been replaced with LoadContext::load_builder. The various calls have now been simplified:
context.loader().load(path)->context.load_builder().load(path)context.loader().with_dynamic_type(type_id).load(path)->context.load_builder().load_erased(type_id, path)context.loader().with_unknown_type().load(path)->context.load_builder().load_untyped(path)context.loader().immediate().load(path)->context.load_builder().load_value(path)context.loader().immediate().with_dynamic_type(type_id).load(path)->context.load_builder().load_erased_value(type_id, path)context.loader().immediate().with_unknown_type().load(path)->context.load_builder().load_untyped_value(path)context.loader().immediate().with_reader(reader).load(path)->context.load_builder().load_value_from_reader(path, reader)context.loader().immediate().with_reader(reader).with_dynamic_type(type_id).load(path)->context.load_builder().load_erased_value_from_reader(type_id, path, reader)context.loader().immediate().with_reader(reader).with_unknown_type().load(path)->context.load_builder().load_untyped_value_from_reader(path, reader)
PipelineCacheError renamed to ShaderCacheError #
PipelineCacheError has been renamed to ShaderCacheError and the ProcessShaderError variant has been Boxed.
ViewTarget's output accessors now return Option #
ViewTarget::out_texture, out_texture_color_attachment, and out_texture_view_format now return an Option. They are None when the render target has no output surface this frame, e.g. an occluded swap chain or a camera with CameraOutputMode::Skip. Nodes that blit to the output should short-circuit when given None.
SystemBuffer requires queue() to be implemented #
SystemBuffer now requires queue() to be implemented, instead of apply(). apply()'s default implementation now delegates to queue().
This is to ensure that a SystemBuffer used in an observer context applies its changes. In most cases, if apply() does not change the World structurally, apply() and queue() can mutate the World directly in the same way.
If apply() does not change the World structurally, apply() should be changed to queue():
// 0.18
impl SystemBuffer for MySystemBuffer {
fn apply(&mut self, system_meta: &SystemMeta, world: &mut World) {
// your impl here
}
}
// 0.19
impl SystemBuffer for MySystemBuffer {
fn queue(&mut self, system_meta: &SystemMeta, mut world: DeferredWorld) {
// your impl here, using a DeferredWorld instead
}
}
If apply() does change the World structurally, implement both apply() and queue(). To queue structural changes to a DeferredWorld, add the structural changes to its command queue, accessible via world.commands().
FontAtlas changes #
The texture atlas layout for font atlases is no longer stored as a separate asset. Instead, it is stored directly in the texture_atlas field of FontAtlas.
The TextureAtlasLayout parameters of FontAtlas's new and add_glyph_to_atlas methods have been removed.
FontAtlas::add_glyph's offset parameter has been changed from an IVec2 to a Vec2
GlyphAtlasInfo's texture_atlas and location fields have been removed, replaced by rect and offset fields.
The size field has been removed from PositionedGlyph. The glyph's size can now be obtained from the Rect stored in the atlas_info: GlyphAtlasInfo field.
GlyphAtlasLocation::offset is now a Vec2.
ComputedNode::stack_index has been replaced by ComputedStackIndex #
The stack_index field has been removed from ComputedNode. Instead the stack index for each UI node is stored on a specialized component ComputedStackIndex, which is required by ComputedNode.
Rename FeathersPlugin to FeathersCorePlugin #
FeathersPlugin has been renamed to FeathersCorePlugin to reduce confusion with FeathersPlugins, the PluginGroup.
Remove android game activity from default #
Bevy previously had android-game-activity as part of its default features. Users that wanted to use android-native-activity instead, had to disable default-features and define all features plus android-native-activity explicitly.
Both options are no longer part of default-features, but they need to be added explicitly.
For apps using GameActivity you need to add the android-game-activity feature to your Cargo.toml:
bevy = { version = "0.19", features = ["android-game-activity"] }
For apps using NativeActivity you no longer have to define all features explicitly, you can simply use:
bevy = { version = "0.19", features = ["android-native-activity"] }
SavedAsset now contains two lifetimes, and AssetSaver now takes an AssetPath. #
SavedAsset now holds two lifetimes instead of one. This is primarily used in the context of AssetSaver. AssetSaver also now takes an AssetPath. So previously, users may have:
// 0.18
impl AssetSaver for MySaver {
type Asset = MyAsset;
type Settings = ();
type OutputLoader = MyLoader;
type Error = std::io::Error;
async fn save(
&self,
writer: &mut Writer,
asset: SavedAsset<'_, Self::Asset>,
settings: &Self::Settings,
) -> Result<(), Self::Error> {
todo!()
}
}
Now with the extra SavedAsset lifetime, and the extra AssetPath, we have:
// 0.19
impl AssetSaver for MySaver {
type Asset = MyAsset;
type Settings = ();
type OutputLoader = MyLoader;
type Error = std::io::Error;
async fn save(
&self,
writer: &mut Writer,
asset: SavedAsset<'_, '_, Self::Asset>,
settings: &Self::Settings,
asset_path: AssetPath<'_>,
) -> Result<(), Self::Error> {
todo!()
}
}
In practice, this should not have an impact on usage.
ShaderStorageBuffer renamed to ShaderBuffer #
ShaderStorageBuffer has been renamed to ShaderBuffer and GpuShaderStorageBuffer has been renamed to GpuShaderBuffer. Update your imports and type references accordingly.
bevy_text migration from Cosmic Text to Parley #
bevy_text now uses Parley for its text layout. For the most part, this change should be invisible to users of bevy_text and Bevy more broadly.
However, some low-level public methods and types (such as FontAtlasKey) have changed to map to parley's distinct API.
This migration should be relatively straightforward. Use the linked PR as an example of the correct migration, but please ask for help (and explain your use case) if you run into difficulties not noted below.
Migration steps:
- System font discovery now requires you to enable the
bevy/system_font_discoveryfeature. Users on Linux will need thefontconfiglibrary for this. On Ubuntu, this can be done usingsudo apt install libfontconfig1-dev. - The various methods for setting the fallback font (such as
set_serif_family,set_sans_serif_familyorset_monospace_family) now return aResult. These will fail if the provided font is not found. By-and-large, you should not need to call these methods: font fallback is handled automatically viafontiquethroughparley, using the system-provided fallback fonts (but see the above note about system font discovery).