-
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 #
The option to ignore the global volume using Volume::Absolute
has been removed and Volume
now stores the volume level directly, removing the need for the VolumeLevel
struct.
Volume::new_absolute
and Volume::new_relative
were removed. Use Volume::new(0.5)
.
Optional override for global spatial scale #
AudioPlugin::spatial_scale
has been renamed to default_spatial_scale
and the default spatial scale can now be overridden on individual audio sources with PlaybackSettings::spatial_scale
.
If you were modifying or reading SpatialScale
at run time, use DefaultSpatialScale
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 #
LogPlugin
has a new optional update_subscriber
field. Use None
or ..default()
to match previous behavior.
Replace DiagnosticId
by DiagnosticPath
#
- 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
for EntityMapper
#
If you are using the following types, update their listed methods to use the new EntityHashMap
. EntityHashMap
has the same methods as the normal HashMap
, 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 return EventId
#
send
/ 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 & manual PartialOrd
/Ord
#
Any unsafe
code relying on field ordering of Entity
or sufficiently cursed shenanigans should change to reflect the different internal representation and alignment requirements of Entity
.
Split WorldQuery into QueryData and QueryFilter #
Check #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
#
TableRow::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 #
- Added a new field to
MainScheduleOrder
,startup_labels
, for editing the startup schedule order.
Auto insert sync points #
apply_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. #
Renamed App::add_state
to init_state
.
Rename ArchetypeEntity::entity
into ArchetypeEntity::id
#
The method ArchetypeEntity::entity
has been renamed to ArchetypeEntity::id
Restore support for running fn
EntityCommands
on entities that might be despawned
#
All Command
types in bevy_ecs
, such as Spawn
, SpawnBatch
, Insert
, etc., have been made private. Use the equivalent methods on Commands
or EntityCommands
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 #
Some 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
#
The 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 returns Commands
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
and QueryState
#
QueryState::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
#
If you use System::type_id()
on function systems (exclusive or not), ensure you are comparing its value to other System::type_id()
calls, or IntoSystem::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 a IntoSystem
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 #
Add a call to Schedule::set_label()
for any custom Schedule
. This is only required if the Schedule
will be stepped
Make the MapEntities trait generic over Mappers, and add a simpler EntityMapper #
- 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 #
The 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 #
Usage of RunFixedUpdateLoop
should be renamed to RunFixedMainLoop
.
Move EntityHash
related types into bevy_ecs
#
- 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 #
- Change
gizmos::CircleBuilder
togizmos::circles::Circle2dBuilder
- Change
gizmos::Circle2dBuilder
togizmos::circles::Circle2dBuilder
move gizmo arcs to their own file #
gizmos::Arc2dBuilder
-> gizmos::arcs::Arc2dBuilder
Multiple Configurations for Gizmos #
GizmoConfig
is no longer a resource and has to be accessed through GizmoConfigStore
resource. The default config group is DefaultGizmoConfigGroup
, but consider using your own custom config group if applicable.
Use Direction3d for gizmos.circle normal #
Pass a Direction3d for gizmos.circle normal, eg. Direction3d::new(vec).unwrap_or(default)
or potentially Direction3d::new_unchecked(vec)
if you know your vec is definitely normalized.
Rename "AddChild" to "PushChild" #
The struct AddChild
has been renamed to PushChild
, and the struct AddChildInPlace
has been renamed to PushChildInPlace
.
Rename Input
to ButtonInput
#
Users need to rename Input
to ButtonInput
in their projects.
Add window entity to TouchInput events #
Add a window
field when constructing or destructuring a TouchInput
struct.
Add delta to CursorMoved event #
You need to add delta
to any manually created CursorMoved
struct.
Remove Default
impl for CubicCurve
#
- 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
to new_unchecked
#
Renamed Direction2d::from_normalized
and Direction3d::from_normalized
to new_unchecked
.
Add Capsule2d
primitive
#
Capsule
is now Capsule3d
. If you were using it for 2d you need to use Capsule2d
Rename RayTest to RayCast #
RayTest2d and RayTest3d have been renamed to RayCast2d and RayCast3d
Use IntersectsVolume
for breakout example collisions
#
sprite::collide_aabb::collide
and sprite::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 new collide_with_side
function in the breakout
example.
Add ReflectFromWorld
and replace the FromWorld
requirement on ReflectComponent
and ReflectBundle
with FromReflect
#
- 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 #
Convert any uses of #[derive(TypeUuid)]
with #[derive(TypePath]
for more complex uses see the relevant documentation for more information.
Explicit color conversion methods #
Color::from(Vec4)
is now Color::rgba_from_array(impl Into<[f32; 4]>)
Vec4::from(Color)
is now Color::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
to Material2d
#
PreparedMaterial2d
has a new depth_bias
field. A value of 0.0 can be used to get the previous behavior.
Bind group layout entries #
RenderDevice::create_bind_group_layout()
doesn’t take a BindGroupLayoutDescriptor
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 #
- 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 #
Lights no longer affect all RenderLayers
by default, now like cameras and meshes they default to RenderLayers::layer(0)
. To recover the previous behaviour and have all lights affect all views, add a RenderLayers::all()
component to the light entity.
Update to wgpu 0.18 #
RenderPassDescriptor
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 #
- 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 #
Renamed PbrInput::occlusion
to diffuse_occlusion
, and added specular_occlusion
.
Texture Atlas rework #
The TextureAtlas
asset that previously contained both the atlas layout and image handle was renamed to TextureAtlasLayout
with the image handle portion moved to a separate Handle<Image>
available from SpriteSheetBundle::texture
or AtlasImageBundle::image
.
TextureAtlasSprite
was removed and replaced by a new component, TextureAtlas
, which now holds the atlas index. The Sprite
component can be used for flip_x
, flip_y
, custom_size
, anchor
, and color
.
SpriteSheetBundle
now uses a Sprite
instead of a TextureAtlasSprite
component and a TextureAtlas
component instead of a Handle<TextureAtlaslayout>
.
DynamicTextureAtlasBuilder::add_texture
takes an additional &Handle<Image>
parameter.
TextureAtlasLayout::from_grid
no longer takes a Handle<Image>
parameter.
TextureAtlasBuilder::finish
now returns a Result<(TextureAtlasLayout, Image), TextureAtlasBuilderError>
.
UiTextureAtlasImage
was removed. The AtlasImageBundle
is now identical to ImageBundle
with an additional TextureAtlas
.
- 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) #
- 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 of T
#
Users of DynamicUniformBuffer::push
now need to pass references to DynamicUniformBuffer::push
(e.g. existing uniforms.push(value)
will now become uniforms.push(&value)
)
Customizable camera main texture usage #
Add main_texture_usages: Default::default()
to your camera bundle.
optimize batch_and_prepare_render_phase #
The trait GetBatchData
no longer hold associated type Data
and Filter
get_batch_data
query_item
type from Self::Data
to Entity
and return Option<(Self::BufferData, Option<Self::CompareData>)>
batch_and_prepare_render_phase
should not have a query
Update to wgpu 0.19 and raw-window-handle 0.6 #
-
bevy_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.md -
wgpu
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 #
For 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 #
If you were using #ifdef STANDARDMATERIAL_NORMAL_MAP
on your shader code, make sure to update the name to STANDARD_MATERIAL_NORMAL_MAP
; (with an underscore between STANDARD
and MATERIAL
)
Async pipeline compilation #
Match on the new Creating
variant for exhaustive matches of CachedPipelineState
Mesh insert indices #
- 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 #
If you were using the pipelined rendering channels, MainToRenderAppSender
and RenderToMainAppReceiver
, they have been combined into the single resource RenderAppChannels
.
Deprecate shapes in bevy_render::mesh::shape
#
Bevy has previously used rendering-specific types like UVSphere
and Quad
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 #
RenderContext::new()
now takes adapter info
Some render graph and Node related types and methods now have additional lifetime constraints.
Stop extracting mesh entities to the render world. #
For efficiency reasons, some meshes in the render world may not have corresponding Entity
IDs anymore. As a result, the entity
parameter to RenderCommand::render()
is now wrapped in an Option
. Custom rendering code may need to be updated to handle the case in which no Entity
exists for an object that is to be rendered.
New Exposure and Lighting Defaults (and calibrate examples) #
The 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 the Exposure::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 #
Render 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, use RenderAssetUsages::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
into Ray2d
and Ray3d
and simplify plane construction
#
Ray
has been renamed to Ray3d
.
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 #
Anywhere 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. #
This change should be backwards compatible, using the new field is optional.
Optional ImageScaleMode #
Re-export futures_lite
in bevy_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
to JustifyText
.
#
TextAlignment
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()
and Time::<Fixed>::overstep_percentage_f64()
#
Time::<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}
to Timer::{fraction,fraction_remaining}
#
Timer::percent()
has been renamed toTimer::fraction()
Timer::percent_left()
has been renamed toTimer::fraction_remaining()
return Direction3d from Transform::up and friends #
Callers of Transform::up()
and similar functions may have to dereference the returned Direction3d
to get to the inner Vec3
.
Make clipped areas of UI nodes non-interactive #
The 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. Its mouse_over
method only returns true when the cursor is over an unclipped part of the node.
RelativeCursorPosition
no longer implements Deref
and DerefMut
.
Create serialize feature for bevy_ui #
If 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 #
If the world contains more than one camera, insert TargetCamera(Entity)
component to each UI root node, where Entity
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, insert Visibility
component on UI root nodes instead.
Change Window
scale factor to f32 (adopted)
#
If manipulating scale_factor, some conversion from f64 to f32 may be necessary.
Update winit dependency to 0.29 #
KeyCode changes #
Several KeyCode
variants have been renamed and now represent physical keys on the keyboard, replacing ScanCode
.
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 of ReceivedCharacter
is now a SmolStr
, 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
's Character
variant instead if you need consistent cross-platform behavior and/or a unified event stream with non-character events.
Remove CanvasParentResizePlugin #
Remove uses of Window::fit_canvas_to_parent
in favor of CSS properties, for example:
canvas {
width: 100% !important;
height: 100% !important;
}
Cleanup bevy winit #
bevy::window::WindowMoved
’s entity
field has been renamed to window
. This is to be more consistent with other windowing events.
Consider changing usage:
-window_moved.entity
+window_moved.window
Add name
to bevy::window::Window
#
Window
has a new name
field for specifying the "window class name." If you don't need this, set it to None
.
delete methods deprecated in 0.12 #
Many 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 #
bevy_winit::AccessibilityPlugin
has been renamed to AccessKitPlugin
.
Use TypeIdMap whenever possible #
TypeIdMap
now lives inbevy_utils
DrawFunctionsInternal::indices
now uses aTypeIdMap
.
bevy_render: use the non-send marker from bevy_core #
If you were using bevy::render::view::NonSendMarker
or bevy::render::view::window::NonSendMarker
, use bevy::core::NonSendMarker
instead