- 0.12 to 0.13
- Support all types of animation interpolation from gltf
- ReadAssetBytesError::Io exposes failing path
- Ensure consistency between Un/Typed AssetId and Handle
- Use impl Into<A> for Assets::add
- GLTF extension support
- Allow TextureAtlasBuilder in AssetLoader
- Remove the ability to ignore global volume
- Optional override for global spatial scale
- Add support for updating the tracing subscriber in LogPlugin
- Replace DiagnosticId by DiagnosticPath
- Use EntityHashMap for EntityMapper
- Update Event send methods to return EventId
- Optimise Entity with repr align & manual PartialOrd/Ord
- Split WorldQuery into QueryData and QueryFilter
- Reduced TableRow as Casting
- Allow the editing of startup schedules
- Auto insert sync points
- Add insert_state to App.
- Rename ArchetypeEntity::entity into ArchetypeEntity::id
- Restore support for running fn EntityCommands on entities that might be despawned
- Simplify conditions
- refactor: Simplify lifetimes for Commands and related types
- Deprecated Various Component Methods from Query and QueryState
- System::type_id Consistency
- System Stepping implemented as Resource
- Make the MapEntities trait generic over Mappers, and add a simpler EntityMapper
- Async channel v2
- Add First/Pre/Post/Last schedules to the Fixed timestep
- Move EntityHash related types into bevy_ecs
- Move Circle Gizmos to Their Own File
- move gizmo arcs to their own file
- Multiple Configurations for Gizmos
- Use Direction3d for gizmos.circle normal
- Rename "AddChild" to "PushChild"
- Rename Input to ButtonInput
- Add window entity to TouchInput events
- Add delta to CursorMoved event
- Remove Default impl for CubicCurve
- Direction: Rename from_normalized to new_unchecked
- Add Capsule2d primitive
- Rename RayTest to RayCast
- Use IntersectsVolume for breakout example collisions
- Add ReflectFromWorld and replace the FromWorld requirement on ReflectComponent and ReflectBundle with FromReflect
- Remove TypeUuid
- Explicit color conversion methods
- Add a depth_bias to Material2d
- Bind group layout entries
- Swap material and mesh bind groups
- light renderlayers
- Update to wgpu 0.18
- Keep track of when a texture is first cleared
- Approximate indirect specular occlusion
- Texture Atlas rework
- Exposure settings (adopted)
- Make DynamicUniformBuffer::push accept an &T instead of T
- Customizable camera main texture usage
- optimize batch_and_prepare_render_phase
- Update to wgpu 0.19 and raw-window-handle 0.6
- RenderGraph Labelization
- Gate diffuse and specular transmission behind shader defs
- Async pipeline compilation
- Mesh insert indices
- wait for render app when main world is dropped
- Deprecate shapes in bevy_render::mesh::shape
- Multithreaded render command encoding
- Stop extracting mesh entities to the render world.
- New Exposure and Lighting Defaults (and calibrate examples)
- Unload render assets from RAM
- Split Ray into Ray2d and Ray3d and simplify plane construction
- Introduce AspectRatio struct
- Include UI node size in the vertex inputs for UiMaterial.
- Optional ImageScaleMode
- Re-export futures_lite in bevy_tasks
- Rename TextAlignment to JustifyText.
- Rename Time::<Fixed>::overstep_percentage() and Time::<Fixed>::overstep_percentage_f64()
- Rename Timer::{percent,percent_left} to Timer::{fraction,fraction_remaining}
- return Direction3d from Transform::up and friends
- Make clipped areas of UI nodes non-interactive
- Create serialize feature for bevy_ui
- Camera-driven UI
- Change Window scale factor to f32 (adopted)
- Update winit dependency to 0.29
- Remove CanvasParentResizePlugin
- Cleanup bevy winit
- Add name to bevy::window::Window
- delete methods deprecated in 0.12
- Renamed Accessibility plugin to AccessKitPlugin in bevy_winit
- Use TypeIdMap whenever possible
- bevy_render: use the non-send marker from bevy_core
Migration Guide: 0.12 to 0.13
Bevy relies heavily on improvements in the Rust language and compiler. As a result, the Minimum Supported Rust Version (MSRV) is "the latest stable release" of Rust.
Support all types of animation interpolation from gltf #
When manually specifying an animation VariableCurve, the interpolation type must be specified:
// 0.12
VariableCurve {
keyframe_timestamps: vec![0.0, 1.0, 2.0, 3.0, 4.0],
keyframes: Keyframes::Rotation(vec![
Quat::IDENTITY,
Quat::from_axis_angle(Vec3::Y, PI / 2.),
Quat::from_axis_angle(Vec3::Y, PI / 2. * 2.),
Quat::from_axis_angle(Vec3::Y, PI / 2. * 3.),
Quat::IDENTITY,
]),
},
// 0.13
VariableCurve {
keyframe_timestamps: vec![0.0, 1.0, 2.0, 3.0, 4.0],
keyframes: Keyframes::Rotation(vec![
Quat::IDENTITY,
Quat::from_axis_angle(Vec3::Y, PI / 2.),
Quat::from_axis_angle(Vec3::Y, PI / 2. * 2.),
Quat::from_axis_angle(Vec3::Y, PI / 2. * 3.),
Quat::IDENTITY,
]),
interpolation: Interpolation::Linear,
},
ReadAssetBytesError::Io exposes failing path #
The ReadAssetBytesError::Io variant now contains two named fields instead of converting from std::io::Error.
path: The requested (failing) path (PathBuf)source: The sourcestd::io::Error
Ensure consistency between Un/Typed AssetId and Handle #
If you relied on any of the panicking From<Untyped...> implementations, simply call the existing typed methods instead. Alternatively, use the new TryFrom implementation instead to directly expose possible mistakes.
Use impl Into<A> for Assets::add #
Some into calls that worked previously might now be broken because of the new trait bounds. You need to either remove into or perform the conversion explicitly with from:
// 0.12
let mesh_handle = meshes.add(shape::Cube { size: 1.0 }.into()),
// 0.13
let mesh_handle = meshes.add(shape::Cube { size: 1.0 }),
let mesh_handle = meshes.add(Mesh::from(shape::Cube { size: 1.0 })),
GLTF extension support #
This will have issues with “asset migrations”, as there is currently no way for .meta files to be migrated. Attempting to migrate .meta files without the new flag will yield the following error:
bevy_asset::server: Failed to deserialize meta for asset test_platform.gltf: Failed to deserialize asset meta: SpannedError { code: MissingStructField { field: "include_source", outer: Some("GltfLoaderSettings") }, position: Position { line: 9, col: 9 } }
This means users who want to migrate their .meta files will have to add the include_source: true, setting to their meta files by hand.
Allow TextureAtlasBuilder in AssetLoader #
- For
add_textureyou need to wrap yourAssetIdinSome finishnow returns the atlas texture image directly instead of a handle. Provide the atlas texture toaddon Assetsto get a Handle Remove the ability to ignore global volume #
AudioThe option to ignore the global volume using
Volume::Absolutehas been removed andVolumenow stores the volume level directly, removing the need for theVolumeLevelstruct.Volume::new_absoluteandVolume::new_relativewere removed. UseVolume::new(0.5).Optional override for global spatial scale #
AudioAudioPlugin::spatial_scalehas been renamed todefault_spatial_scaleand the default spatial scale can now be overridden on individual audio sources withPlaybackSettings::spatial_scale.If you were modifying or reading
SpatialScaleat run time, useDefaultSpatialScaleinstead.// 0.12 app.add_plugins(DefaultPlugins.set(AudioPlugin { spatial_scale: SpatialScale::new(AUDIO_SCALE), ..default() })); // 0.13 app.add_plugins(DefaultPlugins.set(AudioPlugin { default_spatial_scale: SpatialScale::new(AUDIO_SCALE), ..default() }));Add support for updating the tracing subscriber in LogPlugin #
DiagnosticsLogPluginhas a new optionalupdate_subscriberfield. UseNoneor..default()to match previous behavior.Replace
DiagnosticIdbyDiagnosticPath#Diagnostics- const UNIQUE_DIAG_ID: DiagnosticId = DiagnosticId::from_u128(42); + const UNIQUE_DIAG_PATH: DiagnosticPath = DiagnosticPath::const_new("foo/bar"); - Diagnostic::new(UNIQUE_DIAG_ID, "example", 10) + Diagnostic::new(UNIQUE_DIAG_PATH).with_max_history_length(10) - diagnostics.add_measurement(UNIQUE_DIAG_ID, || 42); + diagnostics.add_measurement(&UNIQUE_DIAG_ID, || 42);Use
EntityHashMapforEntityMapper#ECSIf you are using the following types, update their listed methods to use the new
EntityHashMap.EntityHashMaphas the same methods as the normalHashMap, so you just need to replace the name.EntityMapperget_mapget_mut_mapnewworld_scope
ReflectMapEntitiesmap_all_entitiesmap_entitieswrite_to_world
InstanceInfoentity_map- This is a property, not a method.
Update
Eventsend methods to returnEventId#ECSsend/send_default/send_batchFor the following methods:
Events::sendEvents::send_defaultEvents::send_batchEventWriter::sendEventWriter::send_defaultEventWriter::send_batchWorld::send_eventWorld::send_event_defaultWorld::send_event_batch
Ensure calls to these methods either handle the returned value, or suppress the result with
;.// 0.12 fn send_my_event(mut events: EventWriter<MyEvent>) { events.send_default() } // 0.13 fn send_my_event(mut events: EventWriter<MyEvent>) { events.send_default(); }This will most likely be noticed within
matchstatements:// 0.12 match is_pressed { true => events.send(PlayerAction::Fire), // ^--^ No longer returns () false => {} } // 0.13 match is_pressed { true => { events.send(PlayerAction::Fire); }, false => {} }Optimise
Entitywith repr align & manualPartialOrd/Ord#ECSAny
unsafecode relying on field ordering ofEntityor sufficiently cursed shenanigans should change to reflect the different internal representation and alignment requirements ofEntity.Split WorldQuery into QueryData and QueryFilter #
ECSCheck #9918 and #10799 for more information.
- Rename the following trait type usages:
- Trait’s
ExtractComponenttypeQuerytoData. - Trait’s
GetBatchDatatypeQuerytoData. - Trait’s
ExtractInstancetypeQuerytoData.
- Trait’s
- Rename
ReadOnlyWorldQuerytoQueryFilterandWorldQuerytoQueryData - You'll need to update your related derives
// 0.12 #[derive(WorldQuery)] #[world_query(mutable, derive(Debug))] struct CustomQuery { entity: Entity, a: &'static mut ComponentA } #[derive(WorldQuery)] struct QueryFilter { _c: With<ComponentC> } // 0.13 #[derive(QueryData)] #[query_data(mutable, derive(Debug))] struct CustomQuery { entity: Entity, a: &'static mut ComponentA, } #[derive(QueryFilter)] struct QueryFilter { _c: With<ComponentC> }- Replace
Option<With<T>>withHas<T>
// 0.12 fn my_system(query: Query<(Entity, Option<With<ComponentA>>)>) { for (entity, has_a_option) in query.iter(){ let has_a:bool = has_a_option.is_some(); //todo!() } } // 0.13 fn my_system(query: Query<(Entity, Has<ComponentA>)>) { for (entity, has_a) in query.iter(){ //todo!() } }- Fix queries which had filters in the data position or vice versa.
// 0.12 fn my_system(query: Query<(Entity, With<ComponentA>)>) { for (entity, _) in query.iter(){ //todo!() } } // 0.13 fn my_system(query: Query<Entity, With<ComponentA>>) { for entity in query.iter(){ //todo!() } } // 0.12 fn my_system(query: Query<AnyOf<(&ComponentA, With<ComponentB>)>>) { for (entity, _) in query.iter(){ //todo!() } } // 0.13 fn my_system(query: Query<Option<&ComponentA>, Or<(With<ComponentA>, With<ComponentB>)>>) { for entity in query.iter(){ //todo!() } }Reduced
TableRowasCasting #ECSTableRow::new->TableRow::from_usizeTableRow::index->TableRow::as_usizeTableId::new->TableId::from_usizeTableId::index->TableId::as_usize
Allow the editing of startup schedules #
ECS- Added a new field to
MainScheduleOrder,startup_labels, for editing the startup schedule order.
Auto insert sync points #
ECSapply_deferredpoints are added automatically when there is ordering relationship with a system that has deferred parameters likeCommands. If you want to opt out of this you can switch fromafter,before, andchainto the correspondingignore_deferredAPI,after_ignore_deferred,before_ignore_deferredorchain_ignore_deferredfor your system/set ordering.- You can also set
ScheduleBuildSettings::auto_insert_sync_pointstofalseif you want to do it for the whole schedule. Note that in this mode you can still addapply_deferredpoints manually. - For most manual insertions of
apply_deferredyou should remove them as they cannot be merged with the automatically inserted points and might reduce parallelizability of the system graph. - If you were manually deriving
SystemParam, you will need to addsystem_meta.set_has_deferredif you useSystemParam::applyand want sync points auto inserted after use of yourSystemParam.
Add insert_state to App. #
ECSRenamed
App::add_statetoinit_state.Rename
ArchetypeEntity::entityintoArchetypeEntity::id#ECSThe method
ArchetypeEntity::entityhas been renamed toArchetypeEntity::idRestore support for running
fnEntityCommandson entities that might be despawned #ECSAll
Commandtypes inbevy_ecs, such asSpawn,SpawnBatch,Insert, etc., have been made private. Use the equivalent methods onCommandsorEntityCommandsinstead.If you were working with
ChildBuilder, recreate these commands using a closure. For example, you might migrate a Command to insert components like:// 0.12 parent.add_command(Insert { entity: ent_text, bundle: Capitalizable, }); // 0.13 parent.add_command(move |world: &mut World| { world.entity_mut(ent_text).insert(Capitalizable); });Simplify conditions #
ECSSome common run conditions that were previously closures and needed to be called are now just systems. Remove the parentheses.
resource_exists<T>()->resource_exists<T>resource_added<T>()->resource_added<T>resource_changed<T>()->resource_changed<T>resource_exists_and_changed<T>()->resource_exists_and_changed<T>state_exists<S: States>()->state_exists<S: States>state_changed<S: States>()->state_changed<S: States>any_with_component<T: Component>()->any_with_component<T: Component>
refactor: Simplify lifetimes for
Commandsand related types #ECSThe lifetimes for
EntityCommandshave been simplified.// 0.12 (Bevy 0.12) struct MyStruct<'w, 's, 'a> { commands: EntityCommands<'w, 's, 'a>, } // 0.13 (Bevy 0.13) struct MyStruct<'a> { commands: EntityCommands<'a>, }The method
EntityCommands::commandsnow returnsCommandsrather than&mut Commands.// 0.12 (Bevy 0.12) let commands = entity_commands.commands(); commands.spawn(...); // 0.13 (Bevy 0.13) let mut commands = entity_commands.commands(); commands.spawn(...);Deprecated Various Component Methods from
QueryandQueryState#ECSQueryState::get_component_unchecked_mutUse
QueryState::get_unchecked_manualand select for the exact component based on the structure of the exact query as required.Query::(get_)component(_unchecked)(_mut)Use
Query::getand select for the exact component based on the structure of the exact query as required.- For mutable access (
_mut), useQuery::get_mut - For unchecked access (
_unchecked), useQuery::get_unchecked - For panic variants (non-
get_), add.unwrap()
For example:
fn system(query: Query<(&A, &B, &C)>) { // 0.12 let b = query.get_component::<B>(entity).unwrap(); // Alternative 1 (using tuple destructuring) let (_, b, _) = query.get(entity).unwrap(); // Alternative 2 (using tuple item indexing) let b = query.get(entity).unwrap().1; }System::type_idConsistency #ECSIf you use
System::type_id()on function systems (exclusive or not), ensure you are comparing its value to otherSystem::type_id()calls, orIntoSystem::system_type_id().This code wont require any changes, because
IntoSystem’s are directly compared to each other.fn test_system() {} let type_id = test_system.type_id(); // ... // No change required assert_eq!(test_system.type_id(), type_id);Likewise, this code wont, because
System’s are directly compared.fn test_system() {} let type_id = IntoSystem::into_system(test_system).type_id(); // ... // No change required assert_eq!(IntoSystem::into_system(test_system).type_id(), type_id);The below does require a change, since you’re comparing a
Systemtype to aIntoSystemtype.fn test_system() {} // 0.12 assert_eq!(test_system.type_id(), IntoSystem::into_system(test_system).type_id()); // 0.13 assert_eq!(test_system.system_type_id(), IntoSystem::into_system(test_system).type_id());System Stepping implemented as Resource #
ECSEditorAppDiagnosticsAdd a call to
Schedule::set_label()for any customSchedule. This is only required if theSchedulewill be steppedMake the MapEntities trait generic over Mappers, and add a simpler EntityMapper #
ECSScenes- The existing
EntityMapper(notably used to replicateScenesacross differentWorlds) has been renamed toSceneEntityMapper - The
MapEntitiestrait now works with a genericEntityMapperinstead of the specific structEntityMapper. Calls tofn map_entities(&mut self, entity_mapper: &mut EntityMapper)need to be updated tofn map_entities<M: EntityMapper>(&mut self, entity_mapper: &mut M) - The new trait
EntityMapperhas been added to the prelude
Async channel v2 #
ECSTasksThe
PipelinedRenderingplugin is no longer exported on wasm. If you are including it in your wasm builds you should remove it.#[cfg(not(target_arch = "wasm32"))] app.add_plugins(bevy_render::pipelined_rendering::PipelinedRenderingPlugin);Add First/Pre/Post/Last schedules to the Fixed timestep #
ECSTimeUsage of
RunFixedUpdateLoopshould be renamed toRunFixedMainLoop.Move
EntityHashrelated types intobevy_ecs#ECSUtils- Uses of
bevy::utils::{EntityHash, EntityHasher, EntityHashMap, EntityHashSet}now have to be imported frombevy::ecs::entity::hash. - Uses of
EntityHashMapno longer have to specify the first generic parameter. It is now hardcoded to always beEntity.
Move Circle Gizmos to Their Own File #
Gizmos- Change
gizmos::CircleBuildertogizmos::circles::Circle2dBuilder - Change
gizmos::Circle2dBuildertogizmos::circles::Circle2dBuilder
move gizmo arcs to their own file #
Gizmosgizmos::Arc2dBuilder->gizmos::arcs::Arc2dBuilderMultiple Configurations for Gizmos #
GizmosGizmoConfigis no longer a resource and has to be accessed throughGizmoConfigStoreresource. The default config group isDefaultGizmoConfigGroup, but consider using your own custom config group if applicable.Use Direction3d for gizmos.circle normal #
GizmosPass a Direction3d for gizmos.circle normal, eg.
Direction3d::new(vec).unwrap_or(default)or potentiallyDirection3d::new_unchecked(vec)if you know your vec is definitely normalized.Rename "AddChild" to "PushChild" #
HierarchyThe struct
AddChildhas been renamed toPushChild, and the structAddChildInPlacehas been renamed toPushChildInPlace.Rename
InputtoButtonInput#InputUsers need to rename
InputtoButtonInputin their projects.Add window entity to TouchInput events #
InputAdd a
windowfield when constructing or destructuring aTouchInputstruct.Add delta to CursorMoved event #
InputYou need to add
deltato any manually createdCursorMovedstruct.Remove
Defaultimpl forCubicCurve#Math- Remove
CubicCurvefrom any structs that implementDefault. - Wrap
CubicCurvein a new type and provide your own default.
#[derive(Deref)] struct MyCubicCurve<P: Point>(pub CubicCurve<P>); impl Default for MyCubicCurve<Vec2> { fn default() -> Self { let points = [[ vec2(-1.0, -20.0), vec2(3.0, 2.0), vec2(5.0, 3.0), vec2(9.0, 8.0), ]]; Self(CubicBezier::new(points).to_curve()) } }Direction: Rename
from_normalizedtonew_unchecked#MathRenamed
Direction2d::from_normalizedandDirection3d::from_normalizedtonew_unchecked.Add
Capsule2dprimitive #MathCapsuleis nowCapsule3d. If you were using it for 2d you need to useCapsule2dRename RayTest to RayCast #
MathRayTest2d and RayTest3d have been renamed to RayCast2d and RayCast3d
Use
IntersectsVolumefor breakout example collisions #Physicssprite::collide_aabb::collideandsprite::collide_aabb::Collisionwere removed.// 0.12 let collision = bevy::sprite::collide_aabb::collide(a_pos, a_size, b_pos, b_size); if collision.is_some() { // ... } // 0.13 let collision = Aabb2d::new(a_pos.truncate(), a_size / 2.) .intersects(&Aabb2d::new(b_pos.truncate(), b_size / 2.)); if collision { // ... }If you were making use
collide_aabb::Collision, see the newcollide_with_sidefunction in thebreakoutexample.Add
ReflectFromWorldand replace theFromWorldrequirement onReflectComponentandReflectBundlewithFromReflect#Reflection- Existing uses of
ReflectComponent::from_worldandReflectBundle::from_worldwill have to be changed toReflectFromWorld::from_world. - Users of
#[reflect(Component)]and#[reflect(Bundle)]will need to also implement/deriveFromReflect. - Users of
#[reflect(Component)]and#[reflect(Bundle)]may now want to also addFromWorldto the list of reflected traits in case theirFromReflectimplementation may fail. - Users of
ReflectComponentwill now need to pass a&TypeRegistryto itsinsert,apply_or_insertandcopymethods.
Remove TypeUuid #
ReflectionConvert any uses of
#[derive(TypeUuid)]with#[derive(TypePath]for more complex uses see the relevant documentation for more information.Explicit color conversion methods #
RenderingColor::from(Vec4)is nowColor::rgba_from_array(impl Into<[f32; 4]>)Vec4::from(Color)is nowColor::rgba_to_vec4(&self)// 0.12 let color_vec4 = Vec4::new(0.5, 0.5, 0.5); let color_from_vec4 = Color::from(color_vec4); let color_array = [0.5, 0.5, 0.5]; let color_from_array = Color::from(color_array); // 0.13 let color_vec4 = Vec4::new(0.5, 0.5, 0.5); let color_from_vec4 = Color::rgba_from_array(color_vec4); let color_array = [0.5, 0.5, 0.5]; let color_from_array = Color::rgba_from_array(color_array);Add a
depth_biastoMaterial2d#RenderingPreparedMaterial2dhas a newdepth_biasfield. A value of 0.0 can be used to get the previous behavior.Bind group layout entries #
RenderingRenderDevice::create_bind_group_layout()doesn’t take aBindGroupLayoutDescriptoranymore. You need to provide the parameters separately// 0.12 let layout = render_device.create_bind_group_layout(&BindGroupLayoutDescriptor { label: Some("post_process_bind_group_layout"), entries: &[ BindGroupLayoutEntry { // ... }, ], }); // 0.13 let layout = render_device.create_bind_group_layout( "post_process_bind_group_layout", &[ BindGroupLayoutEntry { // ... }, ], );Swap material and mesh bind groups #
Rendering- Custom 2d and 3d mesh/material shaders should now use bind group 2
@group(2) @binding(x)for their bound resources, instead of bind group 1. - Many internal pieces of rendering code have changed so that mesh data is now in bind group 1, and material data is now in bind group 2. Semi-custom rendering setups (that don’t use the Material or Material2d APIs) should adapt to these changes.
light renderlayers #
RenderingLights no longer affect all
RenderLayersby default, now like cameras and meshes they default toRenderLayers::layer(0). To recover the previous behaviour and have all lights affect all views, add aRenderLayers::all()component to the light entity.Update to wgpu 0.18 #
RenderingRenderPassDescriptorcolor_attachments(as well asRenderPassColorAttachment, andRenderPassDepthStencilAttachment) now useStoreOp::StoreorStoreOp::Discardinstead of abooleanto declare whether or not they should be stored.RenderPassDescriptornow havetimestamp_writesandocclusion_query_setfields. These can safely be set toNone.ComputePassDescriptornow have atimestamp_writesfield. This can be set toNonefor now.- See the wgpu changelog for additional details
Keep track of when a texture is first cleared #
Rendering- Remove arguments to
ViewTarget::get_color_attachment()andViewTarget::get_unsampled_color_attachment(). - Configure clear color on
Camerainstead of onCamera3dandCamera2d. - Moved
ClearColorandClearColorConfigfrombevy::core_pipeline::clear_colortobevy::render::camera. ViewDepthTexturemust now be created via thenew()method
Approximate indirect specular occlusion #
RenderingRenamed
PbrInput::occlusiontodiffuse_occlusion, and addedspecular_occlusion.Texture Atlas rework #
RenderingThe
TextureAtlasasset that previously contained both the atlas layout and image handle was renamed toTextureAtlasLayoutwith the image handle portion moved to a separateHandle<Image>available fromSpriteSheetBundle::textureorAtlasImageBundle::image.TextureAtlasSpritewas removed and replaced by a new component,TextureAtlas, which now holds the atlas index. TheSpritecomponent can be used forflip_x,flip_y,custom_size,anchor, andcolor.SpriteSheetBundlenow uses aSpriteinstead of aTextureAtlasSpritecomponent and aTextureAtlascomponent instead of aHandle<TextureAtlaslayout>.DynamicTextureAtlasBuilder::add_texturetakes an additional&Handle<Image>parameter.TextureAtlasLayout::from_gridno longer takes aHandle<Image>parameter.TextureAtlasBuilder::finishnow returns aResult<(TextureAtlasLayout, Image), TextureAtlasBuilderError>.UiTextureAtlasImagewas removed. TheAtlasImageBundleis now identical toImageBundlewith an additionalTextureAtlas.- Sprites
fn my_system( mut commands: Commands, - mut atlases: ResMut<Assets<TextureAtlas>>, + mut atlases: ResMut<Assets<TextureAtlasLayout>>, asset_server: Res<AssetServer> ) { let texture_handle = asset_server.load("my_texture.png"); - let layout = TextureAtlas::from_grid(texture_handle, Vec2::new(25.0, 25.0), 5, 5, None, None); + let layout = TextureAtlasLayout::from_grid(Vec2::new(25.0, 25.0), 5, 5, None, None); let layout_handle = atlases.add(layout); commands.spawn(SpriteSheetBundle { - sprite: TextureAtlasSprite::new(0), - texture_atlas: atlas_handle, // the new sprite initialization is covered by the `..default()` expression; however, it is added to showcase migration + sprite: Sprite::default(), + atlas: TextureAtlas { + layout: layout_handle, + index: 0 + }, + texture: texture_handle, ..default() }); }- UI
fn my_system( mut images: ResMut<Assets<Image>>, - mut atlases: ResMut<Assets<TextureAtlas>>, + mut atlases: ResMut<Assets<TextureAtlasLayout>>, asset_server: Res<AssetServer> ) { let texture_handle = asset_server.load("my_texture.png"); - let layout = TextureAtlas::from_grid(texture_handle, Vec2::new(25.0, 25.0), 5, 5, None, None); + let layout = TextureAtlasLayout::from_grid(Vec2::new(25.0, 25.0), 5, 5, None, None); let layout_handle = atlases.add(layout); commands.spawn(AtlasImageBundle { - texture_atlas_image: UiTextureAtlasImage { - index: 0, - flip_x: false, - flip_y: false, - }, - texture_atlas: atlas_handle, + texture_atlas: TextureAtlas { + layout: layout_handle, + index: 0 + }, + image: UiImage { + texture: texture_handle, + flip_x: false, + flip_y: false, + }, ..default() }); }- Queries (taken from the sprite sheet example)
fn animate_sprite( time: Res<Time>, mut query: Query<( &AnimationIndices, &mut AnimationTimer, - &mut TextureAtlasSprite)>, + &mut TextureAtlas)>, ) { - for (indices, mut timer, mut sprite) in &mut query { + for (indices, mut timer, mut atlas) in &mut query { timer.tick(time.delta()); if timer.just_finished() { - sprite.index = if sprite.index == indices.last { + atlas.index = if atlas.index == indices.last { indices.first } else { - sprite.index + 1 + atlas.index + 1 }; } } }Exposure settings (adopted) #
Rendering- If using a
SkyboxorEnvironmentMapLight, use the newbrightnessandintensitycontrols to adjust their strength. - All 3D scenes will now have different apparent brightnesses due to Bevy implementing proper exposure controls. You will have to adjust the intensity of your lights and/or your camera exposure via the new
Exposurecomponent to compensate.
Make
DynamicUniformBuffer::pushaccept an&Tinstead ofT#RenderingUsers of
DynamicUniformBuffer::pushnow need to pass references toDynamicUniformBuffer::push(e.g. existinguniforms.push(value)will now becomeuniforms.push(&value))Customizable camera main texture usage #
RenderingAdd
main_texture_usages: Default::default()to your camera bundle.optimize batch_and_prepare_render_phase #
RenderingThe trait
GetBatchDatano longer hold associated typeDataandFilterget_batch_dataquery_itemtype fromSelf::DatatoEntityand returnOption<(Self::BufferData, Option<Self::CompareData>)>batch_and_prepare_render_phaseshould not have a queryUpdate to wgpu 0.19 and raw-window-handle 0.6 #
Renderingbevy_render::instance_index::get_instance_index()has been removed as the webgl2 workaround is no longer required as it was fixed upstream in wgpu. TheBASE_INSTANCE_WORKAROUNDshaderdef has also been removed.WebGPU now requires the new
webgpufeature to be enabled. Thewebgpufeature currently overrides thewebgl2feature so you no longer need to disable all default features and re-add them all when targetingwebgpu, but binaries built with both thewebgpuandwebgl2features will only target the webgpu backend, and will only work on browsers that support WebGPU.- Places where you conditionally compiled things for webgl2 need to be updated because of this change, eg:
#[cfg(any(not(feature = "webgl"), not(target_arch = "wasm32")))]becomes#[cfg(any(not(feature = "webgl") ,not(target_arch = "wasm32"), feature = "webgpu"))]#[cfg(all(feature = "webgl", target_arch = "wasm32"))]becomes#[cfg(all(feature = "webgl", target_arch = "wasm32", not(feature = "webgpu")))]if cfg!(all(feature = "webgl", target_arch = "wasm32"))becomesif cfg!(all(feature = "webgl", target_arch = "wasm32", not(feature = "webgpu")))
- Places where you conditionally compiled things for webgl2 need to be updated because of this change, eg:
create_texture_with_datanow also takes aTextureDataOrder. You can probably just set this toTextureDataOrder::default()TextureFormat’sblock_sizehas been renamed toblock_copy_sizeSee the
wgpuchangelog for anything I might’ve missed: https://github.com/gfx-rs/wgpu/blob/trunk/CHANGELOG.mdwgpunow surfaces errors at instance creation time, which may have you run into this error (we’ve also seen it with nsight instead of EOSOverlay):
2024-01-27T02:11:58.491767Z ERROR wgpu_hal::vulkan::instance: GENERAL [Loader Message (0x0)] loader_get_json: Failed to open JSON file C:\Program Files (x86)\Epic Games\Launcher\Portal\Extras\Overlay\EOSOverlayVkLayer-Win32.json 2024-01-27T02:11:58.492046Z ERROR wgpu_hal::vulkan::instance: objects: (type: INSTANCE, hndl: 0x1fbe55dc070, name: ?) 2024-01-27T02:11:58.492282Z ERROR wgpu_hal::vulkan::instance: GENERAL [Loader Message (0x0)] loader_get_json: Failed to open JSON file C:\Program Files (x86)\Epic Games\Launcher\Portal\Extras\Overlay\EOSOverlayVkLayer-Win64.json 2024-01-27T02:11:58.492525Z ERROR wgpu_hal::vulkan::instance: objects: (type: INSTANCE, hndl: 0x1fbe55dc070, name: ?)It just means that the program didn’t properly cleanup their registry keys on an update/uninstall, and vulkan uses those keys to load validation layers. The fix is to backup your registry, then remove the offending keys in
"Computer\HKEY_LOCAL_MACHINE\SOFTWARE\Khronos\Vulkan\ImplicitLayers".RenderGraph Labelization #
RenderingFor Nodes and SubGraphs, instead of using hardcoded strings, you now pass labels, which can be derived with structs and enums.
// 0.12 #[derive(Default)] struct MyRenderNode; impl MyRenderNode { pub const NAME: &'static str = "my_render_node" } render_app .add_render_graph_node::<ViewNodeRunner<MyRenderNode>>( core_3d::graph::NAME, MyRenderNode::NAME, ) .add_render_graph_edges( core_3d::graph::NAME, &[ core_3d::graph::node::TONEMAPPING, MyRenderNode::NAME, core_3d::graph::node::END_MAIN_PASS_POST_PROCESSING, ], ); // 0.13 use bevy::core_pipeline::core_3d::graph::{Node3d, Core3d}; #[derive(Debug, Hash, PartialEq, Eq, Clone, RenderLabel)] pub struct MyRenderLabel; #[derive(Default)] struct MyRenderNode; render_app .add_render_graph_node::<ViewNodeRunner<MyRenderNode>>( Core3d, MyRenderLabel, ) .add_render_graph_edges( Core3d, ( Node3d::Tonemapping, MyRenderLabel, Node3d::EndMainPassPostProcessing, ), );If you still want to use dynamic labels, you can easily create those with tuple structs:
#[derive(Debug, Hash, PartialEq, Eq, Clone, RenderLabel)] pub struct MyDynamicLabel(&'static str);SubGraphs #
bevy_core_pipeline::core_2d::graph:NAME -> Core2dbevy_core_pipeline::core_3d::graph:NAME -> Core3dbevy_ui::render:draw_ui_graph::NAME -> graph::SubGraphUi
Nodes #
bevy_core_pipeline::core_2d::graph:node::MSAA_WRITEBACK -> Node2d::MsaaWritebacknode::MAIN_PASS ->Node2d::MainPassnode::BLOOM -> Node2d::Bloomnode::TONEMAPPING -> Node2d::Tonemappingnode::FXAA -> Node2d::Fxaanode::UPSCALING -> Node2d::Upscalingnode::CONTRAST_ADAPTIVE_SHARPENING -> Node2d::ContrastAdaptiveSharpeningnode::END_MAIN_PASS_POST_PROCESSING -> Node2d::EndMainPassPostProcessing
bevy_core_pipeline::core_3d::graph:node::MSAA_WRITEBACK -> Node3d::MsaaWritebacknode::PREPASS -> Node3d::Prepassnode::DEFERRED_PREPASS -> Node3d::DeferredPrepassnode::COPY_DEFERRED_LIGHTING_ID -> Node3d::CopyDeferredLightingIdnode::END_PREPASSES -> Node3d::EndPrepassesnode::START_MAIN_PASS -> Node3d::StartMainPassnode::MAIN_OPAQUE_PASS -> Node3d::MainOpaquePassnode::MAIN_TRANSMISSIVE_PASS -> Node3d::MainTransmissivePassnode::MAIN_TRANSPARENT_PASS -> Node3d::MainTransparentPassnode::END_MAIN_PASS -> Node3d::EndMainPassnode::BLOOM -> Node3d::Bloomnode::TONEMAPPING -> Node3d::Tonemappingnode::FXAA -> Node3d::Fxaanode::UPSCALING -> Node3d::Upscalingnode::CONTRAST_ADAPTIVE_SHARPENING -> Node3d::ContrastAdaptiveSharpeningnode::END_MAIN_PASS_POST_PROCESSING -> Node3d::EndMainPassPostProcessing
bevy_core_pipeline:taa::draw_3d_graph::node::TAA -> Node3d::Taabevy_pbr:draw_3d_graph::node::SHADOW_PASS -> NodePbr::ShadowPassssao::draw_3d_graph::node::SCREEN_SPACE_AMBIENT_OCCLUSION -> NodePbr::ScreenSpaceAmbientOcclusiondeferred::DEFERRED_LIGHTING_PASS -> NodePbr::DeferredLightingPassbevy_render:main_graph::node::CAMERA_DRIVER -> graph::CameraDriverLabelbevy_ui::render:draw_ui_graph::node::UI_PASS -> graph::Nodeui::UiPass
Gate diffuse and specular transmission behind shader defs #
RenderingIf you were using
#ifdef STANDARDMATERIAL_NORMAL_MAPon your shader code, make sure to update the name toSTANDARD_MATERIAL_NORMAL_MAP; (with an underscore betweenSTANDARDandMATERIAL)Async pipeline compilation #
RenderingMatch on the new
Creatingvariant for exhaustive matches ofCachedPipelineStateMesh insert indices #
Rendering- Use
Mesh::insert_indicesorMesh::with_inserted_indicesinstead ofMesh::set_indices/Mesh::with_indices. - If you have passed
NonetoMesh::set_indicesorMesh::with_indicesyou should useMesh::remove_indicesorMesh::with_removed_indicesinstead.
wait for render app when main world is dropped #
RenderingIf you were using the pipelined rendering channels,
MainToRenderAppSenderandRenderToMainAppReceiver, they have been combined into the single resourceRenderAppChannels.Deprecate shapes in
bevy_render::mesh::shape#RenderingBevy has previously used rendering-specific types like
UVSphereandQuadfor primitive mesh shapes. These have now been deprecated to use the geometric primitives newly introduced in version 0.13.bevy_render::mesh::Capsuleis deprecated usebevy_math::primitives::dim3::Capsule3dinstead;bevy_render::mesh::Cylinderis deprecated usebevy_math::primitives::dim3::Cylinderinstead;bevy_render::mesh::Icosphereis deprecated usebevy_math::primitives::dim3::Sphereinstead;bevy_render::mesh::Cubeis deprecated usebevy_math::primitives::dim3::Cuboidinstead;bevy_render::mesh::Boxis deprecated usebevy_math::primitives::dim3::Cuboidinstead;bevy_render::mesh::Quadis deprecated usebevy_math::primitives::dim2::Rectangleinstead;bevy_render::mesh::Planeis deprecated usebevy_math::primitives::dim2::Plane2dorbevy_math::primitives::dim3::Plane3dinstead;bevy_render::mesh::RegularPolygonis deprecated usebevy_math::primitives::dim2::RegularPolygoninstead;bevy_render::mesh::Circleis deprecated usebevy_math::primitives::dim2::Circleinstead;bevy_render::mesh::Torusis deprecated usebevy_math::primitives::dim3::Torusinstead;bevy_render::mesh::UVSphereis deprecated usebevy_math::primitives::dim3::Sphereinstead;
Some examples:
let before = meshes.add(shape::Box::new(5.0, 0.15, 5.0)); let after = meshes.add(Cuboid::new(5.0, 0.15, 5.0)); let before = meshes.add(shape::Quad::default()); let after = meshes.add(Rectangle::default()); let before = meshes.add(shape::Plane::from_size(5.0)); // The surface normal can now also be specified when using `new` let after = meshes.add(Plane3d::default().mesh().size(5.0, 5.0)); let before = meshes.add( Mesh::try_from(shape::Icosphere { radius: 0.5, subdivisions: 5, }) .unwrap(), ); let after = meshes.add(Sphere::new(0.5).mesh().ico(5).unwrap());Multithreaded render command encoding #
RenderingRenderContext::new()now takes adapter infoSome render graph and Node related types and methods now have additional lifetime constraints.
Stop extracting mesh entities to the render world. #
RenderingFor efficiency reasons, some meshes in the render world may not have corresponding
EntityIDs anymore. As a result, theentityparameter toRenderCommand::render()is now wrapped in anOption. Custom rendering code may need to be updated to handle the case in which noEntityexists for an object that is to be rendered.New Exposure and Lighting Defaults (and calibrate examples) #
RenderingThe increased
Exposure::ev100means that all existing 3D lighting will need to be adjusted to match (DirectionalLights, PointLights, SpotLights, EnvironmentMapLights, etc). Or alternatively, you can adjust theExposure::ev100on your cameras to work nicely with your current lighting values. If you are currently relying on default intensity values, you might need to change the intensity to achieve the same effect. Note that in Bevy 0.12, point/spot lights had a different hard coded ev100 value than directional lights. In Bevy 0.13, they use the same ev100, so if you have both in your scene, the scale between these light types has changed and you will likely need to adjust one or both of them.Many default lighting values were changed:
PointLight's default intensity is now1_000_000.0SpotLight's default intensity is now1_000_000.0DirectionalLight's default illuminance is now light_consts::lux::AMBIENT_DAYLIGHT (10_000.)AmbientLight's default brightness is now80.0
Unload render assets from RAM #
RenderingAssetsRender assets can now be automatically unloaded from the main world after being prepared for rendering in the render world. This is controlled using
RenderAssetUsagesbitflags. To mimic old behavior and keep assets around in the main world, useRenderAssetUsages::default().Meshnow requires a newasset_usagefield. Set it toRenderAssetUsages::default()to mimic the previous behavior.Imagenow requires a newasset_usagefield. Set it toRenderAssetUsages::default()to mimic the previous behavior.MorphTargetImage::new()now requires a newasset_usageparameter. Set it toRenderAssetUsages::default()to mimic the previous behavior.DynamicTextureAtlasBuilder::add_texture()now requires that theTextureAtlasyou pass has anImagewithasset_usage: RenderAssetUsages::default(). Ensure you construct the image properly for the texture atlas.The
RenderAssettrait has significantly changed, and requires adapting your existing implementations.- The trait now requires
Clone. - The
ExtractedAssetassociated type has been removed (the type itself is now extracted). - The signature of
prepare_asset()is slightly different - A new
asset_usage()method is now required (returnRenderAssetUsages::default()to match the previous behavior).
- The trait now requires
Match on the new
NoLongerUsedvariant for exhaustive matches ofAssetEvent.
Split
RayintoRay2dandRay3dand simplify plane construction #RenderingMathRayhas been renamed toRay3d.Ray creation #
// 0.12 Ray { origin: Vec3::ZERO, direction: Vec3::new(0.5, 0.6, 0.2).normalize(), } // 0.13 // Option 1: Ray3d { origin: Vec3::ZERO, direction: Direction3d::new(Vec3::new(0.5, 0.6, 0.2)).unwrap(), } // Option 2: Ray3d::new(Vec3::ZERO, Vec3::new(0.5, 0.6, 0.2))Plane intersections #
// 0.12 let result = ray.intersect_plane(Vec2::X, Vec2::Y); // 0.13 let result = ray.intersect_plane(Vec2::X, Plane2d::new(Vec2::Y));Introduce AspectRatio struct #
RenderingMathAnywhere where you are currently expecting a f32 when getting aspect ratios, you will now receive a
AspectRatiostruct. this still holds the same value.Include UI node size in the vertex inputs for UiMaterial. #
RenderingUIThis change should be backwards compatible, using the new field is optional.
Optional ImageScaleMode #
RenderingUIRe-export
futures_liteinbevy_tasks#Tasks- Remove
futures_litefromCargo.toml.
[dependencies] bevy = "0.12.0" - futures-lite = "1.4.0"- Replace
futures_liteimports withbevy::tasks::futures_lite.
- use futures_lite::future::poll_once; + use bevy::tasks::futures_lite::future::poll_once;Rename
TextAlignmenttoJustifyText. #TextTextAlignmenthas been renamed toJustifyTextTextBundle::with_text_alignmenthas been renamed toTextBundle::with_text_justifyText::with_alignmenthas been renamed toText::with_justify- The
text_alignmentfield ofTextMeasureInfohas been renamed tojustification
Rename
Time::<Fixed>::overstep_percentage()andTime::<Fixed>::overstep_percentage_f64()#TimeTime::<Fixed>::overstep_percentage()has been renamed toTime::<Fixed>::overstep_fraction()Time::<Fixed>::overstep_percentage_f64()has been renamed toTime::<Fixed>::overstep_fraction_f64()
Rename
Timer::{percent,percent_left}toTimer::{fraction,fraction_remaining}#TimeTimer::percent()has been renamed toTimer::fraction()Timer::percent_left()has been renamed toTimer::fraction_remaining()
return Direction3d from Transform::up and friends #
TransformMathCallers of
Transform::up()and similar functions may have to dereference the returnedDirection3dto get to the innerVec3.Make clipped areas of UI nodes non-interactive #
UIThe clipped areas of UI nodes are no longer interactive.
RelativeCursorPositionis now calculated relative to the whole node’s position and size, not only the visible part. Itsmouse_overmethod only returns true when the cursor is over an unclipped part of the node.RelativeCursorPositionno longer implementsDerefandDerefMut.Create serialize feature for bevy_ui #
UIIf you want to use serialize and deserialize with types from bevy_ui, you need to use the feature serialize in your TOML
[dependencies.bevy] features = ["serialize"]Camera-driven UI #
UIIf the world contains more than one camera, insert
TargetCamera(Entity)component to each UI root node, whereEntityis the ID of the camera you want this specific UI tree to be rendered to. Test for any required adjustments of UI positioning and scaling.// 0.12 commands.spawn(Camera3dBundle { ... }); commands.spawn(NodeBundle { ... }); // 0.13 let camera = commands.spawn(Camera3dBundle { ... }).id(); commands.spawn((TargetCamera(camera), NodeBundle { ... }));Remove
UiCameraConfigcomponent from all cameras. If it was used to control UI visibility, insertVisibilitycomponent on UI root nodes instead.Change
Windowscale factor to f32 (adopted) #WindowingIf manipulating scale_factor, some conversion from f64 to f32 may be necessary.
Update winit dependency to 0.29 #
WindowingInputKeyCode changes #
Several
KeyCodevariants have been renamed and now represent physical keys on the keyboard, replacingScanCode.Common examples of the updated variants are as follows:
KeyCode::W->KeyCode::KeyWKeyCode::Up->KeyCode::ArrowUpKeyCode::Key1->KeyCode::Digit1
See the relevant documentation for more information.
ReceivedCharacter changes #
The
charfield ofReceivedCharacteris now aSmolStr, and could contain multiple characters. See these winit docs for details.A simple workaround if you need a
charis to call.chars().last().It's now possible to use
KeyEvent::logical_key'sCharactervariant instead if you need consistent cross-platform behavior and/or a unified event stream with non-character events.Remove CanvasParentResizePlugin #
WindowingRemove uses of
Window::fit_canvas_to_parentin favor of CSS properties, for example:canvas { width: 100% !important; height: 100% !important; }Cleanup bevy winit #
Windowingbevy::window::WindowMoved’sentityfield has been renamed towindow. This is to be more consistent with other windowing events.Consider changing usage:
-window_moved.entity +window_moved.windowAdd
nametobevy::window::Window#WindowingWindowhas a newnamefield for specifying the "window class name." If you don't need this, set it toNone.delete methods deprecated in 0.12 #
No area labelMany methods that were tagged as deprecated in 0.12 have now been removed. You should consider fixing the deprecation warnings before migrating to 0.13.
Renamed Accessibility plugin to AccessKitPlugin in bevy_winit #
No area labelbevy_winit::AccessibilityPluginhas been renamed toAccessKitPlugin.Use TypeIdMap whenever possible #
No area labelTypeIdMapnow lives inbevy_utilsDrawFunctionsInternal::indicesnow uses aTypeIdMap.
bevy_render: use the non-send marker from bevy_core #
No area labelIf you were using
bevy::render::view::NonSendMarkerorbevy::render::view::window::NonSendMarker, usebevy::core::NonSendMarkerinstead