-
0.10 to 0.11
- Schedule-First: the new and improved add_systems
- bevy_audio: ECS-based API redesign
- Allow tuples and single plugins in add_plugins, deprecate add_plugin
- Improve shader import model
- Flatten UI Style properties that use Size + remove Size
- Merge ScheduleRunnerSettings into ScheduleRunnerPlugin
- Add support for custom glTF vertex attributes.
- Delay asset hot reloading
- Allow systems using Diagnostics to run in parallel
- Log to stderr instead of stdout
- Make WorldQuery meta types unnameable
- Increase type safety and clarity for change detection
- Remove ChangeTrackers
- Check for conflicting accesses in assert_is_system
- Remove base set error variants of ScheduleBuildError
- Remove #[system_param(ignore)] and #[world_query(ignore)]
- Replace some unsafe system executor code with safe code
- Use UnsafeWorldCell to increase code quality for SystemParam
- Update increment_change_tick to return a strongly-typed Tick
- Make state private and only accessible through getter for State resource
- Only trigger state transitions if next_state != old_state
- Simplify system piping and make it more flexible
- Simplify world schedule methods
- Rename UnsafeWorldCell::read_change_tick
- Improve safety for the multi-threaded executor using UnsafeWorldCell
- Improve encapsulation for commands and add docs
- Rename apply_system_buffers to apply_deferred
- Rename Command's "write" method to "apply"
- Require read-only queries in QueryState::par_iter
- Migrate the rest of the engine to UnsafeWorldCell
- Simplify the ComponentIdFor type
- Make QueryParIter::for_each_unchecked private
- Deprecate type aliases for WorldQuery::Fetch
- Implement WorldQuery for EntityRef
- Move AppTypeRegistry to bevy_ecs
- Make scene handling of entity references robust
- Rename map_entities and map_specific_entities
- Require #[derive(Event)] on all Events
- Fix boxed labels
- Remove OnUpdate system set
- Document query errors
- Update syn, encase, glam and hexasphere
- Rename keys like LAlt to AltLeft
- Rename Interaction::Clicked -> Interaction::Pressed
- Don't ignore additional entries in UntypedReflectDeserializerVisitor
- FromReflect Ergonomics Implementation
- bevy_reflect: stable type path v2
- Add get_at_mut to bevy_reflect::Map trait
- bevy_reflect: Better proxies
- Construct Box<dyn Reflect> from world for ReflectComponent
- Added Globals struct to prepass shader
- Make render graph slots optional for most cases
- Remove unnecessary values Vec from DynamicUniformBuffer and DynamicStorageBuffer
- Changed (Vec2, Vec2) to Rect in Camera::logical_viewport_rect
- Make glsl and spirv support optional
- Change default tonemapping method
- Apply codebase changes in preparation for StandardMaterial transmission
- Remove Component derive for AlphaMode
- Rename Plane struct to HalfSpace
- Fix Plane UVs / texture flip
- Add RenderTarget::TextureView
- Consistent screen-space coordinates
- Webgpu support
- Take example screenshots in CI
- Compute vertex_count for indexed meshes on GpuMesh
- Built-in skybox
- Left-handed y-up cubemap coordinates
- Add Aabb calculation for Sprite, TextureAtlasSprite and Mesh2d
- Add morph targets
- bevy_scene: Add SceneFilter
- (De) serialize resources in scenes
- Fix look_to variable naming
- Fix transform propagation of orphaned entities
- Remove Val::Undefined
- Changed spelling linebreak_behaviour to linebreak_behavior
- Add CSS Grid support to bevy_ui
- MeasureFunc improvements
- Divide by UiScale when converting UI coordinates from physical to logical
- NoWrap Text feature
- Replace the local text queues in the text systems with flags stored in a component
- Split UI Overflow by axis
- text_system split
- Update ahash and hashbrown
- Move bevy_ui accessibility systems to PostUpdate
Migration Guide: 0.10 to 0.11
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.
Schedule-First: the new and improved add_systems #
We have unified adding systems to schedules under a single API! add_systems
now accepts a ScheduleLabel
as the first parameter. app.add_system
, app.add_startup_system
, app.add_startup_systems
, system.on_startup()
, and system.in_schedule()
have been deprecated in favor of the unified app.add_systems
API.
“base sets” have been removed entirely in favor of Schedules. The built in CoreSet
and StartupSet
base sets have been replaced with top level schedules. (ex: CoreSet::Update
is now the Update
schedule).
This removes a ton of redundant APIs, removes implicit defaults entirely, and clears up a lot of the confusion introduced by base sets. We believe the consistency and ergonomics of the new add_systems
API speaks for itself:
// 0.10
app.add_system(a)
// 0.11
app.add_systems(Update, a)
// 0.10
app.add_systems((a, b).in_schedule(CoreSchedule::Startup))
// 0.11
app.add_systems(Startup, (a, b))
// 0.10
app.add_systems((a, b).in_schedule(CoreSchedule::Startup).in_base_set(StartupSet::PreStartup))
// 0.11
app.add_systems(PreStartup, (a, b))
// 0.10
app.add_startup_systems((a, b))
// 0.11
app.add_systems(Startup, (a, b))
// 0.10
app.add_systems((a, b).on_startup())
// 0.11
app.add_systems(Startup, (a, b))
// 0.10
app.add_systems((c, d, e))
// 0.11 (Update is no longer implied by default)
app.add_systems(Update, (c, d, e))
// 0.10
app.add_systems((f, g).in_schedule(CoreSchedule::FixedUpdate))
// 0.11
app.add_systems(FixedUpdate, (f, g))
// 0.10
app.add_systems(h.in_base_set(CoreSet::PostUpdate))
// 0.11
app.add_systems(PostUpdate, h)
// 0.10
app.add_systems(enter_menu.in_schedule(OnEnter(AppState::Menu)))
// 0.11
app.add_systems(OnEnter(AppState::Menu), enter_menu)
// 0.10
app.add_systems(exit_menu.in_schedule(OnExit(AppState::Menu)))
// 0.11
app.add_systems(OnExit(AppState::Menu), exit_menu)
// 0.10
render_app.add_systems((a, b).in_set(RenderSet::Queue))
// 0.11
render_app.add_systems(Render, (a, b).in_set(RenderSet::Queue))
Set configuration now also accepts a schedule:
// 0.10
app.configure_set(A.in_schedule(PostUpdate).after(B))
// 0.11
app.configure_set(PostUpdate, A.after(B))
// 0.10
app.configure_set(A.after(B))
// 0.11 (Update is no longer implied by default)
app.configure_set(Update, A.after(B))
// 0.10
app.configure_sets((A, B).in_schedule(PostUpdate).after(C))
// 0.11
app.configure_sets(PostUpdate, (A, B).after(C))
// 0.10
app.configure_sets((A, B).after(C))
// 0.11 (Update is no longer implied by default)
app.configure_sets(Update, (A, B).after(C))
bevy_audio: ECS-based API redesign #
// 0.10
/// Need to store handles somewhere
#[derive(Resource)]
struct MyMusic {
sink: Handle<AudioSink>,
}
fn play_music(
asset_server: Res<AssetServer>,
audio: Res<Audio>,
audio_sinks: Res<Assets<AudioSink>>,
mut commands: Commands,
) {
let weak_handle = audio.play_with_settings(
asset_server.load("music.ogg"),
PlaybackSettings::LOOP.with_volume(0.5),
);
// upgrade to strong handle and store it
commands.insert_resource(MyMusic {
sink: audio_sinks.get_handle(weak_handle),
});
}
fn toggle_pause_music(
audio_sinks: Res<Assets<AudioSink>>,
mymusic: Option<Res<MyMusic>>,
) {
if let Some(mymusic) = &mymusic {
if let Some(sink) = audio_sinks.get(&mymusic.sink) {
sink.toggle();
}
}
}
// 0.11
/// Marker component for our music entity
#[derive(Component)]
struct MyMusic;
fn play_music(
mut commands: Commands,
asset_server: Res<AssetServer>,
) {
commands.spawn((
AudioBundle {
source: asset_server.load("music.ogg"),
settings: PlaybackSettings::LOOP.with_volume(Volume::new_relative(0.5)),
},
MyMusic,
));
}
fn toggle_pause_music(
// `AudioSink` will be inserted by Bevy when the audio starts playing
query_music: Query<&AudioSink, With<MyMusic>>,
) {
if let Ok(sink) = query_music.get_single() {
sink.toggle();
}
}
Allow tuples and single plugins in add_plugins
, deprecate add_plugin
#
Replace app.add_plugin(plugin)
calls with app.add_plugins(plugin)
.
Improve shader import model #
Shaders that don't use #import
directives should work without changes.
The most notable user-facing difference is that imported functions/variables/etc need to be qualified at point of use, and there's no "leakage" of visible stuff into your shader scope from the imports of your imports, so if you used things imported by your imports, you now need to import them directly and qualify them.
The current strategy of including/'spreading' mesh_vertex_output
directly into a struct doesn't work any more, so these need to be modified as per the examples (e.g. color_material.wgsl
, or many others). Mesh data is assumed to be in bindgroup 2 by default, if mesh data is bound into bindgroup 1 instead then the shader def MESH_BINDGROUP_1
needs to be added to the pipeline shader_defs
.
// 0.10
struct FragmentInput {
#import bevy_pbr::mesh_vertex_output
}
@fragment
fn fragment(in: FragmentInput) -> @location(0) vec4<f32> {}
// 0.11
#import bevy_pbr::mesh_vertex_output MeshVertexOutput
@fragment
fn fragment(in: MeshVertexOutput) -> @location(0) vec4<f32> {}
If you were importing something like mesh_view_bindings
but only for the globals
uniform buffer you can now import it directly.
// 0.10
#import bevy_pbr::mesh_view_bindings
// use globals.time after this
// 0.11
#import bevy_pbr::mesh_view_bindings globals
// globals is now in scope, but nothing else is imported
Flatten UI Style
properties that use Size
+ remove Size
#
The size
, min_size
, max_size
, and gap
properties have been replaced by the width
, height
, min_width
, min_height
, max_width
, max_height
, row_gap
, and column_gap
properties. Use the new properties instead.
Merge ScheduleRunnerSettings into ScheduleRunnerPlugin #
Instead of inserting the ScheduleRunnerSettings
resource, configure the ScheduleRunnerPlugin
// 0.10
.insert_resource(ScheduleRunnerSettings::run_loop(Duration::from_secs(5)))
.add_plugin(ScheduleRunnerPlugin::default())
// 0.11
.add_plugin(ScheduleRunnerPlugin::run_loop(Duration::from_secs(5)))
Add support for custom glTF vertex attributes. #
If you were instantiating GltfPlugin
using the unit-like struct syntax, you must instead use GltfPlugin::default()
as the type is no longer unit-like.
Delay asset hot reloading #
// 0.10
.add_plugins(DefaultPlugins.set(AssetPlugin {
watch_for_changes: true,
..default()
}))
// 0.11
.add_plugins(DefaultPlugins.set(AssetPlugin {
// You can now give it a configurable delay. This is a safe default.
watch_for_changes: ChangeWatcher::with_delay(Duration::from_millis(200)),
..default()
}))
Allow systems using Diagnostics to run in parallel #
- Register
Diagnostic
’s using the newapp.register_diagnostic(Diagnostic::new(DIAGNOSTIC_ID, "diagnostic_name", 10));
- In systems for writing new measurements, change
mut diagnostics: ResMut<Diagnostics>
tomut diagnostics: Diagnostics
to allow the systems to run in parallel.
- fn system(mut diagnostics: ResMut<Diagnostics>) {}
+ fn system(mut diagnostics: Diagnostics) {}
- In systems for reading measurements, change
diagnostics: Res<Diagnostics>
todiagnostics: Res<DiagnosticsStore>
.
- fn system(diagnostics: Res<Diagnostics>) {}
+ fn system(diagnostics: Res<DiagnosticsStore>) {}
Log to stderr instead of stdout #
On unix systems, when printing logs like info!
, trace!
, error!
, etc, read from stderr
instead of from stdout
- Use
2> output.log
on the command line to savestderr
to a file
Make WorldQuery
meta types unnameable
#
The State
and Fetch
types for types created using #[derive(WorldQuery)]
are now unnameable. If you need to refer to them, use the syntax <T as WorldQuery>::State
, <T as WorldQuery>::Fetch
.
Increase type safety and clarity for change detection #
The engine now uses the type Tick
for dealing with change ticks, instead of u32
. Any code that interfaced with engine internals will need to be updated, including:
- Manual implementers of the traits
SystemParam
,WorldQuery
,DetectChanges
, andDetectChangesMut
. - The methods
World::change_tick
andread_change_tick
. System::set_last_change_tick
andget_last_change_tick
. Also, these methods have been renamed toset_last_run
andget_last_run
, respectively.- The methods
SystemChangeTick::change_tick
andlast_change_tick
. These methods have been renamed tothis_run
andlast_run
, respectively. - The method
Tick::set_changed
, which has been renamed to justset
.
Remove ChangeTrackers #
ChangeTrackers
has been removed. Use Ref<T>
queries instead.
Check for conflicting accesses in assert_is_system
#
The functions assert_is_system
and assert_is_read_only_system
(in bevy_ecs::system
) now panic if the passed system has invalid world accesses. Any tests that called this function on a system with invalid accesses will now fail. Either fix the system’s conflicting accesses, or specify that the test is meant to fail:
- For regular tests (that is, functions annotated with
#[test]
), add the#[should_panic]
attribute to the function. - For documentation tests, add
should_panic
to the start of the code block:```should_panic
Remove base set error variants of ScheduleBuildError
#
With the removal of base sets, the related variants of ScheduleBuildError
have also been removed. If you were handling any of them you can safely remove the code handling them.
Remove #[system_param(ignore)]
and #[world_query(ignore)]
#
The attributes #[system_param(ignore)]
and #[world_query(ignore)]
have been removed. If you were using either of these with PhantomData
fields, you can simply remove the attribute:
#[derive(SystemParam)]
struct MyParam<'w, 's, Marker> {
...
// 0.10
#[system_param(ignore)]
_marker: PhantomData<Marker>,
// 0.11
_marker: PhantomData<Marker>,
}
#[derive(WorldQuery)]
struct MyQuery<Marker> {
...
// 0.10
#[world_query(ignore)]
_marker: PhantomData<Marker>,
// 0.11
_marker: PhantomData<Marker>,
}
If you were using this for another type that implements Default
, consider wrapping that type in Local<>
(this only works for SystemParam
):
#[derive(SystemParam)]
struct MyParam<'w, 's> {
// 0.10
#[system_param(ignore)]
value: MyDefaultType, // This will be initialized using `Default` each time `MyParam` is created.
// 0.11
value: Local<MyDefaultType>, // This will be initialized using `Default` the first time `MyParam` is created.
}
If you are implementing either trait and need to preserve the exact behavior of the old ignore
attributes, consider manually implementing SystemParam
or WorldQuery
for a wrapper struct that uses the Default
trait:
// 0.10
#[derive(WorldQuery)]
struct MyQuery {
#[world_query(ignore)]
str: String,
}
// 0.11
#[derive(WorldQuery)]
struct MyQuery {
str: DefaultQuery<String>,
}
pub struct DefaultQuery<T: Default>(pub T);
unsafe impl<T: Default> WorldQuery for DefaultQuery<T> {
type Item<'w> = Self;
...
unsafe fn fetch<'w>(...) -> Self::Item<'w> {
Self(T::default())
}
}
Replace some unsafe system executor code with safe code #
The function bevy_utils::SyncUnsafeCell::get_mut
now returns a value of type &mut SyncUnsafeCell<T>
. Previously, this returned an immutable reference.
Use UnsafeWorldCell
to increase code quality for SystemParam
#
For manual implementers of SystemParam
: the function get_item
now takes UnsafeWorldCell
instead of &World
. To access world data, use:
.get_entity()
, which returns anUnsafeEntityCell
which can be used to access component data.get_resource()
and its variants, to access resource data.
Update increment_change_tick
to return a strongly-typed Tick
#
The function UnsafeWorldCell::increment_change_tick
is now strongly-typed, returning a value of type Tick
instead of a raw u32
.
Make state private and only accessible through getter for State resource #
Use State::get
instead of accessing the tuple field directly.
Only trigger state transitions if next_state != old_state
#
State transitions are now only triggered when the exited and entered state differ. This means that if the world is currently in state A
, the OnEnter(A)
schedule (or OnExit
) will no longer be run if you queue up a state transition to the same state A
.
Simplify system piping and make it more flexible #
The IntoPipeSystem
trait has been removed, and the pipe
method has been moved to the IntoSystem
trait.
// 0.10
use bevy_ecs::system::IntoPipeSystem;
schedule.add_systems(first.pipe(second));
// 0.11
use bevy_ecs::system::IntoSystem;
schedule.add_systems(first.pipe(second));
Simplify world schedule methods #
The method World::run_schedule_ref
has been deprecated, and will be removed in the next version of Bevy. Use run_schedule
instead.
Rename UnsafeWorldCell::read_change_tick
#
The UnsafeWorldCell
method read_change_tick
has been renamed to change_tick
.
Improve safety for the multi-threaded executor using UnsafeWorldCell
#
The System
trait now uses UnsafeWorldCell
instead of &World
. This type provides a robust API for interior mutable world access.
- The method
run_unsafe
uses this type to manage world mutations across multiple threads. - The method
update_archetype_component_access
uses this type to ensure that only world metadata can be used.
let mut system = IntoSystem::into_system(my_system);
system.initialize(&mut world);
// 0.10
system.update_archetype_component_access(&world);
unsafe { system.run_unsafe(&world) }
// 0.11
system.update_archetype_component_access(world.as_unsafe_world_cell_readonly());
unsafe { system.run_unsafe(world.as_unsafe_world_cell()) }
Improve encapsulation for commands and add docs #
The Command
types Remove
and RemoveResource
may no longer be constructed manually.
// 0.10
commands.add(Remove::<T> {
entity: id,
phantom: PhantomData,
});
// 0.11
commands.add(Remove::<T>::new(id));
// 0.10
commands.add(RemoveResource::<T> { phantom: PhantomData });
// 0.11
commands.add(RemoveResource::<T>::new());
The command type GetOrSpawn
has been removed. It was not possible to use this type outside of bevy_ecs
.
Rename apply_system_buffers to apply_deferred #
apply_system_buffers
has been renamed toapply_deferred
- the
apply_system_buffers
method on theSystem
trait has been renamed toapply_deferred
- the
is_apply_system_buffers
function has been replaced byis_apply_deferred
Executor::set_apply_final_buffers
is nowExecutor::set_apply_final_deferred
Schedule::apply_system_buffers
is nowSchedule::apply_deferred
Rename Command's "write" method to "apply" #
Command::write
implementations need to be changed to implementCommand::apply
instead. This is a mere name change, with no further actions needed.EntityCommand::write
implementations need to be changed to implementEntityCommand::apply
instead. This is a mere name change, with no further actions needed.
Require read-only queries in QueryState::par_iter
#
The function QueryState::par_iter
now forces any world accesses to be read-only, similar to how QueryState::iter
works. Any code that previously mutated the world using this method was unsound. If you need to mutate the world, use par_iter_mut
instead.
Migrate the rest of the engine to UnsafeWorldCell
#
Mutating any world data using &World
is now considered unsound – the type UnsafeWorldCell
must be used to achieve interior mutability. The following methods now accept UnsafeWorldCell
instead of &World
:
QueryState
:get_unchecked
,iter_unchecked
,iter_combinations_unchecked
,for_each_unchecked
,get_single_unchecked
,get_single_unchecked_manual
.SystemState
:get_unchecked_manual
let mut world = World::new();
let mut query = world.query::<&mut T>();
// 0.10
let t1 = query.get_unchecked(&world, entity_1);
let t2 = query.get_unchecked(&world, entity_2);
// 0.11
let world_cell = world.as_unsafe_world_cell();
let t1 = query.get_unchecked(world_cell, entity_1);
let t2 = query.get_unchecked(world_cell, entity_2);
The methods QueryState::validate_world
and SystemState::matches_world
now take a WorldId
instead of &World
:
// 0.10
query_state.validate_world(&world);
// 0.11
query_state.validate_world(world.id());
The methods QueryState::update_archetypes
and SystemState::update_archetypes
now take UnsafeWorldCell
instead of &World
:
// 0.10
query_state.update_archetypes(&world);
// 0.11
query_state.update_archetypes(world.as_unsafe_world_cell_readonly());
Simplify the ComponentIdFor
type
#
The type ComponentIdFor<T>
now implements SystemParam
instead of FromWorld
– this means it should be used as the parameter for a system directly instead of being used in a Local
.
// 0.10
fn my_system(component_id: Local<ComponentIdFor<MyComponent>>) {
let component_id = **component_id;
}
// 0.11
fn my_system(component_id: ComponentIdFor<MyComponent>) {
let component_id = component_id.get();
}
Make QueryParIter::for_each_unchecked
private
#
The method QueryParIter::for_each_unchecked
has been removed – use for_each
or for_each_mut
instead. If your use case can not be achieved using either of these, then your code was likely unsound.
If you have a use-case for for_each_unchecked
that you believe is sound, please open an issue.
Deprecate type aliases for WorldQuery::Fetch
#
The type aliases bevy_ecs::query::QueryFetch
and ROQueryFetch
have been deprecated. If you need to refer to a WorldQuery
struct’s fetch type, refer to the associated type defined on WorldQuery
directly:
// 0.10
type MyFetch<'w> = QueryFetch<'w, MyQuery>;
type MyFetchReadOnly<'w> = ROQueryFetch<'w, MyQuery>;
// 0.11
type MyFetch<'w> = <MyQuery as WorldQuery>::Fetch;
type MyFetchReadOnly<'w> = <<MyQuery as WorldQuery>::ReadOnly as WorldQuery>::Fetch;
Implement WorldQuery for EntityRef #
EntityRef::world
has been removed to make EntityRef
sound to use as a query result. If you were retrieving EntityRef
via World::entity
or World::get_entity
. Save a copy of the reference to the World
before calling World::entity
.
// In 0.10
let entity_ref = world.entity(id);
let world_2 = entity_ref.world();
// In 0.11
let world_2 = &world;
let entity_ref = world.entity(id);
Move AppTypeRegistry to bevy_ecs #
If you were not using a prelude::*
to import AppTypeRegistry
, you should update your imports:
// 0.10
use bevy::app::AppTypeRegistry;
// 0.11
use bevy::ecs::reflect::AppTypeRegistry
Make scene handling of entity references robust #
MapEntities
implementations must change from a &EntityMap
parameter to a &mut EntityMapper
parameter and can no longer return a Result
. Finally, they should switch from calling EntityMap::get
to calling EntityMapper::get_or_reserve
.
Rename map_entities and map_specific_entities #
In bevy_ecs
, ReflectMapEntities::map_entities
now requires an additional entities
parameter to specify which entities it applies to. To keep the old behavior, use the new ReflectMapEntities::map_all_entities
, but consider if passing the entities in specifically might be better for your use case to avoid bugs.
Require #[derive(Event)]
on all Events
#
Add the #[derive(Event)]
macro for events. Third-party types used as events should be wrapped in a newtype.
Fix boxed labels #
The ScheduleLabel
trait has been refactored to no longer depend on the traits std::any::Any
, bevy_utils::DynEq
, and bevy_utils::DynHash
.
Any manual implementations will need to implement new trait methods instead.
impl ScheduleLabel for MyType {
// 0.10
fn dyn_clone(&self) -> Box<dyn ScheduleLabel> { ... }
// 0.11
fn dyn_clone(&self) -> Box<dyn ScheduleLabel> { ... }
fn as_dyn_eq(&self) -> &dyn DynEq {
self
}
// No, `mut state: &mut` is not a typo.
fn dyn_hash(&self, mut state: &mut dyn Hasher) {
self.hash(&mut state);
// Hashing the TypeId isn't strictly necessary, but it prevents collisions.
TypeId::of::<Self>().hash(&mut state);
}
}
Remove OnUpdate
system set
#
Replace OnUpdate
with run_if(in_state(xxx))
.
Document query errors #
QueryEntityError::QueryDoesNotMatch
's display message changed from "The given entity does not have the requested component." to "The given entity's components do not match the query.".
Update syn, encase, glam and hexasphere #
Using #[bundle]
attribute when deriving Bundle
for nested bundles now throws an error. It was already not required since version 0.9, see the migration guide.
#[derive(Bundle)]
struct PlayerBundle {
#[bundle] // Remove this line
sprite_bundle: SpriteBundle,
collider: Collider,
}
Rename keys like LAlt
to AltLeft
#
Migrate by replacing:
LAlt
→AltLeft
RAlt
→AltRight
LBracket
→BracketLeft
RBracket
→BracketRight
LControl
→ControlLeft
RControl
→ControlRight
LShift
→ShiftLeft
RShift
→ShiftRight
LWin
→SuperLeft
RWin
→SuperRight
Rename Interaction::Clicked -> Interaction::Pressed #
Rename all instances of Interaction::Clicked -> Interaction::Pressed
Don't ignore additional entries in UntypedReflectDeserializerVisitor
#
If you were deserializing Box<dyn Reflect>
values with multiple entries (i.e. entries other than "type": { /* fields */ }
) you should remove them or deserialization will fail.
FromReflect
Ergonomics Implementation
#
FromReflect
is now automatically derived within the Reflect
derive macro. Items with both derives will need to remove the FromReflect
one.
// 0.10
#[derive(Reflect, FromReflect)]
struct Foo;
// 0.11
#[derive(Reflect)]
struct Foo;
If using a manual implementation of FromReflect
and the Reflect
derive, users will need to opt-out of the automatic implementation.
// 0.10
#[derive(Reflect)]
struct Foo;
impl FromReflect for Foo {/* ... */}
// 0.11
#[derive(Reflect)]
#[reflect(from_reflect = false)]
struct Foo;
impl FromReflect for Foo {/* ... */}
bevy_reflect: stable type path v2 #
-
Implementors of
Asset
,Material
andMaterial2d
now also need to deriveTypePath
. -
Manual implementors of
Reflect
will need to implement the newget_type_path
method. -
To register types with generic parameters with the type registry, generic parameters must also implement
TypePath
even if they are only used for a#[reflect(ignore)]
field.// 0.10 struct MyType {} #[derive(Reflect)] struct MyTypeWithGeneric<A>{ #[reflect(ignore)] _phantom: PhantomData<A> } fn main() { App::new().add_plugins(DefaultPlugins) .register_type::<MyTypeWithGeneric<MyType>>() // in 0.11 this would error .run(); } // 0.11 #[derive(TypePath)] // New. Can also just use #[derive(Reflect)] struct MyType {} #[derive(Reflect)] struct MyTypeWithGeneric<A: TypePath>{ // changed #[reflect(ignore)] _phantom: PhantomData<A> } fn main() { App::new().add_plugins(DefaultPlugins) .register_type::<MyTypeWithGeneric<MyType>>() .run(); }
If you don't own a type you may need to wrap it in a newtype and manually implement
TypePath
for the newtype.// 0.10 use other_crate::RemoteType; #[derive(Reflect, Default)] struct MyTypeWithGeneric<A>{ #[reflect(ignore)] _phantom: PhantomData<A> } fn main() { App::new().add_plugins(DefaultPlugins) .register_type::<MyTypeWithGeneric<RemoteType>>() .run(); } // 0.11 use other_crate::RemoteType; #[derive(Default)] struct MyType(RemoteType); impl TypePath for MyType { fn type_path() -> &'static str { "my_crate::my_module::MyType" } fn short_type_path() -> &'static str { "MyType" } } #[derive(Reflect, Default)] struct MyTypeWithGeneric<A: TypePath> { #[reflect(ignore)] _phantom: PhantomData<A> } fn main() { App::new().add_plugins(DefaultPlugins) .register_type::<MyTypeWithGeneric::<MyType>>() .run(); }
Add get_at_mut
to bevy_reflect::Map
trait
#
Implementor of the Map
trait now need to implement get_at_mut
.
bevy_reflect: Better proxies #
- The Dynamic types no longer take a string type name. Instead, they require a static reference to
TypeInfo
:
#[derive(Reflect)]
struct MyTupleStruct(f32, f32);
let mut dyn_tuple_struct = DynamicTupleStruct::default();
dyn_tuple_struct.insert(1.23_f32);
dyn_tuple_struct.insert(3.21_f32);
// 0.10
let type_name = std::any::type_name::<MyTupleStruct>();
dyn_tuple_struct.set_name(type_name);
// 0.11
let type_info = <MyTupleStruct as Typed>::type_info();
dyn_tuple_struct.set_represented_type(Some(type_info));
Reflect::get_type_info
has been renamed toReflect::represented_type_info
and now also returns anOption<&'static TypeInfo>
(instead of just&'static TypeInfo
):
// 0.10
let info: &'static TypeInfo = value.get_type_info();
// 0.11
let info: &'static TypeInfo = value.represented_type_info().unwrap();
TypeInfo::Dynamic
andDynamicInfo
has been removed. UseReflect::is_dynamic instead
:
// 0.10
if matches!(value.get_type_info(), TypeInfo::Dynamic) {
// ...
}
// 0.11
if value.is_dynamic() {
// ...
}
Construct Box<dyn Reflect>
from world for ReflectComponent
#
If you were manually creating ReflectComponentFns
you now need to add a from_world
function pointer.
ReflectComponentFns {
from_world: |world| Box::new(MyComponent::from_world(world)),
// where `from_world: fn(&mut World) -> Box<dyn Reflect>`
// ...
}
Added Globals struct to prepass shader #
The Globals
shader struct is now accessible in prepass shaders. If you were manually binding it you can remove that code and use the globals directly.
Make render graph slots optional for most cases #
You can now get the view_entity directly from the RenderGraphContext
.
When implementing the Node:
// 0.10
struct FooNode;
impl FooNode {
const IN_VIEW: &'static str = "view";
}
impl Node for FooNode {
fn input(&self) -> Vec<SlotInfo> {
vec![SlotInfo::new(Self::IN_VIEW, SlotType::Entity)]
}
fn run(
&self,
graph: &mut RenderGraphContext,
// ...
) -> Result<(), NodeRunError> {
let view_entity = graph.get_input_entity(Self::IN_VIEW)?;
// ...
Ok(())
}
}
// 0.11
struct FooNode;
impl Node for FooNode {
fn run(
&self,
graph: &mut RenderGraphContext,
// ...
) -> Result<(), NodeRunError> {
let view_entity = graph.view_entity();
// ...
Ok(())
}
}
When adding the node to the graph, you don’t need to specify a slot_edge for the view_entity.
// 0.10
let mut graph = RenderGraph::default();
graph.add_node(FooNode::NAME, node);
let input_node_id = draw_2d_graph.set_input(vec![SlotInfo::new(
graph::input::VIEW_ENTITY,
SlotType::Entity,
)]);
graph.add_slot_edge(
input_node_id,
graph::input::VIEW_ENTITY,
FooNode::NAME,
FooNode::IN_VIEW,
);
// add_node_edge ...
// 0.11
let mut graph = RenderGraph::default();
graph.add_node(FooNode::NAME, node);
// add_node_edge ...
Remove unnecessary values Vec from DynamicUniformBuffer and DynamicStorageBuffer #
The len()
accessor has been removed because internal changes made it non-trivial to compute. If you were using it and don't have a workaround, please create an issue.
Changed (Vec2, Vec2) to Rect in Camera::logical_viewport_rect #
// 0.10
fn view_logical_camera_rect(camera_query: Query<&Camera>) {
let camera = camera_query.single();
let Some((min, max)) = camera.logical_viewport_rect() else { return };
dbg!(min, max);
}
// 0.11
fn view_logical_camera_rect(camera_query: Query<&Camera>) {
let camera = camera_query.single();
let Some(Rect { min, max }) = camera.logical_viewport_rect() else { return };
dbg!(min, max);
}
Make glsl and spirv support optional #
- If you want to use shaders in
spirv
, enable theshader_format_spirv
feature - If you want to use shaders in
glsl
, enable theshader_format_glsl
feature
Change default tonemapping method #
The default tonemapper has been changed from ReinhardLuminance to TonyMcMapface. Explicitly set ReinhardLuminance on your cameras to get back the previous look.
TonyMcMapface requires the ktx2
, tonemapping_luts
, and zstd
features, which are enabled by default. If you disable the default features and notice that your scene is pink, you can either add the ktx2
, tonemapping_luts
, and zstd
features, or use a different tonemapper.
Of the tonemappers that don't require a lookup table (LUT), SomewhatBoringDisplayTransform is the closest to TonyMcMapface. LUT based tonemappers are preferable as they tend to be faster.
Apply codebase changes in preparation for StandardMaterial
transmission
#
ViewTarget::main_texture()
andViewTarget::main_texture_other()
now return&Texture
instead of&TextureView
. If you were relying on these methods, replace your usage withViewTarget::main_texture_view()
andViewTarget::main_texture_other_view()
, respectively;ViewTarget::sampled_main_texture()
now returnsOption<&Texture>
instead of aOption<&TextureView>
. If you were relying on this method, replace your usage withViewTarget::sampled_main_texture_view()
;- The
apply_fog()
,linear_fog()
,exponential_fog()
,exponential_squared_fog()
andatmospheric_fog()
functions now take a configurableFog
struct. If you were relying on them, update your usage by adding the globalfog
uniform as their first argument;
Remove Component
derive for AlphaMode
#
AlphaMode
is not a component anymore.
It wasn’t used anywhere in the engine. If you were using it as a component for your own purposes, you should use a newtype instead, as follow:
#[derive(Component, Deref)]
struct MyAlphaMode(AlphaMode);
Then replace uses of AlphaMode
with MyAlphaMode
- Query<&AlphaMode, …>,
+ Query<&MyAlphaMode, …>,
Rename Plane
struct to HalfSpace
#
- Change instances of
render::primitives::Plane
torender::primitives::HalfSpace
- Change instances of the
planes
member inrender::primitives::Frustum
tohalf_spaces
Fix Plane
UVs / texture flip
#
Flip the textures you use on Plane
shapes.
Add RenderTarget::TextureView
#
References to the RenderTarget
enum will need to handle the additional field, ie in match
statements.
Consistent screen-space coordinates #
Window::cursor_position
now returns the position of the cursor relative to the top left instead of the bottom left.
This now matches other screen-space coordinates like RelativeCursorPosition
, UI, and viewports.
The world_to_viewport
, viewport_to_world
, and viewport_to_world_2d
methods on Camera
now return/take the viewport position relative to the top left instead of the bottom left.
If you were using world_to_viewport
to position a UI node the returned y
value should now be passed into the top
field on Style
instead of the bottom
field.
Note that this might shift the position of the UI node as it is now anchored at the top.
If you were passing Window::cursor_position
to viewport_to_world
or viewport_to_world_2d
no change is necessary.
Webgpu support #
Plugin::setup
has been renamedPlugin::cleanup
Plugin::finish
has been added, and plugins adding pipelines should do it in this function instead ofPlugin::build
// 0.10
impl Plugin for MyPlugin {
fn build(&self, app: &mut App) {
app.insert_resource::<MyResource>
.add_systems(Update, my_system);
let render_app = match app.get_sub_app_mut(RenderApp) {
Ok(render_app) => render_app,
Err(_) => return,
};
render_app
.init_resource::<RenderResourceNeedingDevice>()
.init_resource::<OtherRenderResource>();
}
}
// 0.11
impl Plugin for MyPlugin {
fn build(&self, app: &mut App) {
app.insert_resource::<MyResource>
.add_systems(Update, my_system);
let render_app = match app.get_sub_app_mut(RenderApp) {
Ok(render_app) => render_app,
Err(_) => return,
};
render_app
.init_resource::<OtherRenderResource>();
}
fn finish(&self, app: &mut App) {
let render_app = match app.get_sub_app_mut(RenderApp) {
Ok(render_app) => render_app,
Err(_) => return,
};
render_app
.init_resource::<RenderResourceNeedingDevice>();
}
}
Take example screenshots in CI #
TimeUpdateStrategy::ManualDuration
's meaning has changed. Instead of setting time to Instant::now()
plus the given duration, it sets time to last update plus the given duration.
Compute vertex_count
for indexed meshes on GpuMesh
#
vertex_count
is now stored directly on GpuMesh
instead of GpuBufferInfo::NonIndexed
.
Built-in skybox #
Flip EnvironmentMapLight
maps if needed to match how they previously rendered (which was backwards).
Left-handed y-up cubemap coordinates #
When sampling from the point light shadow cubemap, use the (expected) light to fragment direction vector but negate the z coordinate. Previously, you would have used the fragment to light direction vector.
Add Aabb
calculation for Sprite
, TextureAtlasSprite
and Mesh2d
#
- 2D entities are now subject to frustum culling, check your 2D camera's z coordinate and projection
far
if some of them are not rendered anymore - In particular, 2D entities with negative z values are now culled by frustum culling with the
default
Camera2dBundle
. We plan on re-adding support for negative values with the default 2D camera in version0.11.1
.
Add morph targets #
MeshPipeline
now has a singlemesh_layouts
field rather than separatemesh_layout
andskinned_mesh_layout
fields. You should handle all possible mesh bind group layouts in your implementation- You should also handle properly the new
MORPH_TARGETS
shader def and mesh pipeline key. A new function is exposed to make this easier:setup_moprh_and_skinning_defs
- The
MeshBindGroup
is nowMeshBindGroups
, cached bind groups are now accessed through theget
method.
bevy_scene: Add SceneFilter
#
DynamicScene::from_scene
and DynamicScene::from_world
no longer require an AppTypeRegistry
reference:
// 0.10
let registry = world.resource::<AppTypeRegistry>();
let dynamic_scene = DynamicScene::from_world(&world, registry);
// let dynamic_scene = DynamicScene::from_scene(&scene, registry);
// 0.11
let dynamic_scene = DynamicScene::from_world(&world);
// let dynamic_scene = DynamicScene::from_scene(&scene);
Removed DynamicSceneBuilder::from_world_with_type_registry
. Now the registry is automatically taken from the given world:
// 0.10
let registry = world.resource::<AppTypeRegistry>();
let builder = DynamicSceneBuilder::from_world_with_type_registry(&world, registry);
// 0.11
let builder = DynamicSceneBuilder::from_world(&world);
(De) serialize resources in scenes #
The scene format has been changed, the user may not be able to use scenes saved prior to this version due to the resources scene field being missing.
Fix look_to variable naming #
Transform::look_to
method changed default value of direction.try_normalize()
from Vec3::Z
to Vec3::NEG_Z
Fix transform propagation of orphaned entities #
- If you called
bevy_transform::systems::sync_simple_transforms
andbevy_transform::systems::propagate_transforms
(which is not re-exported by bevy) you need to account for the additionalRemovedComponents<Parent>
parameter.
Remove Val::Undefined
#
Val::Undefined
has been removed. Bevy UI’s behaviour with default values should remain the same.- The default values of
UiRect
’s fields have been changed toVal::Px(0.)
. Style
’sposition
field has been removed. Itsleft
,right
,top
andbottom
fields have been added toStyle
directly.- For the
size
,margin
,border
, andpadding
fields ofStyle
,Val::Undefined
should be replaced withVal::Px(0.)
. - For the
min_size
,max_size
,left
,right
,top
andbottom
fields ofStyle
,Val::Undefined
should be replaced withVal::Auto
Changed spelling linebreak_behaviour to linebreak_behavior #
Update where linebreak_behaviour
is used to linebreak_behavior
Updated the event FileDragAndDrop::HoveredFileCancelled
where used to HoveredFileCanceled
Update Touches.just_cancelled
where used as Touches.just_canceled
The event TouchPhase::Cancelled
is now called TouchPhase::Canceled
Add CSS Grid support to bevy_ui
#
- The
UiSystem::Flex
system set has been renamed toUiSystem::Layout
. - It is not possible to use the struct literal update syntax in const time with
Style
anymore, since one of its field implementsDrop
, doing so would raise a "the destructor for this type cannot be evaluated in constants" error (see this issue).
// 0.10
pub const ABSOLUTE_STYLE: Style = Style {
position_type: PositionType::Absolute,
..Style::DEFAULT
};
// 0.11
pub const ABSOLUTE_STYLE: Style = {
let mut style = Style::DEFAULT;
style.position_type = PositionType::Absolute;
style
};
MeasureFunc
improvements
#
-
CalculatedSize
has been renamed toContentSize
. -
The
upsert_leaf
function has been removed fromUiSurface
and replaced withupdate_measure
which updates theMeasureFunc
without node insertion. -
The
dyn_clone
method has been removed from theMeasure
trait. -
The new function of
CalculatedSize
has been replaced with the methodset
. -
ImageBundle
andTextBundle
don't implementClone
anymore. You can either:- Wrap yourself the bundle type and implement
Clone
by skipping cloning theContentSize
field. - Use a closure instead of
clone
:
// 0.10 let circle = ImageBundle { style: image_style, image: materials.circle.clone(), ..Default::default() }; commands.spawn(circle.clone()); commands.spawn(circle.clone()); commands.spawn(circle.clone()); commands.spawn(circle.clone()); // 0.11 let circle = || ImageBundle { style: image_style, image: materials.circle.clone(), ..Default::default() }; commands.spawn(circle()); commands.spawn(circle()); commands.spawn(circle()); commands.spawn(circle());
- Wrap yourself the bundle type and implement
Divide by UiScale
when converting UI coordinates from physical to logical
#
Physical UI coordinates are now divided by both the UiScale
and the window’s scale factor to compute the logical sizes and positions of UI nodes.
This ensures that UI Node size and position values, held by the Node
and GlobalTransform
components, conform to the same logical coordinate system as the style constraints from which they are derived, irrespective of the current scale_factor
and UiScale
.
NoWrap
Text
feature
#
bevy_text::text::BreakLineOn
has a new variant NoWrap
that disables text wrapping for the Text
.
Text wrapping can also be disabled using the with_no_wrap
method of TextBundle
.
Replace the local text queues in the text systems with flags stored in a component #
TextBundle
has a new field text_flag
of type TextFlags
.
Split UI Overflow
by axis
#
The Style
property Overflow
is now a struct with x
and y
fields, that allow for per-axis overflow control.
Use these helper functions to replace the variants of Overflow
:
- Replace
Overflow::Visible
withOverflow::visible()
- Replace
Overflow::Hidden
withOverflow::clip()
text_system
split
#
ImageBundle
has a new field image_size
of type UiImageSize
which contains the size of the image bundle's texture and is updated automatically by update_image_calculated_size_system
.
Update ahash and hashbrown #
If you were using hashes to an asset or using one of the fixed hasher exposed by Bevy with a previous version, you will have to update the hashes
Move bevy_ui accessibility systems to PostUpdate
#
bevy_ui
accessibility systems have been moved to PostUpdate
, if you were scheduling systems relative to these, make sure you now do it in PostUpdate
.