- 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_texture
you need to wrap yourAssetId
inSome
finish
now returns the atlas texture image directly instead of a handle. Provide the atlas texture toadd
on Assetsto get a Handle Remove the ability to ignore global volume #
AudioThe option to ignore the global volume using
Volume::Absolute
has been removed andVolume
now stores the volume level directly, removing the need for theVolumeLevel
struct.Volume::new_absolute
andVolume::new_relative
were removed. UseVolume::new(0.5)
.Optional override for global spatial scale #
AudioAudioPlugin::spatial_scale
has been renamed todefault_spatial_scale
and the default spatial scale can now be overridden on individual audio sources withPlaybackSettings::spatial_scale
.If you were modifying or reading
SpatialScale
at run time, useDefaultSpatialScale
instead.// 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 #
DiagnosticsLogPlugin
has a new optionalupdate_subscriber
field. UseNone
or..default()
to match previous behavior.Replace
DiagnosticId
byDiagnosticPath
#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
EntityHashMap
forEntityMapper
#ECSIf you are using the following types, update their listed methods to use the new
EntityHashMap
.EntityHashMap
has the same methods as the normalHashMap
, so you just need to replace the name.EntityMapper
get_map
get_mut_map
new
world_scope
ReflectMapEntities
map_all_entities
map_entities
write_to_world
InstanceInfo
entity_map
- This is a property, not a method.
Update
Event
send methods to returnEventId
#ECSsend
/send_default
/send_batch
For the following methods:
Events::send
Events::send_default
Events::send_batch
EventWriter::send
EventWriter::send_default
EventWriter::send_batch
World::send_event
World::send_event_default
World::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
match
statements:// 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
Entity
with repr align & manualPartialOrd
/Ord
#ECSAny
unsafe
code relying on field ordering ofEntity
or 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
ExtractComponent
typeQuery
toData
. - Trait’s
GetBatchData
typeQuery
toData
. - Trait’s
ExtractInstance
typeQuery
toData
.
- Trait’s
- Rename
ReadOnlyWorldQuery
toQueryFilter
andWorldQuery
toQueryData
- 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
TableRow
as
Casting #ECSTableRow::new
->TableRow::from_usize
TableRow::index
->TableRow::as_usize
TableId::new
->TableId::from_usize
TableId::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_deferred
points 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
, andchain
to the correspondingignore_deferred
API,after_ignore_deferred
,before_ignore_deferred
orchain_ignore_deferred
for your system/set ordering.- You can also set
ScheduleBuildSettings::auto_insert_sync_points
tofalse
if you want to do it for the whole schedule. Note that in this mode you can still addapply_deferred
points manually. - For most manual insertions of
apply_deferred
you 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_deferred
if you useSystemParam::apply
and want sync points auto inserted after use of yourSystemParam
.
Add insert_state to App. #
ECSRenamed
App::add_state
toinit_state
.Rename
ArchetypeEntity::entity
intoArchetypeEntity::id
#ECSThe method
ArchetypeEntity::entity
has been renamed toArchetypeEntity::id
Restore support for running
fn
EntityCommands
on entities that might be despawned #ECSAll
Command
types inbevy_ecs
, such asSpawn
,SpawnBatch
,Insert
, etc., have been made private. Use the equivalent methods onCommands
orEntityCommands
instead.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
Commands
and related types #ECSThe lifetimes for
EntityCommands
have 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::commands
now returnsCommands
rather 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
Query
andQueryState
#ECSQueryState::get_component_unchecked_mut
Use
QueryState::get_unchecked_manual
and select for the exact component based on the structure of the exact query as required.Query::(get_)component(_unchecked)(_mut)
Use
Query::get
and 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_id
Consistency #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
System
type to aIntoSystem
type.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 theSchedule
will be steppedMake the MapEntities trait generic over Mappers, and add a simpler EntityMapper #
ECSScenes- The existing
EntityMapper
(notably used to replicateScenes
across differentWorld
s) has been renamed toSceneEntityMapper
- The
MapEntities
trait now works with a genericEntityMapper
instead 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
EntityMapper
has been added to the prelude
Async channel v2 #
ECSTasksThe
PipelinedRendering
plugin 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
RunFixedUpdateLoop
should be renamed toRunFixedMainLoop
.Move
EntityHash
related types intobevy_ecs
#ECSUtils- Uses of
bevy::utils::{EntityHash, EntityHasher, EntityHashMap, EntityHashSet}
now have to be imported frombevy::ecs::entity::hash
. - Uses of
EntityHashMap
no 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::CircleBuilder
togizmos::circles::Circle2dBuilder
- Change
gizmos::Circle2dBuilder
togizmos::circles::Circle2dBuilder
move gizmo arcs to their own file #
Gizmosgizmos::Arc2dBuilder
->gizmos::arcs::Arc2dBuilder
Multiple Configurations for Gizmos #
GizmosGizmoConfig
is no longer a resource and has to be accessed throughGizmoConfigStore
resource. 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
AddChild
has been renamed toPushChild
, and the structAddChildInPlace
has been renamed toPushChildInPlace
.Rename
Input
toButtonInput
#InputUsers need to rename
Input
toButtonInput
in their projects.Add window entity to TouchInput events #
InputAdd a
window
field when constructing or destructuring aTouchInput
struct.Add delta to CursorMoved event #
InputYou need to add
delta
to any manually createdCursorMoved
struct.Remove
Default
impl forCubicCurve
#Math- Remove
CubicCurve
from any structs that implementDefault
. - Wrap
CubicCurve
in 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_normalized
tonew_unchecked
#MathRenamed
Direction2d::from_normalized
andDirection3d::from_normalized
tonew_unchecked
.Add
Capsule2d
primitive #MathCapsule
is nowCapsule3d
. If you were using it for 2d you need to useCapsule2d
Rename RayTest to RayCast #
MathRayTest2d and RayTest3d have been renamed to RayCast2d and RayCast3d
Use
IntersectsVolume
for breakout example collisions #Physicssprite::collide_aabb::collide
andsprite::collide_aabb::Collision
were 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_side
function in thebreakout
example.Add
ReflectFromWorld
and replace theFromWorld
requirement onReflectComponent
andReflectBundle
withFromReflect
#Reflection- Existing uses of
ReflectComponent::from_world
andReflectBundle::from_world
will 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 addFromWorld
to the list of reflected traits in case theirFromReflect
implementation may fail. - Users of
ReflectComponent
will now need to pass a&TypeRegistry
to itsinsert
,apply_or_insert
andcopy
methods.
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_bias
toMaterial2d
#RenderingPreparedMaterial2d
has a newdepth_bias
field. 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 aBindGroupLayoutDescriptor
anymore. 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
RenderLayers
by 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 #
RenderingRenderPassDescriptor
color_attachments
(as well asRenderPassColorAttachment
, andRenderPassDepthStencilAttachment
) now useStoreOp::Store
orStoreOp::Discard
instead of aboolean
to declare whether or not they should be stored.RenderPassDescriptor
now havetimestamp_writes
andocclusion_query_set
fields. These can safely be set toNone
.ComputePassDescriptor
now have atimestamp_writes
field. This can be set toNone
for 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
Camera
instead of onCamera3d
andCamera2d
. - Moved
ClearColor
andClearColorConfig
frombevy::core_pipeline::clear_color
tobevy::render::camera
. ViewDepthTexture
must now be created via thenew()
method
Approximate indirect specular occlusion #
RenderingRenamed
PbrInput::occlusion
todiffuse_occlusion
, and addedspecular_occlusion
.Texture Atlas rework #
RenderingThe
TextureAtlas
asset that previously contained both the atlas layout and image handle was renamed toTextureAtlasLayout
with the image handle portion moved to a separateHandle<Image>
available fromSpriteSheetBundle::texture
orAtlasImageBundle::image
.TextureAtlasSprite
was removed and replaced by a new component,TextureAtlas
, which now holds the atlas index. TheSprite
component can be used forflip_x
,flip_y
,custom_size
,anchor
, andcolor
.SpriteSheetBundle
now uses aSprite
instead of aTextureAtlasSprite
component and aTextureAtlas
component instead of aHandle<TextureAtlaslayout>
.DynamicTextureAtlasBuilder::add_texture
takes an additional&Handle<Image>
parameter.TextureAtlasLayout::from_grid
no longer takes aHandle<Image>
parameter.TextureAtlasBuilder::finish
now returns aResult<(TextureAtlasLayout, Image), TextureAtlasBuilderError>
.UiTextureAtlasImage
was removed. TheAtlasImageBundle
is now identical toImageBundle
with 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
Skybox
orEnvironmentMapLight
, use the newbrightness
andintensity
controls 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
Exposure
component to compensate.
Make
DynamicUniformBuffer::push
accept an&T
instead ofT
#RenderingUsers of
DynamicUniformBuffer::push
now 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
GetBatchData
no longer hold associated typeData
andFilter
get_batch_data
query_item
type fromSelf::Data
toEntity
and returnOption<(Self::BufferData, Option<Self::CompareData>)>
batch_and_prepare_render_phase
should 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_WORKAROUND
shaderdef has also been removed.WebGPU now requires the new
webgpu
feature to be enabled. Thewebgpu
feature currently overrides thewebgl2
feature so you no longer need to disable all default features and re-add them all when targetingwebgpu
, but binaries built with both thewebgpu
andwebgl2
features 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_data
now also takes aTextureDataOrder
. You can probably just set this toTextureDataOrder::default()
TextureFormat
’sblock_size
has been renamed toblock_copy_size
See the
wgpu
changelog for anything I might’ve missed: https://github.com/gfx-rs/wgpu/blob/trunk/CHANGELOG.mdwgpu
now 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 -> Core2d
bevy_core_pipeline::core_3d::graph
:NAME -> Core3d
bevy_ui::render
:draw_ui_graph::NAME -> graph::SubGraphUi
Nodes #
bevy_core_pipeline::core_2d::graph
:node::MSAA_WRITEBACK -> Node2d::MsaaWriteback
node::MAIN_PASS ->Node2d::MainPass
node::BLOOM -> Node2d::Bloom
node::TONEMAPPING -> Node2d::Tonemapping
node::FXAA -> Node2d::Fxaa
node::UPSCALING -> Node2d::Upscaling
node::CONTRAST_ADAPTIVE_SHARPENING -> Node2d::ContrastAdaptiveSharpening
node::END_MAIN_PASS_POST_PROCESSING -> Node2d::EndMainPassPostProcessing
bevy_core_pipeline::core_3d::graph
:node::MSAA_WRITEBACK -> Node3d::MsaaWriteback
node::PREPASS -> Node3d::Prepass
node::DEFERRED_PREPASS -> Node3d::DeferredPrepass
node::COPY_DEFERRED_LIGHTING_ID -> Node3d::CopyDeferredLightingId
node::END_PREPASSES -> Node3d::EndPrepasses
node::START_MAIN_PASS -> Node3d::StartMainPass
node::MAIN_OPAQUE_PASS -> Node3d::MainOpaquePass
node::MAIN_TRANSMISSIVE_PASS -> Node3d::MainTransmissivePass
node::MAIN_TRANSPARENT_PASS -> Node3d::MainTransparentPass
node::END_MAIN_PASS -> Node3d::EndMainPass
node::BLOOM -> Node3d::Bloom
node::TONEMAPPING -> Node3d::Tonemapping
node::FXAA -> Node3d::Fxaa
node::UPSCALING -> Node3d::Upscaling
node::CONTRAST_ADAPTIVE_SHARPENING -> Node3d::ContrastAdaptiveSharpening
node::END_MAIN_PASS_POST_PROCESSING -> Node3d::EndMainPassPostProcessing
bevy_core_pipeline
:taa::draw_3d_graph::node::TAA -> Node3d::Taa
bevy_pbr
:draw_3d_graph::node::SHADOW_PASS -> NodePbr::ShadowPass
ssao::draw_3d_graph::node::SCREEN_SPACE_AMBIENT_OCCLUSION -> NodePbr::ScreenSpaceAmbientOcclusion
deferred::DEFERRED_LIGHTING_PASS -> NodePbr::DeferredLightingPass
bevy_render
:main_graph::node::CAMERA_DRIVER -> graph::CameraDriverLabel
bevy_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_MAP
on your shader code, make sure to update the name toSTANDARD_MATERIAL_NORMAL_MAP
; (with an underscore betweenSTANDARD
andMATERIAL
)Async pipeline compilation #
RenderingMatch on the new
Creating
variant for exhaustive matches ofCachedPipelineState
Mesh insert indices #
Rendering- Use
Mesh::insert_indices
orMesh::with_inserted_indices
instead ofMesh::set_indices
/Mesh::with_indices
. - If you have passed
None
toMesh::set_indices
orMesh::with_indices
you should useMesh::remove_indices
orMesh::with_removed_indices
instead.
wait for render app when main world is dropped #
RenderingIf you were using the pipelined rendering channels,
MainToRenderAppSender
andRenderToMainAppReceiver
, they have been combined into the single resourceRenderAppChannels
.Deprecate shapes in
bevy_render::mesh::shape
#RenderingBevy has previously used rendering-specific types like
UVSphere
andQuad
for primitive mesh shapes. These have now been deprecated to use the geometric primitives newly introduced in version 0.13.bevy_render::mesh::Capsule
is deprecated usebevy_math::primitives::dim3::Capsule3d
instead;bevy_render::mesh::Cylinder
is deprecated usebevy_math::primitives::dim3::Cylinder
instead;bevy_render::mesh::Icosphere
is deprecated usebevy_math::primitives::dim3::Sphere
instead;bevy_render::mesh::Cube
is deprecated usebevy_math::primitives::dim3::Cuboid
instead;bevy_render::mesh::Box
is deprecated usebevy_math::primitives::dim3::Cuboid
instead;bevy_render::mesh::Quad
is deprecated usebevy_math::primitives::dim2::Rectangle
instead;bevy_render::mesh::Plane
is deprecated usebevy_math::primitives::dim2::Plane2d
orbevy_math::primitives::dim3::Plane3d
instead;bevy_render::mesh::RegularPolygon
is deprecated usebevy_math::primitives::dim2::RegularPolygon
instead;bevy_render::mesh::Circle
is deprecated usebevy_math::primitives::dim2::Circle
instead;bevy_render::mesh::Torus
is deprecated usebevy_math::primitives::dim3::Torus
instead;bevy_render::mesh::UVSphere
is deprecated usebevy_math::primitives::dim3::Sphere
instead;
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
Entity
IDs anymore. As a result, theentity
parameter toRenderCommand::render()
is now wrapped in anOption
. Custom rendering code may need to be updated to handle the case in which noEntity
exists for an object that is to be rendered.New Exposure and Lighting Defaults (and calibrate examples) #
RenderingThe increased
Exposure::ev100
means that all existing 3D lighting will need to be adjusted to match (DirectionalLights, PointLights, SpotLights, EnvironmentMapLights, etc). Or alternatively, you can adjust theExposure::ev100
on 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.0
SpotLight
's default intensity is now1_000_000.0
DirectionalLight
'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
RenderAssetUsages
bitflags. To mimic old behavior and keep assets around in the main world, useRenderAssetUsages::default()
.Mesh
now requires a newasset_usage
field. Set it toRenderAssetUsages::default()
to mimic the previous behavior.Image
now requires a newasset_usage
field. Set it toRenderAssetUsages::default()
to mimic the previous behavior.MorphTargetImage::new()
now requires a newasset_usage
parameter. Set it toRenderAssetUsages::default()
to mimic the previous behavior.DynamicTextureAtlasBuilder::add_texture()
now requires that theTextureAtlas
you pass has anImage
withasset_usage: RenderAssetUsages::default()
. Ensure you construct the image properly for the texture atlas.The
RenderAsset
trait has significantly changed, and requires adapting your existing implementations.- The trait now requires
Clone
. - The
ExtractedAsset
associated 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
NoLongerUsed
variant for exhaustive matches ofAssetEvent
.
Split
Ray
intoRay2d
andRay3d
and simplify plane construction #RenderingMathRay
has 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
AspectRatio
struct. 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_lite
inbevy_tasks
#Tasks- Remove
futures_lite
fromCargo.toml
.
[dependencies] bevy = "0.12.0" - futures-lite = "1.4.0"
- Replace
futures_lite
imports withbevy::tasks::futures_lite
.
- use futures_lite::future::poll_once; + use bevy::tasks::futures_lite::future::poll_once;
Rename
TextAlignment
toJustifyText
. #TextTextAlignment
has been renamed toJustifyText
TextBundle::with_text_alignment
has been renamed toTextBundle::with_text_justify
Text::with_alignment
has been renamed toText::with_justify
- The
text_alignment
field ofTextMeasureInfo
has 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 returnedDirection3d
to get to the innerVec3
.Make clipped areas of UI nodes non-interactive #
UIThe clipped areas of UI nodes are no longer interactive.
RelativeCursorPosition
is now calculated relative to the whole node’s position and size, not only the visible part. Itsmouse_over
method only returns true when the cursor is over an unclipped part of the node.RelativeCursorPosition
no longer implementsDeref
andDerefMut
.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, whereEntity
is 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
UiCameraConfig
component from all cameras. If it was used to control UI visibility, insertVisibility
component on UI root nodes instead.Change
Window
scale 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
KeyCode
variants have been renamed and now represent physical keys on the keyboard, replacingScanCode
.Common examples of the updated variants are as follows:
KeyCode::W
->KeyCode::KeyW
KeyCode::Up
->KeyCode::ArrowUp
KeyCode::Key1
->KeyCode::Digit1
See the relevant documentation for more information.
ReceivedCharacter changes #
The
char
field ofReceivedCharacter
is now aSmolStr
, and could contain multiple characters. See these winit docs for details.A simple workaround if you need a
char
is to call.chars().last()
.It's now possible to use
KeyEvent::logical_key
'sCharacter
variant 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_parent
in favor of CSS properties, for example:canvas { width: 100% !important; height: 100% !important; }
Cleanup bevy winit #
Windowingbevy::window::WindowMoved
’sentity
field has been renamed towindow
. This is to be more consistent with other windowing events.Consider changing usage:
-window_moved.entity +window_moved.window
Add
name
tobevy::window::Window
#WindowingWindow
has a newname
field 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::AccessibilityPlugin
has been renamed toAccessKitPlugin
.Use TypeIdMap whenever possible #
No area labelTypeIdMap
now lives inbevy_utils
DrawFunctionsInternal::indices
now uses aTypeIdMap
.
bevy_render: use the non-send marker from bevy_core #
No area labelIf you were using
bevy::render::view::NonSendMarker
orbevy::render::view::window::NonSendMarker
, usebevy::core::NonSendMarker
instead