Bevy 0.15
Posted on November 29, 2024 by Bevy Contributors
Thanks to 294 contributors, 1217 pull requests, community reviewers, and our generous donors, we're happy to announce the Bevy 0.15 release on crates.io!
For those who don't know, Bevy is a refreshingly simple data-driven game engine built in Rust. You can check out our Quick Start Guide to try it today. It's free and open source forever! You can grab the full source code on GitHub. Check out Bevy Assets for a collection of community-developed plugins, games, and learning resources.
To update an existing Bevy App or Plugin to Bevy 0.15, check out our 0.14 to 0.15 Migration Guide.
Since our last release a few months ago we've added a ton of new features, bug fixes, and quality of life tweaks, but here are some of the highlights:
- Required Components: A rethink of how spawning entities works that significantly improves the Bevy user experience
- Entity Picking / Selection: A modular system for selecting entities across contexts
- Animation Improvements: generalized entity animation, animation masks, additive blending, and animation events
- Curves: a new
Curve
trait, cyclic splines, common easing functions, color gradient curves - Reflection Improvements: Function reflection, unique reflect, remote type reflection
- Bevy Remote Protocol (BRP): A new protocol that allows external clients (such as editors) to interact with running Bevy games
- Visibility Bitmask Ambient Occlusion (VBAO): An improved GTAO algorithm that improves ambient occlusion quality
- Chromatic Aberration: A new post processing effect that simulates lenses that fail to focus light to a single point
- Volumetric Fog Improvements: "Fog volumes" that define where volumetric fog is rendered (and what form it takes), along with Point Lights and Spotlight compatibility
- Order Independent Transparency: A new opt-in transparency algorithm that improves the stability / quality of transparent objects as their distance from the camera changes
- Improved Text Rendering: We've switched to Cosmic Text for our text rendering, which significantly improves our ability to render text, especially for non-Latin-based languages that require font shaping and bidirectional text
- Gamepads as Entities: Gamepads are now represented as entities, making them much easier to interact with
- UI Box Shadows: Bevy UI nodes can now render configurable box shadows
Bevy 0.15 was prepared using our new release candidate process to help ensure that you can upgrade right away with peace of mind. We worked closely with both plugin authors and ordinary users to catch critical bugs, polish new features, and refine the migration guide. For each release candidate, we prepared fixes, shipped a new release candidate on crates.io, let core ecosystem crates update, and listened closely for show-stopping problems. A huge thanks to everyone who helped out! These efforts are a vital step towards making Bevy something that teams large and small can trust to work reliably.
Required Components #
First: buckle up because Required Components is one of the most profound improvements to the Bevy API surface since Bevy was first released.
Since Bevy's creation, Bundle
has been our abstraction for spawning an entity of a given "type". A Bundle
is just a Rust type, where each field is a Component
:
#[derive(Bundle)]
struct PlayerBundle {
player: Player,
team: Team,
sprite: Sprite,
transform: Transform,
global_transform: GlobalTransform,
visibility: Visibility,
inherited_visibility: InheritedVisibility,
view_visibility: ViewVisibility,
}
Then whenever a new player needs to be spawned, developers would initialize and insert a PlayerBundle
on an entity:
commands.spawn(PlayerBundle {
player: Player {
name: "hello".into(),
..default()
},
team: Team::Blue,
..default()
});
This inserts all of the components in PlayerBundle
, including the ones not explicitly being set. The Bundle
concept is functional (it has gotten us this far), but it is also far from ideal:
- It is an entirely new set of APIs that developers need to learn. Someone that wants to spawn a
Player
entity needs to know thatPlayerBundle
exists. - Bundle APIs don't exist at runtime after insertion ... they are an additional spawn-only concept that developers need to think about. You don't write
PlayerBundle
behaviors. You writePlayer
behaviors. - The
Player
component needs the components inPlayerBundle
to function as aPlayer
. SpawningPlayer
on its own is possible, and it likely (depending on the implementation) wouldn't function as intended. - Bundles are always "flat" (by convention). The person defining the
Player
component needs to define all of the component dependencies.Sprite
needsTransform
andVisibility
,Transform
needsGlobalTransform
,Visibility
needsInheritedVisibility
andViewVisibility
. This lack of "dependency inheritance" makes defining bundles much harder and error prone than it needs to be. It requires consumers of APIs to be intimately aware of what amounts to implementation details. And when these details change, the developer of theBundle
needs to be aware and update theBundle
accordingly. Nested bundles are supported, but they are a pain for users to work with and we have disallowed them in upstream Bevy bundles for a while now. PlayerBundle
is effectively defined by the needs of thePlayer
component, but when spawning it is possible to never mention thePlayer
symbol. Ex:commands.spawn(PlayerBundle::default())
. This is odd given thatPlayer
is the "driving concept".- Bundles introduce significant "stutter" to the API. Notice the
player: Player
andteam: Team
in the example above. - Bundles introduce additional (arguably excessive) nesting and
..default()
usage.
Every one of these points has a sizable impact on what it feels like to use Bevy on a day-to-day basis. In Bevy 0.15 we've landed Required Components, which solves these problems by fundamentally rethinking how this all works.
Required Components are the first step in our Next Generation Scene / UI effort, which aims to make Bevy a best-in-class app / scene / UI development framework. Required Components stand on their own as a direct improvement to Bevy developers' lives, but they also help set the stage for making Bevy's upcoming next generation scene system (and the upcoming Bevy Editor) something truly special.
What are they?
Required Components enable developers to define which components a given component needs:
#[derive(Component, Default)]
#[require(Team, Sprite)]
struct Player {
name: String,
}
When the Player
component is inserted, its Required Components and the components required by those components are automatically inserted as well!
commands.spawn(Player::default());
The code above automatically inserts Team
and Sprite
. Sprite
requires Transform
and Visibility
, so those are automatically inserted as well. Likewise Transform
requires GlobalTransform
and Visibility
requires InheritedVisibility
and ViewVisibility
.
This code produces the same result as the PlayerBundle
code in the previous section:
commands.spawn((
Player {
name: "hello".into(),
..default()
},
Team::Blue,
))
Much better right? The Player
type is easier and less error prone to define, and spawning it takes less typing and is easier to read.
Efficiency
We've implemented Required Components in a way that makes them effectively "free":
- Required Components are only initialized and inserted if the caller did not insert them manually. No redundancy!
- Required Components are inserted alongside the normal components, meaning (for you ECS nerds out there) there are no additional archetype changes or table moves. From this perspective, the Required Components version of the
Player
example is identical to thePlayerBundle
approach, which manually defines all of the components up front. - Required Components are cached on the archetype graph, meaning computing what required components are necessary for a given type of insert only happens once.
Component Initialization
By default, Required Components will use the Default
impl for the component (and fail to compile if one does not exist):
#[derive(Component)]
#[require(Team)] // Team::Red is the default value
struct Player {
name: String,
}
#[derive(Component, Default)]
enum Team {
#[default]
Red,
Blue,
}
This can be overridden by passing in a function that returns the component:
#[derive(Component)]
#[require(Team(blue_team))]
struct Player {
name: String,
}
fn blue_team() -> Team {
Team::Blue
}
To save space, you can also pass a closure to the require directly:
#[derive(Component)]
#[require(Team(|| Team::Blue))]
struct Player {
name: String,
}
Isn't this a bit like inheritance?
Required Components can be considered a form of inheritance. But it is notably not traditional object-oriented inheritance. Instead it is "inheritance by composition". A Button
widget can (and should) require Node
to make it a "UI node". In a way, a Button
"is a" Node
like it would be in traditional inheritance. But unlike traditional inheritance:
- It is expressed as a "has a" relationship, not an "is a" relationship.
Button
andNode
are still two entirely separate types (with their own data), which you query for separately in the ECS.- A
Button
can require more components in addition toNode
. You aren't limited to "straight line" standard object-oriented inheritance. Composition is still the dominating pattern. - You don't need to require components to add them. You can still tack on whatever additional components you want during spawn to add behaviors in the normal "composition style".
What is happening to Bundles?
The Bundle
trait will continue to exist, and it is still the fundamental building block for insert APIs (tuples of components still implement Bundle
). Developers are still free to define their own custom bundles using the Bundle
derive. Bundles play nicely with Required Components, so you can use them with each other.
That being said, as of Bevy 0.15 we have deprecated all built-in bundles like SpriteBundle
, NodeBundle
, PbrBundle
, etc. in favor of Required Components. In general, Required Components are now the preferred / idiomatic approach. We encourage Bevy plugin and app developers to port their bundles over to Required Components.
Porting Bevy to Required Components
As mentioned above, all built-in Bevy bundles have been deprecated in favor of Required Components. We've also made API changes to take advantage of this new paradigm. This does mean breakage in a few places, but the changes are so nice that we think people won't complain too much :)
In general, we are moving in the direction specified by our Next Generation Scene / UI document. Some general design guidelines:
- When spawning an entity, generally there should be a "driving concept" component. When implementing a new entity type / behavior, give it a concept name ... that is the name of your "driving component" (ex: the "player" concept is a
Player
component). That component should require any additional components necessary to perform its functionality. - People should think directly in terms of components and their fields when spawning. Prefer using component fields directly on the "concept component" as the "public API" for the feature.
- Prefer simple APIs / don't over-componentize. By default, if you need to attach new properties to a concept, just add them as fields to that concept's component. Only break out new components / concepts when you have a good reason, and that reason is motivated by user experience or performance (and weight user experience highly). If a given "concept" (ex: a
Sprite
) is broken up into 10 components, that is very hard for users to reason about and work with. - Instead of using Asset handles directly as components, define new components that hold the necessary handles. Raw asset handles as components were problematic for a variety of reasons (a big one is that you can't define context-specific Required Components for them), so we have removed the
Component
implementation forHandle<T>
to encourage (well ... force) people to adopt this pattern.
UI
Bevy UI has benefitted tremendously from Required Components. UI nodes require a variety of components to function, and now all of those requirements are consolidated on Node
. Defining a new UI node type is now as simple as adding #[require(Node)]
to your component.
#[derive(Component)]
#[require(Node)]
struct MyNode;
commands.spawn(MyNode);
The Style
component fields have been moved into Node
. Style
was never a comprehensive "style sheet", but rather just a collection of properties shared by all UI nodes. A "true" ECS style system would style properties across components (Node
, Button
, etc), and we do have plans to build a true style system. All "computed" node properties (such as the size of the node after it has been laid out) have been moved to the ComputedNode
component.
This change has made spawning UI nodes in Bevy much cleaner and clearer:
commands.spawn(Node {
width: Val::Px(100.),
..default()
});
Compare that to what it was before!
commands.spawn(NodeBundle {
style: Style {
width: Val::Px(100.),
..default()
},
..default()
})
UI components like Button
, ImageNode
(previously UiImage
), and Text
now require Node
. Notably, Text
has been reworked to be easier to use and more component driven (we'll cover this more in the next section):
commands.spawn(Text::new("Hello there!"));
MaterialNode<M: UiMaterial>
is now a proper component for "UI material shaders", and it also requires Node
:
commands.spawn(MaterialNode(my_material));
Text
Bevy's Text API has been reworked to be simpler and more component driven. There are still two primary text components: Text
(the UI text component) and Text2d
(the world-space 2D text component).
The first thing that has changed is that these primary components are literally just a String
newtype:
commands.spawn(Text("hello".to_string()))
commands.spawn(Text::new("hello"))
commands.spawn(Text2d("hello".to_string()))
commands.spawn(Text2d::new("hello"))
Spawn one of these components, and you have text! Both of these components now require the following components:
TextFont
: configures the font / sizeTextColor
: configures the colorTextLayout
: configures how the text is laid out.
Text
, which is the UI component, also requires Node
because it is a node. Similarly, Text2d
requires a Transform
because it is positioned in world space.
Both Text
and Text2d
are a standalone "block" of text. These top level text components also contribute a single "span" of text, which is added to the "block". If you need "rich text" with multiple colors / fonts / sizes, you can add TextSpan
entities as children of either Text
or Text2d
. TextSpans
use the same TextFont
/ TextLayout
components to configure text. Each TextSpan
will contribute its span to its parent text:
// The `Text` UI node will render "hello world!", where "hello" is red and "world!" is blue
commands.spawn(Text::default())
.with_child((
TextSpan::new("hello"),
TextColor::from(RED),
))
.with_child((
TextSpan::new(" world!"),
TextColor::from(BLUE),
));
This produces the exact same output, but uses the "default" span on the top-level Text
component:
commands.spawn((
Text::new("hello"),
TextColor::from(RED),
))
.with_child((
TextSpan::new(" world!"),
TextColor::from(BLUE),
));
This "entity driven" approach to text spans replaces the "internal span array" approach used in previous Bevy versions. This yields significant benefits. First, it lets you use the normal Bevy ECS tools, such as marker components and queries, to mark a text span and access it directly. This is much easier (and more resilient) than using indices in an array, which are hard to guess and unstable as the array contents change:
#[derive(Component)]
struct NameText;
commands.spawn(Text::new("Name: "))
.with_child((
TextSpan::new("Unknown"),
NameText,
));
fn set_name(mut names: Query<&mut TextSpan, With<NameText>>) {
names.single_mut().0 = "George".to_string();
}
Text spans as entities play nicer with Bevy Scenes (including the upcoming Next Generation Scene / UI system), and allow it to integrate nicely with existing tools like entity inspectors, animation systems, timers, etc.
Sprites
Sprites are largely unchanged. In addition to the Required Components port (Sprite
now requires Transform
and Visibility
), we've also done some component consolidation. The TextureAtlas
component is now an optional Sprite::texture_atlas
field. Likewise the ImageScaleMode
component is now a Sprite::image_mode
field. Spawning sprites is now super simple!
commands.spawn(Sprite {
image: assets.load("player.png"),
..default()
});
Transforms
Transform
now requires GlobalTransform
. If you want your entity to have "hierarchical transforms", require Transform
(and it will add GlobalTransform
). If you just want your entity to have a "flat" global transform, require GlobalTransform
.
Most Bevy components that are intended to exist in world space now require Transform
.
Visibility
The Visibility
component now requires InheritedVisibility
and ViewVisibility
, meaning that you can now just require Visibility
if you want your entity to be visible. Bevy's built-in "visible" components, such as Sprite
, require Visibility
.
Cameras
The Camera2d
and Camera3d
components now each require Camera
. Camera
requires the various camera components (Frustum
, Transform
, etc.). This means that you can spawn a 2D or 3D camera like this:
commands.spawn(Camera2d::default());
commands.spawn(Camera3d::default());
Camera2d
and Camera3d
also require the components that set the relevant default render graphs and enable the default render features relevant to the 2D and 3D contexts (respectively).
You can of course explicitly set the values of the other components:
commands.spawn((
Camera3d::default(),
Camera {
hdr: true,
..default()
},
Transform {
translation: Vec3::new(1.0, 2.0, 3.0),
..default()
},
));
Bevy has a number of components that enable "camera render features": MotionBlur
, TemporalAntiAliasing
, ScreenSpaceAmbientOcclusion
, and ScreenSpaceReflections
. Some of these camera features depend on other camera feature components to function. These dependencies are now expressed and enforced using Required Components. For example, MotionBlur
now requires DepthPrepass
and MotionVectorPrepass
. This makes enabling camera features much easier!
commands.spawn((
Camera3d::default(),
MotionBlur,
))
Meshes
The old mesh approach relied on adding Handle<Mesh>
and Handle<M: Material>
components directly (via PbrBundle
and MaterialMeshBundle
), neither of which were compatible with required components.
In Bevy 0.15 you use Mesh3d
and MeshMaterial3d<M: Material>
to render a mesh in 3D:
commands.spawn((
Mesh3d(mesh),
MeshMaterial3d(material),
));
Mesh3d
requires Transform
and Visibility
.
There are also 2D equivalents:
commands.spawn((
Mesh2d(mesh),
MeshMaterial2d(material),
));
Meshlets
Bevy's "virtual geometry" implementation (similar to Nanite), has also been ported. It uses the same pattern as Mesh3d
and Mesh2d
:
commands.spawn((
MeshletMesh3d(mesh),
MeshMaterial3d(material),
));
Lights
The light port involved no major changes to the component structure. All of the spatial light types (PointLight
, DirectionalLight
, SpotLight
) now require Transform
and Visibility
, and each light component requires the relevant light-specific configuration components (ex: PointLight
requires CubemapFrusta
and CubemapVisibleEntities
).
Spawning a light of a given type is now as simple as:
commands.spawn(PointLight {
intensity: 1000.0,
..default()
});
The LightProbe
component now also requires Transform
and Visibility
.
Volumetric Fog
The FogVolume
component now requires Transform
and Visibility
, meaning you can now add volumetric fog like this:
commands.spawn(FogVolume {
density_factor: 0.2,
..default()
});
Scenes
Scenes previously used raw Handle<Scene>
components, spawned via SceneBundle
. Bevy 0.15 introduces the SceneRoot
component, which wraps the scene handle and requires Transform
and Visibility
:
commands.spawn(SceneRoot(some_scene));
Likewise, there is now DynamicSceneRoot
, which is exactly like SceneRoot
, but it wraps Handle<DynamicScene>
instead of Handle<Scene>
.
Audio
Audio also used a raw Handle<AudioSource>
spawned via an AudioBundle
. We've added a new AudioPlayer
component, which will trigger audio playback when spawned:
command.spawn(AudioPlayer(assets.load("music.mp3")));
AudioPlayer
requires the PlaybackSettings
component.
Non-standard audio from arbitrary Decodable
trait impls can use the AudioSourcePlayer
component, which also requires PlaybackSettings
.
IDE Integration
Required Components play nicely with Rust Analyzer. You can "go to definition" / press F12
on required components to navigate to where they are defined in code.
Runtime Required Components
In some cases, developers without direct control over a component might want to add additional Required Components on top of the ones provided directly on the type via #[require(Thing)]
. This is supported!
// Make `Bird` require `Wings` with a `Default` constructor.
app.register_required_components::<Bird, Wings>();
// Make `Wings` require `FlapSpeed` with a custom constructor.
app.register_required_components_with::<Wings, FlapSpeed>(|| FlapSpeed::from_duration(1.0 / 80.0));
Note that only adding Required Components is allowed. Removing Required Components from a type you do not own is explicitly not supported, as that could invalidate assumptions made upstream.
In general, this is intended to be used in very specific, targeted contexts, such as a physics plugin adding additional metadata to a core type it does not control. Adding a new component requirement could change the performance characteristics of the app or break it in unexpected ways. When in doubt, don't do it!
Chromatic Aberration #
We've added chromatic aberration, which is a common postprocessing effect that simulates lenses that fail to focus all colors of light to a single point. It's often used for impact effects and/or horror games. Our implementation uses the technique from Inside (Gjøl & Svendsen 2016), which allows the developer to customize the particular color pattern to achieve different effects.
To use it, add the ChromaticAberration
component to your camera:
commands.spawn((Camera3d::default(), ChromaticAberration));
Visibility Bitmask Ambient Occlusion (VBAO) #
Bevy 0.15 introduces a new Screen Space Ambient Occlusion (SSAO) algorithm: Visibility Bitmask Ambient Occlusion (VBAO). VBAO builds on GTAO by adding a bitmask that allows multiple "sectors" of a considered half circle to be occluded, instead of just one angle. This improves the accuracy of the technique and is particularly important on thin geometry (like the chair legs in the scene below):
Drag this image to compare
VBAO produces a significant enough quality improvement that we have replaced our old GTAO algorithm entirely. Just add the existing ScreenSpaceAmbientOcclusion
component to your camera to enable it.
Volumetric Fog Support for Point Lights and Spotlights #
Volumetric fog was introduced in Bevy 0.14. Initially, only directional lights could interact with it. In Bevy 0.15, point lights and spot lights work with it too:
To add volumetric fog to your scene, add VolumetricFog to the camera, and add VolumetricLight to directional light, point light, or spot light that you wish to be volumetric.
// Add VolumetricFog to the camera.
commands
.spawn((
Camera3d::default(),
Camera {
hdr: true,
..default()
},
))
.insert(VolumetricFog {
// This value is explicitly set to 0 since we have no environment map light.
ambient_intensity: 0.0,
..default()
});
// Add VolumetricLight to point light.
commands.spawn((
PointLight {
shadows_enabled: true,
range: 150.0,
color: RED.into(),
intensity: 1000.0,
..default()
},
VolumetricLight,
Transform::from_xyz(-0.4, 1.9, 1.0),
));
// Add VolumetricLight to spot light.
commands.spawn((
SpotLight {
intensity: 5000.0, // lumens
color: Color::WHITE,
shadows_enabled: true,
inner_angle: 0.76,
outer_angle: 0.94,
..default()
},
VolumetricLight,
Transform::from_xyz(-1.8, 3.9, -2.7).looking_at(Vec3::ZERO, Vec3::Y),
));
Fog Volumes #
Bevy 0.15 adds the concept of "fog volumes". These are entities with the FogVolume
component, which defines a bounding box for fog, which can be scaled and positioned to define where the fog will be rendered.
A camera with the VolumetricFog
component will render any FogVolume
entities in its view. Fog volumes can also define a density texture, which is a 3D texture of voxels that specify the density of the fog at each point:
FogVolume
has a density_texture_offset
, which allows the 3D texture to be "scrolled". This allows effects such as clouds "passing through" the volume:
Order Independent Transparency #
Before this feature, Bevy only used alpha blending to render transparent meshes. We now have the option to use Order Independent Transparency when rendering transparent meshes. Instead of only sorting the mesh, this will sort every pixel that contributes to a transparent triangle. This is useful if you have a lot of transparent layers in your scene.
The implementation is currently pretty simple and uses a lot of GPU memory, but it should always render perfectly accurate transparency as long as you have configured enough layers.
This feature is still a work in progress and we will keep working on improving it.
This feature was contributed to Bevy by Foresight Spatial Labs. It is based on an internal implementation they use in their applications.
User-Friendly CPU Drawing #
There are many situations where you might want to just set the color of pixels from CPU code. Procedural assets, certain art styles, or simply because it is easier. No need to bother with shaders and materials, when you just want to change a few specific pixels!
In previous versions of Bevy, this was difficult and tedious. Bevy gives you access to the raw data bytes of an Image
, but you had to compute the byte offset corresponding to your desired pixel coordinate, make sure to encode your bytes with respect to the TextureFormat
, etc. Very low level!
In Bevy 0.15, there are now user-friendly APIs for reading and writing the colors of pixels in an Image
. The tricky low-level details are dealt with for you! You can even use bevy_color
's fancy color space APIs!
fn my_system(mut images: ResMut<Assets<Image>>, mut commands: Commands) {
// Create a new image.
let mut image = Image::new_fill(
// 64x64 size
Extent3d {
width: 64,
height: 64,
depth_or_array_layers: 1,
},
TextureDimension::D2,
&Srgba::WHITE.to_u8_array(),
TextureFormat::Rgba8UnormSrgb,
RenderAssetUsages::all(),
);
// This is new:
// Make the pixel at x: 23, y: 32 magenta
image.set_color_at(23, 32, Color::srgb(1.0, 0.0, 1.0))
.expect("Error writing color");
// Set the pixel at 10,10 to a color specified using the Oklch color space:
image.set_color_at(10, 10, Color::oklch(0.3, 0.2, 0.5))
.expect("Error writing color");
// read the bytes of the pixel we just wrote:
let bytes = image.pixel_bytes(UVec3::new(10, 10, 0)).unwrap();
// read the (approximate) color back (as sRGB):
let color = image.get_color_at(10, 10);
// We could add our new image to Bevy's assets
// and spawn a sprite to display it:
commands.spawn(Sprite {
image: images.add(image),
..default()
});
}
Note: The Color
-based methods are lossy. They have to convert to/from the Image
's TextureFormat
. If you read back the color you wrote, it will be slightly different.
Entity Picking / Selection #
Being able to click on objects to select them is a vital and seemingly simple task in any game. Since 2020, doing this in Bevy has largely meant pulling in @aevyrie
's beloved ecosystem crate, bevy_mod_picking
and its simple raycasting companion bevy_mod_raycast
.
Over the years, this crate has been refined and battle-tested, both by Foresight Spatial Labs (a CAD-creating, Bevy-using company where Aevyrie works) and the broader open source community of game developers that have used it for everything from first-person-shooters to point-and-click adventures. Bevy is thrilled to have had the chance to work with the team behind bevy_mod_picking
and have adopted the project wholesale into Bevy itself. Integrating a large project is a ton of work, and we're incredibly grateful to the contributors who have made bevy_picking
a stable, first-class feature of the engine.
The new bevy_picking
crate follows the existing modular architecture closely:
- Inputs are gathered from mouse, touch and pen devices. Each pointing device (humans are equipped with 10 by default) gets a screen-space
PointerLocation
. - Each modular backend performs the domain-specific work (like raycasting) of figuring out how these pointer locations map to
PointerHits
on objects that they're watching. - The hit information from each backend is combined and sorted to produce a coherent
HoverMap
, which lists which entities each pointer is hovering over. - High level events (both ordinary events and observers!) are emitted for each hovered entity, capturing complex behavior such as clicking, dragging or releasing various objects.
In Bevy 0.15, we're shipping three first-party picking backends for UI, sprites, and meshes. Each of these comes with its own caveats for now:
- UI: both the legacy
Interaction
and newPickingInteraction
components exist for now, with subtle behavioral differences. - Sprites: picking always uses the full rectangle, and alpha transparency is not taken into account.
- Mesh: this is a naive raycast against the full mesh. If you run into performance problems here, you should use simplified meshes and an acceleration data structure like a BVH to speed this up. As a result, this functionality is currently disabled by default. It can be enabled by adding the
MeshPickingPlugin
.
We expect both bevy_rapier
and avian
(the two most popular ecosystem physics crates for Bevy) to add their own accelerated collider picking backends to work with the newly upstreamed API. Unless you're debugging, building an editor or really care about the exact triangles of raw meshes, you should use one of those crates for efficient mesh picking.
Usage
There are two good ways to get started with the API:
First, you might want to quickly update the state of your objects (be they UI or game objects) based on what is being done to them, typically highlighting them or changing their color. For that, simply query for changes to the PickingInteraction
component, which will change based on the current picking state.
Second, you might want to respond dynamically to various pointer-powered events. For that, we recommend using observers. Here, we're spawning a simple text node and responding to pointer events:
// UI text that prints a message when clicked:
commands
.spawn(Text::new("Click Me!"))
.observe(on_click_print_hello);
// A cube that spins when dragged:
commands
.spawn((
Mesh3d(meshes.add(Cuboid::default())),
MeshMaterial3d(materials.add(Color::WHITE)),
))
.observe(on_drag_spin);
}
fn on_click_print_hello(click: Trigger<Pointer<Click>>) {
println!("{} was clicked!", click.entity());
}
fn on_drag_spin(drag: Trigger<Pointer<Drag>>, mut transforms: Query<&mut Transform>) {
let mut transform = transforms.get_mut(drag.entity()).unwrap();
transform.rotate_y(drag.delta.x * 0.02);
}
If you want to override how an entity interacts with picking, add the PickingBehavior
component to them and configure it to meet your needs.
Bubbling Observers #
Virtually every pointer interaction (like mouse click) is rare (humans are slow!), and often requires a complex response.
This pattern is particularly useful in UI, where unhandled interactions are often intended for the pane that contains the entity that's on top, but is also valuable for in-game interactions: clicking on a unit's sword should select the unit!
To support this, we've extended the Event
trait to include an associated Traversal
type and an associated AUTO_PROPAGATE
constant. This behavior is opt-in: when you derive the Event
type, these are set to ()
and false
respectively.
For the Pointer<E>
event type, we've chosen to implement this as:
impl <E> Event for Pointer<E>{
type Traversal = &Parent;
const AUTO_PROPAGATE: bool = true;
}
This means that, unless you call Trigger::propagate(false)
, pointer events will be bubbled up the hierarchy (accessing the Entity
stored in the Parent
component) until it reaches the entity root.
Any type that implements the Traversal
trait can be used as the associated type and can access arbitrary read-only query data from the world. While using the standard entity hierarchy is a sensible choice for many applications, bubbling can be used for arbitrary event propagation using your own proto-relations. Let us know what you cook up: user feedback is indispensable for building a better Bevy!
Virtual Geometry Improvements #
Virtual geometry (the meshlet
feature) got a ton of improvements in Bevy 0.15. It's still not production ready, and will remain as an experimental module, but performance has been greatly improved upon since the last release.
For all the interesting details, read the author's blog post.
For existing users of this feature:
- Your GPU must now support
WgpuFeatures::SHADER_INT64_ATOMIC_MIN_MAX
to use this feature. As forewarned in the previous release, older GPUs may no longer be compatible. - You must regenerate your MeshletMesh assets. MeshletMesh assets generated in Bevy 0.14 are not compatible with Bevy 0.15.
- Make sure you read both the migration guide and the updated rustdoc for full details on how to upgrade your project.
Animation Masks #
Animations now support masking out animation targets (joints). This is implemented at the level of animation blend graphs (AnimationGraph
) and can be used to play different animations on separate parts of the same model without interfering with one another. For example, you can play separate animations on a character's upper and lower body.
In this video, the fox's head and legs are playing two separate animations, thanks to animation masks:
Generalized Animation #
AnimationClip
can now be used to animate component fields with arbitrary curves.
animation_clip.add_curve_to_target(
animation_target_id,
AnimatableCurve::new(
animated_field!(TextFont::font_size),
// Oscillate the font size during the length of the animation.
FunctionCurve::new(
Interval::UNIT,
|t| 25.0 * f32::sin(TAU * t) + 50.0
)
)
);
This works for any named field and uses the new Curve
API, which supports arbitrary curve types. Animating Transform
fields will likely be the most common use case:
animation_clip.add_curve_to_target(
animation_target_id,
AnimatableCurve::new(
animated_field!(Transform::translation),
// Construct a `Curve<Vec3>`` using a built-in easing curve constructor.
EasingCurve::new(
vec3(-10., 2., 0.),
vec3(6., 2., 0.),
EaseFunction::CubicInOut,
)
)
);
Bevy's internal animation handling for things like GLTF animations uses the same API!
If you need more complicated logic than "animate a specific component field", you can implement AnimatableProperty
, which can be used in AnimatableCurve
in place of animated_field!
.
Animation Graph: Additive Blending #
Bevy's animation graphs (AnimationGraph
), which are used to combine simultaneously playing animations, now support additive blending.
Additive blending is a technique which allows separately authored animations to be applied on top of an arbitrary base animation. For instance, an animation in which a character swings a weapon may be applied additively on top of a walking or running animation.
Within an animation graph itself, this is accomplished by using Add
nodes. The situation above might be described with an animation graph that looks something like this (weights omitted):
┌─────┐
│Walk ┼─┐
└─────┘ │ ┌─────┐
┼─┼Blend┼─┐
┌─────┐ │ └─────┘ │ ┌─────┐ ┌─────┐
│Run ┼─┘ ┼─┤Add ┼───┼Root │
└─────┘ ┌─────┐ │ └─────┘ └─────┘
│Swing┼─┘
└─────┘
The Add
node functions by taking its first input (here, a blend of the 'Walk' and 'Run' clips) as-is and then applying the subsequent inputs additively on top of it. In code, the graph might be constructed as follows:
let mut animation_graph = AnimationGraph::new();
// Attach an `Add` node to the root.
let add_node = animation_graph.add_additive_blend(1.0, animation_graph.root);
// Add the `Blend` node and the additive clip as children; the `Blend` result
// will be used as the base because it is listed first.
let blend_node = animation_graph.add_blend(1.0, add_node);
animation_graph.add_clip(swing_clip_handle, 1.0, add_node);
// Finally, blend the 'Walk' and 'Run' clips to use as a base.
animation_graph.add_clip(walk_clip_handle, 0.5, blend_node);
animation_graph.add_clip(run_clip_handle, 0.5, blend_node);
Animation Events #
Triggering gameplay events at specific points in an animation is a common pattern for synchronizing the visual, audible, and mechanical parts of your game. In Bevy 0.15 we've added "animation event" support to AnimationClip
, which means that you can trigger a specific Event
at a given point in time during AnimationClip
playback:
#[derive(Event, Clone)]
struct PlaySound {
sound: Handle<AudioSource>,
}
// This will trigger the PlaySound event at the 1.5 second mark in `animation_clip`
animation_clip.add_event(1.5, PlaySound {
sound: assets.load("sound.mp3"),
});
app.add_observer(|trigger: Trigger<PlaySound>, mut commands: Commands| {
let sound = trigger.event().sound.clone();
commands.spawn(AudioPlayer::new(sound));
});
You can also trigger events for specific animation targets (such as bones):
animation_clip.add_event_to_target(AnimationTargetId::from_iter(["LeftLeg", "LeftFoot"], 0.5, TouchingGround);
This enables things like "triggering a dust effect each time a foot touches the ground in an animation":
Bevy Remote Protocol (BRP) #
The Bevy Remote Protocol allows the ECS of a running Bevy application to be interacted with remotely. This can be used, for example, to inspect and edit entities and their components at runtime. We anticipate that this will be used to create things like inspectors which monitor the content of the ECS from a separate process. We're planning on using BRP in the upcoming Bevy Editor to communicate with remote Bevy apps.
Currently, you can use BRP to:
- Get the serialized values of a set of components from an entity
- Perform a query for all entities matching a set of components and retrieve the matching values
- Create a new entity with a given set of component values
- For a given entity, insert or remove a set of components
- Despawn an entity
- Reparent one or more entities
- List the components registered in the ECS or present on an entity
Here is the minimal app setup required to use BRP over HTTP:
fn main() {
App::new()
.add_plugins((
DefaultPlugins,
// The "core" plugin, which handles remote requests provided by transports
RemotePlugin::default(),
// Provides remote request transport via HTTP
RemoteHttpPlugin::default(),
))
.run();
}
Here is a sample request:
{
"method": "bevy/get",
"id": 0,
"params": {
"entity": 4294967298,
"components": [
"bevy_transform::components::transform::Transform"
]
}
}
And here is a sample response:
{
"jsonrpc": "2.0",
"id": 0,
"result": {
"bevy_transform::components::transform::Transform": {
"rotation": { "x": 0.0, "y": 0.0, "z": 0.0, "w": 1.0 },
"scale": { "x": 1.0, "y": 1.0, "z": 1.0 },
"translation": { "x": 0.0, "y": 0.5, "z": 0.0 }
}
}
}
Gamepads as Entities #
Gamepads are now represented as entities, which makes them easier to work with! The Gamepad
component provides button and axis state, as well as metadata such as the vendor and product ID. The GamepadSettings
component provides configurable settings for a given Gamepad
, such as deadzones and sensitivity. The name of the gamepad is now stored in Bevy's standard Name
component.
In Bevy 0.14, you might write:
fn gamepad_system(
gamepads: Res<Gamepads>,
button_inputs: Res<ButtonInput<GamepadButton>>,
button_axes: Res<Axis<GamepadButton>>,
axes: Res<Axis<GamepadAxis>>,
) {
for gamepad in &gamepads {
if button_inputs.just_pressed(
GamepadButton::new(gamepad, GamepadButtonType::South)
) {
info!("just pressed South");
}
let right_trigger = button_axes
.get(GamepadButton::new(
gamepad,
GamepadButtonType::RightTrigger2,
))
.unwrap();
if right_trigger.abs() > 0.01 {
info!("RightTrigger2 value is {}", right_trigger);
}
let left_stick_x = axes
.get(GamepadAxis::new(gamepad, GamepadAxisType::LeftStickX))
.unwrap();
if left_stick_x.abs() > 0.01 {
info!("LeftStickX value is {}", left_stick_x);
}
}
}
In 0.15, we can write this much more simply as:
fn gamepad_system(gamepads: Query<&Gamepad>) {
for gamepad in &gamepads {
if gamepad.just_pressed(GamepadButton::South) {
println!("just pressed South");
}
let right_trigger = gamepad.get(GamepadButton::RightTrigger2).unwrap();
if right_trigger.abs() > 0.01 {
info!("RightTrigger2 value is {}", right_trigger);
}
let left_stick_x = gamepad.get(GamepadAxis::LeftStickX).unwrap();
if left_stick_x.abs() > 0.01 {
info!("LeftStickX value is {}", left_stick_x);
}
}
}
Much better!
Box Shadows #
Bevy UI now supports box shadows! Box shadows can be used to achieve particular artistic effects, such as creating a sense of depth to cue to users that an element is interactable.
By adding the new BoxShadow
component to any Node
entity, you can draw a pretty shadow behind your widgets.
#[derive(Component)]
pub struct BoxShadow {
/// The shadow's color
pub color: Color,
/// Horizontal offset
pub x_offset: Val,
/// Vertical offset
pub y_offset: Val,
/// How much the shadow should spread outward.
///
/// Negative values will make the shadow shrink inwards.
/// Percentage values are based on the width of the UI node.
pub spread_radius: Val,
/// Blurriness of the shadow
pub blur_radius: Val,
}
We have plans for future improvements: enable using shadows to create an inset / sunken look, and adding shadow support for images and text. If those possibilities excite you, get involved! We love helping new contributors land the features they care about.
Cosmic Text #
Historically, Bevy has used the ab_glyph
library to render text. This handled simple latin text rendering reasonably well. But Bevy aims to be a generic app framework usable with any language, and there were a number of areas where ab_glyph
wasn't meeting our needs.
The Rust text space has evolved significantly since we selected ab_glyph
. Fortunately there are a number of good options now. We chose cosmic-text
because of its robust feature set and its use in production applications (Iced, Cosmic Desktop, Zed, Lapce, etc). Notably, cosmic-text
gives us support for:
- Font Shaping: The ability to take a string of character codes and perform layout and transformation rules. This can involve moving, modifying, and combining characters (such as ligatures). This is extremely important for non-Latin-based languages.
- System Font Loading: The ability to scan for the available fonts installed on a system and load them.
- Bidirectional Text: Not all languages go from left to right! Cosmic Text gives us support for bidirectional text.
- Text Editing: Cosmic Text has its own internal text editing model, which we can take advantage of.
In Bevy 0.15 we ported our text rendering to cosmic-text
. This was largely an internal change (unlike the other "high level" text API changes this release, such as the port to Required Components).
That being said, you will definitely notice our improved ability to render text! Here is Bevy rendering Arabic text, right-to-left, using the Noto Sans Arabic font:
Note that we haven't yet wired up cosmic-text
's "system font loading" features, but we're working on it!
UI Scrolling #
Bevy 0.15 introduces scrolling support for UI containers.
A UI Node
with the overflow
property set to Overflow::scroll()
will offset its contents by the positive offset_x
and offset_y
values of the ScrollPosition
component on the node.
Scrolling is done by modifying ScrollPosition
directly; there is currently no built-in scroll input handler. A new scroll
example demonstrates handling simple mouse wheel scrolling. Axes of a node without OverflowAxis::Scroll
will ignore changes to ScrollPosition
.
Retained Rendering World #
For awhile now, Bevy has had a "parallel pipelined renderer". To enable this, we added a Render World, in addition to the Main World (a World
holds ECS data like Entities, Components, and Resources). The Main World is the source of truth for app logic. While the Render World is rendering the current frame, the Main World can be simulating the next frame. There is a brief "extract step", where we synchronize the two and copy relevant data from the Main World to the Render World.
In previous versions of Bevy, we employed an "immediate mode" approach to Main World -> Render World synchronization: we fully cleared the Render World entities every frame. This accomplished a couple of things:
- It allowed us to ensure entity IDs "lined up", allowing us to reuse entities in both places.
- It prevented us from needing to solve the "desync problem". By clearing every frame and re-syncing, we ensure the two Worlds are always perfectly in sync.
There was also precedent for the "immediate mode" pipelined rendering approach: Bungie's Destiny renderer uses it to great effect!
However we learned pretty quickly that clearing every frame had major downsides:
- The clearing process itself had overhead.
- "Table" ECS storage could be expensive to rebuild every frame relative to alternatives, due to "archetype moves". As a result, we employed many workarounds such as moving storage outside of the ECS.
- Full resyncs every frame meant re-doing work that didn't need redoing. ECS gives us a nice global view of how our data is changing. We should take advantage of that!
In Bevy 0.15 we switched to a "retained Render World". We no longer clear each frame. We no longer rely on a shared entity ID space. Instead:
- Each world has its own entities
- For entities that are related, we store that relationship as components (ex: Render World entities have a
MainEntity
component and Main World entities have aRenderEntity
component). If a Main World entity withSyncToRenderWorld
is spawned, we spawn an equivalent in the Render World. If a Main World entity is despawned, we despawn the relevant entity in the Render World.
Ensuring synchronization is perfect is not an easy problem. Plugging all of the holes took a lot of time this cycle and we will likely continue to evolve our synchronization strategy in the future. But we think "retained" is fundamentally better for Bevy, and we're excited to have this foundation laid!
Curves #
The new Curve<T>
trait provides a shared interface for curves, describing how values of type T
change as we vary a f32
parameter t
over some domain.
What's changing, and the domain that it's changing over are both incredibly flexible. You might choose to set the generic parameter T
to anything from position, to damage, to colors (like we did to create a powerful abstraction for color gradients).
The progress parameter t
often represents time (like in animation), but it can also represent things like a fraction/percentage of progress between a starting and ending value or distance (such as curves that are mapped into 2D or 3D space),
Constructing Curves
Each curve made be defined in a variety of ways. For example, a curve may be:
- defined by a function
- interpolated from samples
- constructed using splines
- produced by an easing function
Take a look at the constructors on the Curve<T>
trait for more details.
Modifying curves
Procedurally modifying curves is a powerful tool for both creating curves with the desired behavior and dynamically altering them.
Bevy 0.15 provides a number of flexible adaptors for taking an existing curve and modifying its output and/or parametrization.
For example:
let timed_angles = [
(0.0, 0.0),
(1.0, -FRAC_PI_2),
(2.0, 0.0),
(3.0, FRAC_PI_2),
(4.0, 0.0)
];
// A curve interpolating our list of (time, angle)-pairs. At each time, it
// produces the angle, so it is a `Curve<f32>` parametrized over `[0, 4]`.
let angle_curve = UnevenSampleAutoCurve::new(timed_angles).unwrap();
// Interpret these angles as angles of rotation for a `Curve<Rot2>`.
let rotation_curve = angle_curve.map(Rot2::radians);
// Change the parameterizing interval so that the whole loop happens in
// only 1 second instead of 4.
let fast_rotation_curve = rotation_curve.reparametrize_linear(Interval::UNIT).unwrap();
A number of other adaptors are available. For instance:
- a curve may be reversed, repeated, or ping-ponged
- two curves may be chained together to form a longer curve
- two curves may be zipped together to form a curve valued in tuples
Sampling from curves
Sampling is the process of asking "what is the value of this curve at some particular value of t
". To do so, just call Curve::sample
!
Much like how vector graphics can be rasterized into pixels, curves can be rasterized into regular, discretized intervals. By resampling into an approximation derived from sample interpolation on the original curve, we can make curves of diverse origin uniform at the level of data.
While this may seem exotic, this technique is critical for serializing curves or approximating properties via numerical methods.
// A curve defined by a function, which may be challenging to store as data.
let exponential_curve = FunctionCurve::new(
interval(0.0, 10.0).unwrap(),
|t| f32::exp(2.0 * t)
);
// A curve approximating the original by resampling on 100 segments.
// Internally, this just holds the samples and the parameter interval.
let raster_curve = exponential_curve.resample_auto(100).unwrap();
Common Easing Functions #
"Easing functions" can be used to easily construct curves that interpolate between two values. There are many "common" easing functions that each have a different "character" to them. These are often used in "tweening" scenarios to give life to the interpolation.
Bevy 0.15 adds a new Ease
trait, which defines how to interpolate a value of a given type. The Ease
types include:
- vector types (
f32
,Vec2
,Vec3
, ...); - direction types (
Dir2
,Dir3
,Dir3A
); - rotation types (
Rot2
,Quat
).
We've also added an EaseFunction
enum, which defines many common easing functions. The new EasingCurve
type uses these as inputs to define a final Curve
from the given easing parameters.
For example, we can use an easing function to interpolate between two rotations:
// Ease between no rotation and a rotation of angle PI/2 about the y-axis.
let rotation_curve = EasingCurve::new(
Quat::IDENTITY,
Quat::from_rotation_y(FRAC_PI_2),
EaseFunction::ElasticInOut,
)
.reparametrize_linear(interval(0.0, 4.0).unwrap())
.unwrap();
Cyclic Splines #
Most cubic spline constructions now support creating a closed loop instead of just a path, if desired. This can be convenient for constructing things like periodic paths for NPCs or other game entities.
The only difference is that to_curve_cyclic
must be called in place of to_curve
. The supported spline constructions are:
- Hermite splines (
CubicHermite
), - Cardinal splines (
CubicCardinalSpline
), - B-splines (
CubicBSpline
), - Linear splines (
LinearSpline
).
PartialReflect
#
Bevy boasts a powerful reflection system that allows you to introspect and build types at runtime. It works by passing around data as Reflect
trait objects like Box<dyn Reflect>
. This has the effect of erasing the compile-time type information, allowing data to be stored and moved around without having to know the exact type behind the trait object.
Because of this type erasure, bevy_reflect
can also get away with some interesting tricks. For instance, there are many cases where a type needs to be built up field-by-field, such as during deserialization. This works fine when you know the type at compile-time, but it becomes very challenging to do at runtime. To solve this, bevy_reflect
has a concept of dynamic types.
Dynamic types exist as a way to dynamically construct and store reflected data in a way that appears like a concrete type. Behind the scenes, bevy_reflect
uses these types to build up a representation of the target type. And it can do so since we hide the actual type behind the dyn Reflect
trait object.
Unfortunately, this comes with a very common issue: it becomes very easy to accidentally believe a dyn Reflect
is a concrete type when it's actually a dynamic type representing that concrete type.
To address this problem, Bevy 0.15 has reworked the Reflect
trait based on the Unique Reflect RFC. This splits it into two separate traits: Reflect
and PartialReflect
.
PartialReflect
is much like the Reflect
trait of previous versions. It allows access to fundamental reflection capabilities and allows for type-erasure behind a dyn PartialReflect
trait object. It allows for both concrete types and dynamic types to be used interchangeably.
Reflect
, on the other hand, has become a much stricter trait. It's a subset of PartialReflect
that guarantees the underlying type beneath the trait object is exactly the concrete type it says it is.
This split allows reflection-based APIs and user code to be more explicit about the dynamic-ness of the trait objects they're working with. It moves the knowledge of whether a type is dynamic or not to compile-time, preventing many common pitfalls of working with dynamic types, including knowing when they need to be handled separately.
Reflect Remote Types #
The bevy_reflect
crate relies on types implementing Reflect
in order to make them reflectable. Fields of structs and enums that don't implement Reflect
must be specifically ignored with #[reflect(ignore)]
. And due to Rust's orphan rule, this is often the case for types not owned by the current crate.
Following serde
's example, Bevy 0.15 introduces a way to reflect remote types using a new #[reflect_remote(...)]
attribute macro. This allows users to define a model for reflection to base its behavior on, while still operating with the actual type.
// Pretend this type is defined in a crate called `external_crate`
#[derive(Default)]
struct Name {
pub value: String,
}
// We can define our model, including other derives and reflection attributes
#[reflect_remote(external_crate::Name)]
#[derive(Default)]
#[reflect(Default)]
struct NameWrapper {
pub value: String,
}
// Now we can use `Name` as a field in a reflected type without having to ignore it
#[derive(Reflect)]
struct Player {
#[reflect(remote = NameWrapper)]
name: external_crate::Name,
}
Under the hood, this works by transforming our model into a transparent wrapper around the actual type:
#[repr(transparent)]
struct NameWrapper(pub external_crate::Name);
The macro then uses the model to generate all the reflection trait implementations, driven by a new ReflectRemote
trait for swapping between our wrapper and the remote type. Compile-time assertions are also generated to help ensure the model and the actual type stay in sync.
While this feature has many aspects complete, including generic support, enum support, and nesting, there are still some limitations we hope to address in future releases, including support for reflecting a remote type with private fields.
The Reflectable
Trait #
bevy_reflect
is powered by many different traits working together to provide the full reflection API. These include traits like Reflect
, but also other traits like TypePath
, Typed
, and GetTypeRegistration
.
This can make adding the right bounds on generic parameters a bit confusing, and it's easy to forget to include one of these traits.
To make this simpler, 0.15 introduces the Reflectable
trait. All the traits listed above are supertraits of Reflectable
, allowing it to be used in place of all of them where necessary.
Function Reflection #
Rust's options for working with functions in a dynamic context are limited. We're forced to either coerce the function to a function pointer (e.g. fn(i32, i32) -> i32
) or turn it into a trait object (e.g. Box<dyn Fn(i32, i32) -> i32>
).
In both cases, only functions with the same signature (both inputs and outputs) can be stored as an object of the same type. For truly dynamic contexts, such as working with scripting languages or fetching functions by name, this can be a significant limitation.
Bevy's bevy_reflect
crate already removes the need for compile-time knowledge of types through reflection. In Bevy 0.15, functions can be reflected as well!
This feature is opt-in and requires the reflect_functions
feature to be enabled on bevy
(or the functions
feature on bevy_reflect
if using that crate directly).
It works by converting regular functions which arguments and return type derive Reflect
into a DynamicFunction
type using a new IntoFunction
trait.
fn add(a: i32, b: i32) -> i32 {
a + b
}
let function = add.into_function();
With a DynamicFunction
, we can then generate our list of arguments into an ArgList
and call the function:
let args = ArgList::new()
.push_owned(25_i32)
.push_owned(75_i32);
let result = function.call(args);
Calling a function returns a FunctionResult
which contains our Return
data or a FunctionError
if something went wrong.
match result {
Ok(Return::Owned(value)) => {
let value = value.try_take::<i32>().unwrap();
println!("Got: {}", value);
}
Err(err) => println!("Error: {:?}", err),
_ => unreachable!("our function always returns an owned value"),
}
Closure Reflection
This feature doesn't just work for regular functions—it works on closures too!
For closures that capture their environment immutably, we can continue using DynamicFunction
and IntoFunction
. For closures that capture their environment mutably, there's DynamicFunctionMut
and IntoFunctionMut
.
let mut total = 0;
let increment = || total += 1;
let mut function = increment.into_function_mut();
function.call(ArgList::new()).unwrap();
function.call(ArgList::new()).unwrap();
function.call(ArgList::new()).unwrap();
// Drop the function to release the mutable borrow of `total`.
// Alternatively, our last call could have used `call_once` instead.
drop(function);
assert_eq!(total, 3);
FunctionInfo
Reflected functions hold onto their type metadata via FunctionInfo
which is automatically generated by the TypedFunction
trait. This allows them to return information about the function including its name, arguments, and return type.
let info = String::len.get_function_info();
assert_eq!(info.name().unwrap(), "alloc::string::String::len");
assert_eq!(info.arg_count(), 1);
assert!(info.args()[0].is::<&String>());
assert!(info.return_info().is::<usize>());
One thing to note is that closures, anonymous functions, and function pointers are not automatically given names. For these cases, names can be provided manually.
The same is true for all arguments including self
arguments: names are not automatically generated and must be supplied manually if desired.
Using FunctionInfo
, a DynamicFunction
will print out its signature when debug-printed.
dbg!(String::len.into_function());
// Outputs:
// DynamicFunction(fn alloc::string::String::len(_: &alloc::string::String) -> usize)
Manual Construction
For cases where IntoFunction
won't work, such as for functions with too many arguments or for functions with more complex lifetimes, DynamicFunction
can also be constructed manually.
// Note: This function would work with `IntoFunction`,
// but for demonstration purposes, we'll construct it manually.
let add_to = DynamicFunction::new(
|mut args| {
let a = args.take::<i32>()?;
let b = args.take_mut::<i32>()?;
*b += a;
Ok(Return::unit())
},
FunctionInfo::named("add_to")
.with_arg::<i32>("a")
.with_arg::<&mut i32>("b")
.with_return::<()>(),
);
The Function Registry
To make it easier to work with reflected functions, a dedicated FunctionRegistry
has been added. This works similarly to the TypeRegistry
where functions can be registered and retrieved by name.
let mut registry = FunctionRegistry::default();
registry
// Named functions can be registered directly
.register(add)?
// Unnamed functions (e.g. closures) must be registered with a name
.register_with_name("add_3", |a: i32, b: i32, c: i32| a + b + c)?;
let add = registry.get("my_crate::math::add").unwrap();
let add_3 = registry.get("add_3").unwrap();
For better integration with the rest of Bevy, a new AppFunctionRegistry
resource has been added along with registration methods on App
.
The Function
Trait
A new reflection trait—appropriately called Function
—has been added to correspond to functions.
Due to limitations in Rust, we're unable to implement this trait for all functions, but it does make it possible to pass around a DynamicFunction
as a PartialReflect
trait object.
#[derive(Reflect)]
#[reflect(from_reflect = false)]
struct EventHandler {
callback: DynamicFunction<'static>,
}
let event_handler: Box<dyn Struct> = Box::new(EventHandler {
callback: (|| println!("Event fired!")).into_function(),
});
let field = event_handler.field("callback").unwrap();
if let ReflectRef::Function(callback) = field.reflect_ref() {
callback.reflect_call(ArgList::new()).unwrap();
}
Limitations
While this feature is quite powerful already, there are still a number of limitations.
Firstly, IntoFunction
/IntoFunctionMut
only work for functions with up to 16 arguments, and only support returning borrowed data where the lifetime is tied to the first argument (normally self
in methods).
Secondly, the Function
trait can't be implemented for all functions due to how the function reflection traits are defined.
Thirdly, all arguments and return types must have derived Reflect
. This can be confusing for certain types such as &str
since only &'static str
implements Reflect
and its borrowed version would be &&'static str
.
Lastly, while generic functions are supported, they must first be manually monomorphized. This means that if you have a generic function like fn foo<T>()
, you have to create the DynamicFunction
like foo::<i32>.into_function()
.
Most of these limitations are due to Rust itself. The lack of variadics and issues with coherence are among the two biggest difficulties to work around. Despite this, we will be looking into ways of improving the ergonomics and capabilities of this feature in future releases.
We already have a PR up to add support for overloaded functions: functions with a variable number of arguments and argument types.
TypeInfo
Improvements #
With bevy_reflect
, compile-time type information can be retrieved from a reflected type as TypeInfo
.
Bevy 0.15 adds many improvements and convenience methods for working with TypeInfo
.
Generic Parameter Info
The first addition is the ability to get information about a type's generic parameters. This not includes the parameter's type, but also its name and—if it's a const parameter—its default value.
#[derive(Reflect)]
struct MyStruct<T>(T);
let generics = MyStruct::<f32>::type_info().generics();
let t = generics.get(0).unwrap();
assert_eq!(t.name(), "T");
assert!(t.ty().is::<f32>());
assert!(!t.is_const());
Nested TypeInfo
Pretty much every type in Rust is made up of other types. Structs, maps, lists—they all contain other types.
In previous versions of Bevy, TypeInfo
granted you limited access to type information of these nested types. It mostly just provided the type's TypeId
and TypePath
.
However, in Bevy 0.15, you can now directly access the TypeInfo
of these nested types.
#[derive(Reflect)]
struct Row {
id: usize
}
let struct_info: StructInfo = Row::type_info().as_struct();
let field: NamedField = struct_info.field("id").unwrap();
// `NamedField` now exposes a way to fetch the `TypeInfo` of the field's type
let field_info: TypeInfo = field.type_info().unwrap();
assert!(field_info.is::<usize>());
TypeInfo
Convenience Casts
In most cases, TypeInfo
needs to first be pattern matched to the correct variant in order to gain full access to the type's compile-time information. This can be mildly annoying when you already know the variant ahead of time. This often occurs when writing tests, but also shows up when trying to get the type's ReflectRef
data along with its TypeInfo
. It tends to looks something like:
// We have to pattern match on `ReflectRef`...
let ReflectRef::List(list) = reflected_value.reflect_ref() else {
panic!("expected a list");
};
// ...and still need to pattern match on `TypeInfo`
let TypeInfo::List(list_info) = reflected_value.get_represented_type_info().unwrap() else {
panic!("expected a list info");
};
In such cases, the variant is already verified via the ReflectRef
but the TypeInfo
must still be pattern matched regardless.
In Bevy 0.15, convenience methods have been added to TypeInfo
, ReflectRef
, ReflectMut
, and ReflectOwned
to conveniently cast to the expected variant or return an error upon failure.
// We can simply verify the kind of our reflected value once...
let ReflectRef::List(list) = reflected_value.reflect_ref() else {
panic!("expected a list");
};
// ...and just assert the `TypeInfo`
let list_info = reflected_value.get_represented_type_info().unwrap().as_list().unwrap();
If the .as_list()
cast fails in the snippet above, it will return an error detailing what kind we expected (i.e. List
) and what we actually got (e.g. Array
, Struct
, etc.).
And this works in the opposite direction as well:
let TypeInfo::List(list_info) = reflected_value.get_represented_type_info().unwrap() else {
panic!("expected a list info");
};
let list = reflected_value.reflect_ref().as_list().unwrap();
The Type
Type #
Rust's TypeId
is a unique identifier for a type, making it a perfect candidate for use as a key in mappings and for checking whether two types are the same at runtime. And since it's essentially just two u64
values, it's extremely cheap to copy, compare, and hash.
One of the downsides to using TypeId
, though, is that it doesn't contain any other information about the type, including its name. This can make debugging somewhat frustrating as you can't easily tell which type a TypeId
corresponds to.
Since bevy_reflect
makes heavy use of TypeId
, 0.15 introduces a new type to help alleviate the debugging issue while still maintaining the benefits of TypeId
: Type
.
Type
is a simple wrapper around TypeId
that also stores the TypePathTable
. Like TypeId
it's Copy
, Eq
, and Hash
, delegating to the underlying TypeId
for the latter two. But unlike TypeId
, its Debug
implementation will print the type path of the type it represents. This debuggability comes at the cost of an extra 32 bytes, but may often be well worth it, especially if that data would have been stored elsewhere anyway.
It can be constructed from any type that implements TypePath
:
let ty = Type::of::<String>();
let mut map = HashMap::<Type, i32>::new();
map.insert(ty, 25);
let debug = format!("{:?}", map);
assert_eq!(debug, "{alloc::string::String: 25}");
Reflection support for Sets #
Inside of bevy_reflect
, every reflected Rust object ends up being mapped to one of a handful of ReflectKind
variants.
Before Bevy 0.15, sets (like HashSet
) were treated as opaque "values": there was no way to view or modify their contents via reflection. With these changes, we can now properly represent sets of all kinds, which is particularly handy for runtime debugging tools like bevy-inspector-egui
!
Change Detection Source Location Tracking #
Keeping track of when and where values are changed can be tricky in any complex program, and Bevy applications are no exception. Thankfully, our unified ECS-backed data model makes it easy for us to add debugging tools that work right out of the box, with no user configuration required.
When you turn on the track_change_detection
feature flag, Bevy will record the exact line of code that mutated your component or resource side-by-side with the value. While this is obviously too expensive for ordinary use, it's a godsend for debugging tricky issues, as the value can be logged or read directly via the debugger of your choice.
As shown in the change_detection
example, simply turn on the feature and call my_component.changed_by()
on any Ref
, Mut
, Res
or ResMut
smart pointer to get a helpful string pointing you straight to the last line of code that mutated your data!
Optimized Iteration of Mixed Sparse Set and Table Components #
In Bevy, components can be stored using one of two different mechanisms, according to the StorageType
set when implementing the Component
trait.
Table storage is the traditional archetypal ECS storage, where component data is densely packed into tables of raw data with other entities who share the same set of components. By contrast, sparse set storage keeps the component information out of the table, separating entities by archetype (the set of components they have) without fragmenting otherwise shared tables.
As a result of the map-like storage strategy used by sparse set components, they have faster insertion and removal speed, at the cost of slower random-access iteration. This is a reasonable tradeoff, but historically, one that Bevy developers were unlikely to use.
That's because a long-standing bug caused iteration to use the slower, fallback sparse-style iteration if even one of the components in the query or its filters were sparse sets, regardless of whether or not this was necessary. The fix has resulted in query iteration speeds that are between 1.8 and 3.5 times faster (when using parallel iteration) for these scenarios!
Iterating over the data in sparse set components is still relatively slow, but they should finally be a good default choice for any repeatedly inserted or dataless components.
Expose winit's MonitorHandle
#
The new Monitor
component simplifies the process of working with multi-monitor setups by providing easy access to monitor properties such as resolution, refresh rate, position, and scaling factor. This feature is especially useful for developers who need to spawn windows on specific displays, gather monitor details, or adjust their application based on available hardware. This is especially useful for creative setups like multi-projector installations or LED video walls, where precise control over display environments is critical.
Monitor
can be queried for and used for things like spawning or resizing Windows:
fn spawn_windows(
mut commands: Commands,
monitors: Query<(Entity, &Monitor)>,
) {
for (entity, monitor) in monitors_added.iter() {
commands.spawn(Window {
mode: WindowMode::Fullscreen(MonitorSelection::Entity(entity)),
position: WindowPosition::Centered(MonitorSelection::Entity(entity)),
..default()
});
}
}
Custom Cursors #
Previously Bevy's native window cursors supported only a fixed set of built-in OS cursors. Bevy now also supports arbitrary images as "custom cursors". Custom cursors still use native facilities of the OS, which allows them to stay perfectly responsive even when the frame rate of the application drops.
Insert the CursorIcon
component with a CustomCursor
to set a Window
entity's cursor:
commands
.entity(window)
.insert(CursorIcon::Custom(CustomCursor::Image {
handle: asset_server.load("cursor_icon.png"),
hotspot: (5, 5),
}));
Uniform Mesh Sampling #
The surfaces of meshes can now be randomly sampled. This can be used for things like placing scenery or particle effects.
This consists of:
- The
Mesh::triangles
method, which allows the extraction of aMesh
's list of triangles (Triangle3d
). - The
UniformMeshSampler
type, which allows the creation of aDistribution
that uniformly samples points in space (Vec3
) from a collection of triangles.
The functionality comes from putting these together:
let mut rng = StdRng::seed_from_u64(8765309);
// Get an iterator over triangles in the mesh. This can fail if the mesh has
// the wrong format or if its vertex/index data is malformed.
let triangles = my_mesh.triangles().unwrap();
// Construct the distribution. This can fail in some cases - most notably if
// the mesh surface has zero area.
let distribution = UniformMeshSampler::try_new(triangles).unwrap();
// Get 1000 points uniformly sampled from the surface of the mesh.
let samples: Vec<Vec3> = distribution.sample_iter(&mut rng).take(1000).collect();
EventMutator
#
When working with complex event-driven logic, you may find that you want to conditionally modify events without changing their type or re-emitting them. While this has always been possible, it was quite onerous:
// We need to manually track which events this system has read
// using a system-local `EventCursor`, previously called `ManualEventReader`.
fn mutate_events(mut events: ResMut<Events<MyEvent>>, mut local_cursor: Local<EventCursor<MyEvent>>){
for event in local_cursor.read_mut(&mut *events){
event.some_mutation();
}
}
Now, you can simply use the new EventMutator
system param, which keeps track of this bookkeeping for you.
fn mutate_events(mut event_mutator: EventMutator<MyEvent>>){
for event in event_mutator.read(){
event.some_mutation();
}
}
Isometry Types #
Vectors and quaternions are commonly used in 3D to describe relative and absolute positions and orientations of objects. However, when performing more complicated transformations, such as going from a global frame of reference to an object's local space and back, or composing multiple translations and rotations together, they can get rather unwieldy and difficult to reason about.
The new Isometry2d
and Isometry3d
types introduced in Bevy 0.15 are a simple yet powerful tool for efficiently describing these kinds of transformations. An isometry represents a rotation followed by a translation, similar to a Transform
with a scale of 1.
// Create an isometry from a translation and rotation.
let iso1 = Isometry3d::new(Vec3::new(2.0, 1.0, 3.0), Quat::from_rotation_z(FRAC_PI_2));
// Transform a point using the isometry.
let point = Vec3::new(4.0, 4.0, 4.0);
let result = iso1.transform_point(point); // or iso1 * point
assert_relative_eq!(result, Vec3::new(-2.0, 5.0, 7.0));
// Create another isometry.
let iso2 = Isometry3d::from_rotation(Quat::from_rotation_z(FRAC_PI_2));
// Compute the relative translation and rotation.
let relative_iso = iso1.inverse_mul(iso2); // or iso1.inverse() * iso2
Isometries are most useful in mathematical contexts where scaling is not desired, such as when describing relative positions of objects for intersection tests and other geometric queries. However, they are now also used in some APIs, including gizmo methods:
// Specify rectangle position and orientation with an isometry.
gizmos.rect_2d(Isometry2d::new(translation, Rot2::degrees(45.0)), Vec2::splat(250.0), CYAN);
// Many methods take an `impl Into<Isometry3d>`, so it is enough to only provide
// translation or rotation if a full isometry isn't needed.
gizmos.sphere(translation, 1.0, PURPLE);
Transform
and GlobalTransform
can also be converted to an Isometry3d
using the to_isometry
method, providing a convenient way to use these APIs when you already have access to entity transforms.
Note that unlike Transform
, these isometry types are not components. They are purely convenience types for math.
Lifecycle Hook & Observer Trigger for Replaced Values #
Bevy 0.14 introduced Component Lifecycle Hooks and Observers, and included several built-in observer triggers for each way that components could be added to or removed from entities: OnAdd
, OnInsert
and OnRemove
. However, there was a hole in this API. While OnRemove
is a counterpart to OnAdd
, OnInsert
had no such counterpart, meaning certain operations had no corresponding lifecycle hook or observer trigger:
use bevy::{
ecs::component::{ComponentHooks, StorageType},
prelude::{Commands, Component, Deref, DerefMut, Entity, Query, Resource},
utils::HashMap,
};
#[derive(Hash, PartialEq, Eq, Clone, Copy)]
struct SomeId(u32);
#[derive(Resource, Deref, DerefMut)]
struct EntityLookupById(HashMap<SomeId, Entity>);
impl Component for SomeId {
const STORAGE_TYPE: StorageType = StorageType::Table;
fn register_component_hooks(hooks: &mut ComponentHooks) {
hooks
.on_insert(|mut world, entity, _| {
let this = *world.entity(entity).get::<Self>().unwrap();
world
.resource_mut::<EntityLookupById>()
.insert(this, entity);
})
.on_remove(|mut world, entity, _| {
let this = *world.entity(entity).get::<Self>().unwrap();
world.resource_mut::<EntityLookupById>().remove(&this);
});
}
}
fn some_system(mut commands: Commands, query: Query<(Entity, &SomeId)>) {
let mut iter = query.iter();
let Some((a_entity, _)) = iter.next() else {
return;
};
let Some((_, &b_id)) = iter.next() else {
return;
};
commands.entity(a_entity).insert(b_id);
}
In this example, the system inserts a new component value onto an entity that already has one, which overwrites the previous component value. This causes the on_insert
lifecycle hook to run for the new value, but the on_remove
hook doesn't run for the previous value. As a result, the hashmap entry for the previous ID value is still present, even though it has been replaced.
Bevy 0.15 introduces a new component lifecycle hook and observer trigger for this scenario: on_replace
/OnReplace
. This hook runs just before the on_remove
hook in all cases, and additionally runs in the aforementioned scenario where a component value is entirely replaced. The hook runs just before the replacement occurs, letting you access the soon-to-be-dropped value to perform bookkeeping and cleanup.
The above example would be fixed by simply replacing the on_remove
hook with the new on_replace
hook:
21 .resource_mut::<EntityLookupById>()
22 .insert(this, entity);
23 })
-24 .on_remove(|mut world, entity, _| {
+24 .on_replace(|mut world, entity, _| {
25 let this = *world.entity(entity).get::<Self>().unwrap();
26 world.resource_mut::<EntityLookupById>().remove(&this);
27 });
Note that it does not run if a component value is merely mutated - in those cases you want to use change detection instead.
Pack multiple vertex and index arrays together into growable buffers #
Bevy 0.15 changes the way meshes are stored on the GPU to greatly improve CPU performance. Instead of using separate vertex and index buffers for every mesh as is done in Bevy 0.14, now they are coalesced respectively into 'slabs' of configurable size. This cuts down on how frequently we need to change bind groups, winning us up to 2x speedups!
The MeshAllocatorSettings
resource allows tuning slab sizes, growth rate, and cut-offs to best fit your application's needs. The defaults should already be a significant win for most scenes.
WebGL2 does not support packing vertex buffers together, so only index buffers get combined on this platform.
Some measurements on the Bistro scene:
Overall frame time improves from 8.74 ms to 5.53 ms (1.58x speedup) Render system time improves from 6.57 ms to 3.54 ms (1.86x speedup) Opaque pass time improves from 4.64 ms to 2.33 ms (1.99x speedup)
Rewrite screenshots #
Screenshots can now be taken with a new observer based API that allows targeting any RenderTarget
that can be used with a Camera
, not just windows.
// Capture the primary window
commands
.spawn(Screenshot::primary_window())
.observe(save_to_disk(path));
// Or a `Handle<Image>`
commands
.spawn(Screenshot::image(render_target))
.observe(save_to_disk(path));
The observer triggers with a ScreenshotCaptured
event containing an Image that can be used for saving to disk, post-processing, or generating thumbnails. This flexible approach makes it easier to capture content from any part of your rendering pipeline, whether it’s a window, an off-screen render target, or a texture in a custom render pass.
SystemParamBuilder
#
Bevy 0.14 introduced the SystemBuilder
type to allow systems to be created with dynamic queries. In Bevy 0.15, this has been extended to many more types of system parameters!
The SystemBuilder
type has been replaced with a SystemParamBuilder<P>
trait to make it easier to compose builders. Aggregates of parameters, including tuples, ParamSet
, Vec<T>
, and custom parameters using #[derive(SystemParam)]
, can now be used in dynamic systems. For example, a ParamSet<Vec<Query<FilteredEntityMut>>>
can be used to pass a variable number of dynamic queries that may conflict.
New FilteredResources
and FilteredResourcesMut
types can access a set of resources configured at runtime, similar to how the existing FilteredEntityRef
and FilteredEntityMut
access a set of components on one entity.
Finally, a new DynSystemParam
type allows systems to use parameters of dynamic type and then downcast them. This is especially useful for implementing part of a system with trait objects, where each trait implementation can use a different system parameter type.
Taken together, these can be used to build a system that runs a script defined at runtime, where the script needs a variable number of query and resource parameters. Or, they can be used to build systems out of parts assembled at runtime!
fn buildable_system(
query_a: Query<&A>,
query_b: Query<&B>,
queries_with_locals: Vec<(Query<FilteredEntityMut>, Local<usize>)>,
mut dynamic_params: ParamSet<Vec<DynSystemParam>>,
resources: FilteredResourcesMut,
) {
// Parameters in a `ParamSet<Vec>` are accessed by index.
let mut dyn_param_0: DynSystemParam = dynamic_params.get_mut(0);
// Parameters in a `DynSystemParam` are accessed by downcasting to the original type.
let param: Local<&str> = dyn_param_0.downcast_mut::<Local<&str>>().unwrap();
// `FilteredResources` and `FilteredResourcesMut` have methods to get resources by type or by ID.
let res: Ref<R> = resources.get::<R>().unwrap();
}
let param_builder = (
// Parameters that don't need configuration can be built using `ParamBuilder` or its factory methods.
ParamBuilder,
ParamBuilder::query(),
// A `Vec` of parameters can be built using a `Vec` of builders.
vec![
// A tuple of parameters can be built using a tuple of builders.
(
// Queries are built with a callback that supplies a `QueryBuilder` to configure the query.
QueryParamBuilder::new(|builder| { builder.data::<&A>(); }),
// Locals are built by passing the initial value for the local.
LocalBuilder(123),
),
],
// A `ParamSet` can be built for either a tuple or a `Vec`.
ParamSetBuilder(vec![
// A `DynSystemParam` is built using a builder for any type, and can be downcast to that type.
DynParamBuilder::new(LocalBuilder("hello")),
DynParamBuilder::new(ParamBuilder::resource::<R>()),
// The type may be any system parameter, even a tuple or a `Vec`!
DynParamBuilder::new((ParamBuilder::query::<&A>(), ParamBuilder::query::<&B>())),
]),
// `FilteredResources` and `FilteredResourcesMut` are built with a callback
// that supplies a builder to configure the resource access.
FilteredResourcesMutParamBuilder::new(|builder| { builder.add_read::<R>(); }),
);
let system = param_builder
.build_state(&mut world)
.build_system(buildable_system);
// The built system is just like any other system, and can be added to a schedule.
schedule.add_systems(system);
State Scoped Events #
State scoped events will be automatically cleared when exiting a state (similar to StateScoped entities). This is useful when you want to guarantee clean state transitions.
Normally, you would configure your event via:
fn setup(app: &mut App) {
app.add_event::<MyGameEvent>();
}
If you want the events to be cleared when you exit a specific state, change this to:
fn setup(app: &mut App) {
app.add_state_scoped_event::<MyGameEvent>(GameState::Play);
}
EntityRefExcept
and EntityMutExcept
#
EntityMut
and EntityRef
are powerful tools for interacting with all components of a given entity at once in arbitrary ways. These types implement QueryData
, so you can add them to any Query
you'd like!
However, because they can access any component information, Rust's prohibition against mutable aliasing prevent you from simultaneously accessing other component information, even if you pinky promise not to read any data that's being written to.
// This system is forbidden!
//
// Inside the body of the function, we could choose to mutate the `AnimationPlayer` itself
// while reading its value!
fn animate_anything(query: Query<(&AnimationPlayer, EntityMut)> ){}
To let you work around this limitation, we've introduced a matching pair of tools: EntityMutExcept
and EntityRefExcept
, which work just like the EntityMut
and EntityRef
but don't provide access to a bundle of components that you declare off-limits.
/// Look mom, no mutable aliasing!
fn animate_anything(query: Query<(&AnimationPlayer, EntityMutExcept<AnimationPlayer>)> ){}
Cached One-shot Systems #
Bevy 0.15 introduces a convenient new "cached" API for running one-shot systems:
// Old, uncached API:
let foo_id = commands.register_system(foo);
commands.run_system(foo_id);
// New, cached API:
commands.run_system_cached(foo);
This allows you to call register_system_cached
without needing to worry about producing duplicate systems.
// Uncached API:
let id1 = world.register_system(quux);
let id2 = world.register_system(quux);
assert!(id1 != id2);
// Cached API:
let id1 = world.register_system_cached(quux);
let id2 = world.register_system_cached(quux);
assert!(id1 == id2);
Comparison to run_system_once
run_system_once
sets up a system, runs it once, and tears it down. This means system parameters like Local
and EventReader
that rely on persistent state between runs will be lost. Any system parameters like Query
that rely on cached computations to improve performance will have to rebuild their cache each time, which can be costly. As a consequence, run_system_once
is only recommended for diagnostic use (e.g. unit tests), and run_system
or run_system_cached
should be preferred for "real" code.
Limitations
With the cached API, different systems cannot be cached under the same CachedSystemId<S>
. There can be no more than one distinct system of type S
. This is true when size_of::<S>() == 0
, which is almost always true in practice. To enforce correctness, the new API will give you a compile-time error if you try to use a non-zero-sized function (like a function pointer or a capturing closure).
Fallible System Parameters #
In Bevy 0.14 and prior, the following code would panic:
#[derive(Resource)]
struct MyResource;
fn my_system(my_resource: Res<MyResource>) {}
fn main() {
let mut app = App::new();
app.add_plugins(DefaultPlugins)
app.add_systems(my_system);
// Panic here: `my_system` cannot fetch `MyResource`, because it was never added.
app.run();
}
but in Bevy 0.15, my_system
simply won't be executed and a warning will be logged.
This works for all system-based features:
- Systems and Observers will be skipped.
- Run Conditions will be skipped and return
false
.
Compound systems, like system_a.pipe(system_b)
, are currently skipped if any required data is missing.
Pre-existing parameters which now benefit from this feature are: Res
and ResMut
as well as their siblings NonSend
and NonSendMut
. Parameters that build on top of other parameters: tuples, DynSystemParam
and ParamSet
are considered present if and only if all of their system parameters are present.
Additionally, few new system params were introduced to simplify existing code:
Single<D, F>
- Works likeQuery<D, F>::single
, fails if query contains 0 or more than 1 match,Option<Single<D, F>>
- Works likeQuery<D, F>::single
, fails if query contains more than 1 match,Populated<D, F>
- Works like aQuery<D, F>
, fails if query contains no matches.
Warnings
Fallible system params come with a primitive warning mechanic. Currently, systems can behave in one of two ways:
- (default) warn exactly once,
- never warn.
The default can be changed as following:
// For systems
app.add_systems(my_system.never_param_warn());
// For observers
app.add_observer(my_observer.never_param_warn());
// For run conditions
app.add_systems(my_system.run_if(my_condition.never_param_warn()));
Let us know what other warning strategies you'd like!
Passing Data Into Systems By Reference #
System piping is a powerful (if relatively niche) tool to pass data directly from one system to another. While this is useful for error handling, it's a general purpose tool for composing fragments of logic by gluing together matching inputs and outputs.
This machinery has since been repurposed for use with one-shot systems, allowing you to call World::run_system_with_input
to evaluate systems with whatever input you supply, and get the return value back out. Great for writing tests!
However, this set of tools has always had a frustrating and confusing limitation: any data passed into a system must have a static lifetime. This seems absurd; the data is passed directly from one owner to the next and the systems are run as if they were a single unit.
With the liberal application of some type magic pixie dust, this limitation has been lifted!
let mut world = World::new();
let mut value = 2;
// This always worked:
fn square(In(input): In<usize>) -> usize {
input * input
}
value = world.run_system_with_input(value, square);
// Now possible:
fn square_ref(InRef(input): InRef<usize>) -> usize {
*input * *input
}
value = world.run_system_with_input(&value, square_ref);
// Mutably:
fn square_mut(InMut(input): InMut<usize>) {
*input *= *input;
}
world.run_system_with_input(&mut value, square_mut);
We're excited to see what you do with this newfound power.
List Components in QueryEntityError::QueryDoesNotMatch #
When accessing an entity through a query fails due to mismatched components, the error now includes the names of the components the entity has:
QueryDoesNotMatch(0v1 with components Sprite, Transform, GlobalTransform, Visibility, InheritedVisibility, ViewVisibility, SyncToRenderWorld)
no_std
Progress #
Bevy relies heavily on Rust's standard library, making it challenging to use on embedded, niche platforms, and even certain consoles. But what if that wasn't the case?
We've undertaken a new initiative to challenge the reliance on the standard library, with the eventual goal of providing a no_std
compatible subset of Bevy which could be used on a much wider range of platforms.
The first very simple step is to enable a new set of lints:
For those unfamiliar with no_std
Rust, the standard library, std
, gets a lot of its functionality from two smaller crates, core
and alloc
. The core
crate is available on every Rust target with very few exceptions, providing the fundamental infrastructure that the Rust language relies on, such as iterators, Result
, and many more. Complementing that the alloc
crate provides access to allocation-related functionality, such as Vec
, Box
, and String
.
Rust's support for platforms follows a three tiered policy, where tier 1 is guaranteed to work and will always provide the std
crate, and tiers 2 and 3 may have the std
crate, but often do not. The reason for this is some platforms simply don't support the features the std
crate requires, such as a filesystem, networking, or threading.
But why should Bevy care about these platforms? When a new platform is added to Rust, it is often lacking tier 1 support. Even modern consoles such as the Nintendo Switch, PlayStation 5, or Xbox Series don't have tier 1 support due to non-disclosure agreements and platform specifics. Adding no_std
support to Bevy will make it easier for commercial teams developing for these platforms to get started and stay up to date.
Beyond the commercially-relevant modern consoles, there is a vibrant community of embedded and retro enthusiasts developing for platforms that may never support the standard library. Crates such as agb
and psx
provide support for developing games on the GameBoy Advance and PlayStation One respectively. With no_std
support in Bevy, users may be able to leverage the wider Rust ecosystem to run their software on these platforms.
We're still a while away from true no_std
support in Bevy, but the first few changes have already been accepted, with many more lined up for the next release in 0.16.
If this work sounds interesting, check out the no_std
tracking issue on GitHub, where you can find a list of pull requests, and even prototypes of Bevy running in no_std
environments.
GltfMaterialName
Component #
The glTF 3D model file format allows a single mesh to be associated with multiple materials. For example, a teapot may consist of a single mesh, yet each part may have a different material. When a single mesh is assigned multiple materials, it is divided into several primitive nodes, with each primitive assigned a unique material.
{
"meshes": [
{
"name": "Cube",
"primitives": [
{
"attributes": { "POSITION": 0, "NORMAL": 1, "TEXCOORD_0": 2 },
"indices": 3,
"material": 0
},
{
"attributes": { "POSITION": 4, "NORMAL": 5, "TEXCOORD_0": 6 },
"indices": 7,
"material": 1
},
{
"attributes": { "POSITION": 8, "NORMAL": 9, "TEXCOORD_0": 10 },
"indices": 11,
"material": 2
},
{
"attributes": { "POSITION": 12, "NORMAL": 13, "TEXCOORD_0": 14 },
"indices": 15,
"material": 3
}
]
}
]
}
In Bevy 0.14 and before, these primitives are named using the format "Mesh.Index", which complicates querying. A new component GltfMaterialName is now added to each primitive node that has a material, letting you quickly look up the primitive by using this component with the material name.
fn find_top_material_and_mesh(
mut materials: ResMut<Assets<StandardMaterial>>,
mut meshes: ResMut<Assets<Mesh>>,
time: Res<Time>,
mesh_materials: Query<(
&MeshMaterial3d<StandardMaterial>,
&Mesh3d,
&GltfMaterialName,
)>,
) {
for (mat_handle, mesh_handle, name) in &mesh_materials {
// locate the material and associated submesh by name
if name.0 == "Top" {
if let Some(material) = materials.get_mut(mat_handle) {
// ...
}
if let Some(mesh) = meshes.get_mut(mesh_handle) {
// ...
}
}
}
}
GPU Readback #
The new Readback
component simplifies the tricky process of getting data back from the GPU to the CPU using an observer-based API.
commands.spawn(Readback::buffer(buffer.clone())).observe(
|trigger: Trigger<ReadbackComplete>| {
let data = trigger.event().to_shader_type();
// ...
},
);
Normally, manually retrieving data from the GPU involves a lot of boilerplate and careful management of GPU resources. You have to deal with synchronization, ensure the GPU has finished processing, and handle copying data between memory spaces—which isn’t straightforward!
The new Readback
component streamlines this process. When spawned into the main world, Readback
will queue a Handle<Image>
or Handle<ShaderStorageBuffer>
to be asynchronously read and copied back from the GPU to CPU in a future frame where it will trigger a ReadbackComplete
event containing the raw bytes of the resource.
This is especially useful for debugging, saving GPU-generated data, or performing CPU-side computations with results from the GPU. It’s perfect for scenarios where you need to analyze simulation data, capture rendered frames, or process large datasets on the GPU and retrieve the results for further use on the CPU.
Android: Configurable GameActivity
and NativeActivity
#
Bevy now uses GameActivity
as the default Activity
for Android projects, replacing NativeActivity
. NativeActivity
is still available, but has been placed behind a feature flag.
This change updates Bevy to a more modern Android stack, and includes an SDK minimum version bump to PlayStore's current version requirement. We've also switched to a cargo-ndk
based build, which gives us more control by default. Gradle projects for both GameActivity
and NativeActivity
are provided.
GameActivity
brings with it improvements to game interaction (SurfaceView
rendering, improved touch and input handling), more frequent updates, and access to other parts of the JetPack ecosystem. It is better placed to integrate with Rust code without excessive JNI wrangling. You can read more about GameActivity
here.
Reflection Serialization Improvements #
Serialization with registry context
bevy_reflect
provides a way to easily serialize and deserialize nearly any type that implement Reflect
. It does so by relying purely on the reflection APIs and the TypeRegistry
, without having to know the type at compile-time.
However, sometimes serialization/deserialization for a type requires more explicit control. In such cases, a custom Serialize
/Deserialize
implementation can be provided by registering the ReflectSerialize
/ReflectDeserialize
type data for the type in the TypeRegistry
.
This approach generally works well enough for most cases. However, sometimes you want to handle the case for your type alone and continue using reflection for the rest of the fields. For example, you might want to serialize your type as a map that includes a few extra entries, but you still want to use the reflection serializer for each value.
Unfortunately, not only does this not nest well within serializers, but it also means you need to manually capture a reference to the TypeRegistry
so that you can pass it down to the nested reflection serializers. What this basically means is that you can't use custom logic along with reflection-based serialization.
Thankfully, Bevy 0.15 introduces the SerializeWithRegistry
and DeserializeWithRegistry
traits, which work much like Serialize
and Deserialize
but with an additional TypeRegistry
parameter. This allows you to perform your custom logic while still being able to continue using reflection for the rest.
impl SerializeWithRegistry for MyType {
fn serialize<S>(&self, serializer: S, registry: &TypeRegistry) -> Result<S::Ok, S::Error>
where
S: Serializer,
{
let state = serializer.serialize_map(None)?;
// ...custom logic...
state.serialize_entry(
"data",
// Continue using reflection-based serialization
&ReflectSerializer::new(
self.data,
registry,
),
)?;
state.end()
}
}
With your custom serialization and deserialization logic in place, you can then register the ReflectSerializeWithRegistry
and ReflectDeserializeWithRegistry
type data for your type to have the reflection serializer/deserializer make use of your custom logic for all instances of your type.
Reflect de/serializer processors
Alongside SerializeWithRegistry
and DeserializeWithRegistry
, a new tool has been added for users who use the reflect machinery for de/serialization. When using the ReflectSerializer
or ReflectDeserializer
, you can now use with_processor
and pass in a de/serializer processor. This processor allows you to override the de/serialization logic for specific values and specific types, while also capturing any context you might need inside the processor itself.
The motivating example for this is being able to deserialize Handle<T>
s properly inside an asset loader when reflect-deserializing. Let's imagine that we have an asset that looks like this:
#[derive(Debug, Clone, Reflect)]
struct AnimationGraph {
nodes: Vec<Box<dyn AnimationNode>>,
}
trait AnimationNode: Send + Sync + Reflect { /* .. */ }
#[derive(Debug, Clone, Reflect)]
struct ClipNode {
clip: Handle<AnimationClip>
}
impl AnimationNode for ClipNode { /* .. */ }
#[derive(Debug, Clone, Reflect)]
struct AdjustSpeedNode {
speed_multiplier: f32,
}
impl AnimationNode for AdjustSpeedNode { /* .. */ }
(
animation_graph: (
nodes: [
{
"my_app::animation::node::ClipNode": (
clip: "animations/run.anim.ron",
)
},
{
"my_app::animation::node::AdjustSpeedNode": (
speed_multiplier: 1.5,
)
}
]
)
)
When we write an AssetLoader
for this AnimationGraph
, we have access to a &mut LoadContext
which we can use to start new asset load operations, and get a Handle
to that asset. We can also use the existing ReflectDeserializer
to deserialize Box<dyn AnimationNode>
s. However, when the deserializer encounters a Handle<AnimationClip>
, this will be deserialized as Handle::default
and no asset load will be kicked off, making the handle useless.
With a ReflectDeserializerProcessor
, we can pass in a processor which captures the &mut LoadContext
and, if it encounters a Handle<T>
, it will kick off an asset load for T
, and assigns the result of that load to the field it's deserializing.
struct HandleProcessor<'a> {
load_context: &'a mut LoadContext,
}
impl ReflectDeserializerProcessor for HandleProcessor<'_> {
fn try_deserialize<'de, D>(
&mut self,
registration: &TypeRegistration,
_registry: &TypeRegistry,
deserializer: D,
) -> Result<Result<Box<dyn PartialReflect>, D>, D::Error>
where
D: Deserializer<'de>,
{
let Some(reflect_handle) = registration.data::<ReflectHandle>() else {
// we don't want to deserialize this - give the deserializer back
// and do default deserialization logic
return Ok(Err(deserializer));
};
let asset_type_id = reflect_handle.asset_type_id();
let asset_path = deserializer.deserialize_str(AssetPathVisitor)?;
let handle: Handle<LoadedUntypedAsset> = self.load_context
.loader()
.with_dynamic_type(asset_type_id)
.load(asset_path);
Ok(Box::new(handle))
}
}
Combined with ReflectSerializerProcessor
, this can be used to round-trip Handle
s to/from string asset paths.
The processors take priority over all other serde logic, including De/SerializeWithRegistry
, so it can be used to override any reflect serialization logic.
Contextual Serialization Errors
Sometimes when working with the reflection serializer and deserializer, it can be difficult to track down the source of an error. Since we can't tell whether a type can be serialized or not until runtime, an un-serializable type might slip into a type that is supposed to be serializable.
In Bevy 0.15, a new default debug
feature has been added to the bevy_reflect
crate, which allows the serializers and deserializers to retain contextual information in order to provide the type's "stack" when an error occurs.
These messages can be used to more easily track down the source of the error:
type `bevy_utils::Instant` did not register the `ReflectSerialize` type data. For certain types, this may need to be registered manually using `register_type_data` (stack: `bevy_time::time::Time<bevy_time::real::Real>` -> `bevy_time::real::Real` -> `bevy_utils::Instant`)
Simplified Multi-Entity Access #
When using some of the more advanced features of Bevy's ECS, like hooks or exclusive systems, it's common to want to fetch entities straight out of a World
:
#[derive(Component)]
#[component(on_add = on_foo_added)]
struct Foo;
fn on_foo_added(world: DeferredWorld, entity: Entity, _: ComponentId) {
let has_foo = world.entity(entity);
println!("{:?} has a Foo", has_foo.id());
}
In previous versions of Bevy, you could grab multiple entities from a World
using a variety of different functions:
World::many_entities<N>(&self, [Entity; N]) -> [EntityRef; N]
World::many_entities_mut<N>(&mut self, [Entity; N]) -> [EntityMut; N]
World::get_many_entities<N>(&self, [Entity; N]) -> Result<[EntityRef; N], Entity>
World::get_many_entities_dynamic(&self, &[Entity]) -> Result<Vec<EntityRef>, Entity>
World::get_many_entities_mut<N>(&mut self, [Entity; N]) -> Result<[EntityMut; N], QueryEntityError>
World::get_many_entities_dynamic_mut(&self, &[Entity]) -> Result<Vec<EntityMut>, QueryEntityError>
World::get_many_entities_from_set_mut(&mut self, &EntityHashSet) -> Result<Vec<EntityMut>, QueryEntityError>
As you can see, that's a lot of functions with very long names! But the gist of them is that we want to support the ability to give a bunch of entity IDs, and receive a bunch of entity references. Surely there's a better way!
In 0.15
, all of those functions have been deprecated and now all you need is the panicking World::entity
/World::entity_mut
or the non-panicking World::get_entity
/World::get_entity_mut
:
let e1: Entity = world.spawn_empty().id();
let e2: Entity = world.spawn_empty().id();
// Note: use World::get_entity or World::get_entity_mut instead to receive a Result
// You can still pass a single ID as normal:
let eref = world.entity(e1);
let emut = world.entity_mut(e1);
// But you can also pass in an array of IDs (any amount N supported!):
let [eref1, eref2]: [EntityRef; 2] = world.entity([e1, e2]);
let [emut1, emut2]: [EntityMut; 2] = world.entity_mut([e1, e2]);
// Or a slice of IDs:
let ids = vec![e1, e2];
let eref_vec: Vec<EntityRef> = world.entity(&ids);
let emut_vec: Vec<EntityMut> = world.entity_mut(&ids);
// Or even a set of IDs:
let ids = EntityHashSet::from_iter([e1, e2]);
let eref_map: EntityHashMap<EntityRef> = world.entity(&ids);
let emut_map: EntityHashMap<EntityMut> = world.entity_mut(&ids);
It might feel like magic, but it's all standard Rust code! The Entity
id parameter that the World::entity
family of functions accept was changed to instead accept anything that implements a newly introduced trait: WorldEntityFetch
. Check out the trait and World::entity
to learn more about how it was accomplished.
Hierarchy Traversal Tools #
We've spruced up the HierarchyQueryExt
extension trait, making it easier to traverse entity hierarchies defined by the Parent
and Children
components.
The full set of methods is now:
parent
(new)children
(new)root_ancestor
(new)iter_leaves
(new)iter_siblings
(new)iter_descendants
iter_descendants_depth_first
(new)iter_ancestors
All of these operations were previously possible, but we hope that this API makes working with hierarchies more pleasant, especially for UI and animation.
Shader Storage Buffer Asset #
A new asset ShaderStorageBuffer
has been added to simplify working with storage buffers in custom materials and compute shaders. Storage buffers are large, GPU-accessible memory buffers designed for storing data that can be read from or written to by shaders. Unlike smaller, more restricted uniform buffers, storage buffers allow you to store large amounts of data, making them perfect for general purpose tasks where large datasets need to be processed. Examples include managing complex data in physics simulations (like particle systems), holding the transformation data for thousands of objects in a scene, or storing procedural geometry information for dynamic terrain generation. Storage buffers are particularly useful when different stages of the rendering pipeline (such as compute shaders and rendering passes) need to share and update large amounts of data efficiently.
#[derive(Asset, TypePath, AsBindGroup, Debug, Clone)]
struct CustomMaterial {
#[storage(0, read_only)]
colors: Handle<ShaderStorageBuffer>,
}
fn setup(
mut buffers: ResMut<Assets<ShaderStorageBuffer>>,
mut materials: ResMut<Assets<CustomMaterial>>,
) {
// Example data for the storage buffer
let color_data: Vec<[f32; 4]> = vec![
[1.0, 0.0, 0.0, 1.0],
[0.0, 1.0, 0.0, 1.0],
[0.0, 0.0, 1.0, 1.0],
[1.0, 1.0, 0.0, 1.0],
[0.0, 1.0, 1.0, 1.0],
];
let colors = buffers.add(ShaderStorageBuffer::from(color_data));
// Create the custom material with the storage buffer
let custom_material = CustomMaterial { colors };
materials.add(custom_material);
}
By declaring Handle<ShaderStorageBuffer>
on the material using AsBindGroup
, this buffer can now be accessed in the shader:
@group(2) @binding(0) var<storage, read> colors: array<vec4<f32>, 5>;
Accumulated Mouse Inputs #
"How much has the player moved their mouse this frame" is a natural question for games when the player is trying to aim or scroll a map. Unfortunately, the operating system, and thus winit
, only provides us with a stream of events, in the form of individual MouseMotion
events.
To get the summarized information (and the equivalent MouseScroll
) information that most game systems care about, you had to sum them yourself.
pub fn accumulate_mouse_motion_system(
mut mouse_motion_event: EventReader<MouseMotion>,
mut accumulated_mouse_motion: ResMut<AccumulatedMouseMotion>,
) {
let mut delta = Vec2::ZERO;
for event in mouse_motion_event.read() {
delta += event.delta;
}
accumulated_mouse_motion.delta = delta;
}
Bevy now does this for you, exposed in the new AccumulatedMouseMotion
and AccumulatedMouseScroll
resources.
Stable Interpolation and Smooth Following #
When animating cameras or programming unit AI (not that kind of AI!), moving something continuously towards a target is an essential basic operation. Simply lerping to the target seems easy enough, but as Freya Holmer explains, making sure that this interpolation is timestep independent is both vital and surprisingly tricky.
We've done the math for you; you just need to use the StableInterpolate
trait's interpolate_stable
and smooth_nudge
methods and tune the decay_rate
parameter to really optimize your game feel. Fear not: it even works on quaternions! Stable, smooth camera controllers have never been easier.
What's Next? #
The features above may be great, but what else does Bevy have in flight? Peering deep into the mists of time (predictions are extra hard when your team is almost all volunteers!), we can see some exciting work taking shape:
- Bevy Scene Notation: Required components mark the first step on Cart's master plan for BSN. Over the next few months, he's going to be heads-down developing a Bevy-specific file format (complete with matching macro and IDE support), the
Construct
trait (to easily include asset data in scenes), patches (to layer modifications to scenes) and experimenting with approaches to reactivity for UI. - Better font support: While
cosmic_text
is a huge leap forward for text shaping and rendering, our approach to handling fonts and type-faces is still quite crude. Bidirectional text, working with system fonts, a convenient Markdown-style "bold this section of the text" API, font fallback and more are planned. - Picking-Powered UI Interaction:
bevy_picking
introduces a much more powerful and expressive way to handle pointer interactions, but we're not leveraging its full power withinbevy_ui
itself. While picking events are great, a single source of truth for "what's the user doing with this button" is vital for responsive widget styling. bevy_lint
: Try as we might, it is possible to misuse Bevy's API! As part of a broaderbevy_cli
project, the Bevy community has developed a Bevy-specific linter to catch common mistakes or hazards and are looking for early adopters to try it out!- Focus abstraction: Keeping track of which UI element is focused is vital to allow users of screen readers, gamepads and keyboards to comfortably navigate the UI. We're planning to build on our success with
bevy_picking
and develop a complementary focus-tracking solution, along with a few simple backends to opt-in to keyboard or gamepad-based UI navigation. - Immutable components: Component hooks and observers are really powerful for responding to changes and upholding invariants, but they're easily bypassed by simply mutating the component. The mad science crew has been experimenting with a way to opt-out of direct mutation, opening the door to more robust hierarchies, complex observer-powered reactions and a first-party component indexing solution.
- Actually Retained Rendering: While the render world is technically retained in Bevy 0.15, most of our existing code still spawns and despawns entities every frame to reduce the risk of introducing bugs during the migration. We're looking forward to gradually changing this and profiling the performance impact!
no_std
Bevy: To better support weird platforms (like the Playdate!) and make life easier for devs experimenting with Bevy on modern consoles, we've been working towards ensuring that (much of) Bevy can compile and run without Rust's standard library.
Support Bevy #
Bevy will always be free and open-source, but it isn't free to make! Because Bevy is free, we rely on the generosity of the Bevy community to fund our efforts. If you are a happy user of Bevy or you believe in our mission, please consider donating to the Bevy Foundation... every bit helps!
Contributors #
A huge thanks to the 294 contributors that made this release (and associated docs) possible! In random order:
- @Gingeh
- @cBournhonesque
- @benfrankel
- @SarthakSingh31
- @ItsDoot
- @kornelski
- @NiklasEi
- @adrylain
- @TrialDragon
- @lubomirkurcak
- @BD103
- @eero-lehtinen
- @TheDudeFromCI
- @PPakalns
- @mirsella
- @seabassjh
- @Bluefinger
- @mamekoro
- @K-JBoon
- @jatimix
- @esensar
- @Sigma-dev
- @kristoff3r
- @jpetkau
- @rewin123
- @Jondolf
- @mrchantey
- @aevyrie
- @mockersf
- @lee-orr
- @barsoosayque
- @Azorlogh
- @databasedav
- @tychedelia
- @DataTriny
- @bugsweeper
- @jakeswenson
- @awtterpip
- @recatek
- @fernanlukban
- @ActuallyHappening
- @doup
- @Shatur
- @mahkoh
- @dmyyy
- @Victoronz
- @freiksenet
- @Litttlefish
- @dragostis
- @papow65
- @rudderbucky
- @dmlary
- @DasLixou
- @doonv
- @Lubba-64
- @long-long-float
- @theredfish
- @LiamGallagher737
- @luca-della-vedova
- @soqb
- @NiseVoid
- @SludgePhD
- @pcwalton
- @simbleau
- @bes
- @Trashtalk217
- @clarfonthey
- @johannesvollmer
- @fluffiac
- @miniex
- @Zeenobit
- @coreh
- @sampettersson
- @villor
- @hut
- @ickk
- @vladinator1000
- @NWPlayer123
- @AndrewDanial
- @nealtaylor98
- @andristarr
- @gagnus
- @flash-freezing-lava
- @ickshonpe
- @Aztro-dev
- @NthTensor
- @cactusdualcore
- @Cioraz
- @SOF3
- @spacemen0
- @LucDrenth
- @bytemunch
- @Dimchikkk
- @LikeLakers2
- @djeedai
- @wiggleforlife
- @IsseW
- @CupOfTeaJay
- @johanhelsing
- @oceantume
- @foxzool
- @SpecificProtagonist
- @Dentosal
- @Wcubed
- @torsteingrindvik
- @tbillington
- @GuillaumeGomez
- @james-j-obrien
- @JohnTheCoolingFan
- @bushrat011899
- @Vrixyz
- @blazepaws
- @Chubercik
- @s-puig
- @ecoskey
- @nilsiker
- @Katsutoshii
- @ChosenName
- @dpeke
- @chendaohan
- @Elabajaba
- @mgi388
- @hamirmahal
- @nixpulvis
- @AFKessen
- @jnhyatt
- @dav-wolff
- @dependabot[bot]
- @Nilirad
- @imrn99
- @YohDeadfall
- @thebluefish
- @bash
- @nicoburns
- @VitalyAnkh
- @atornity
- @SkiFire13
- @JMS55
- @Octorine
- @theotherphil
- @eidloi
- @FastestMolasses
- @tom-frantz
- Mason Kramer
- @hooded-shrimp
- @thatchedroof
- @mintlu8
- @brandon-reinhart
- @hymm
- @StarArawn
- @venhelhardt
- @re0312
- @crvarner
- @13ros27
- @Luminoth
- @Luracasmus
- @Wuketuke
- @AlexanderStein
- @IceSentry
- @zeux
- @mweatherley
- @Henauxg
- @icorbrey
- @m-edlund
- @joshka
- @wduminy
- @JoNil
- @JoJoJet
- @alphastrata
- @Earthmark
- @Kanabenki
- @Kees-van-Beilen
- @BigWingBeat
- @MiniaczQ
- @cart
- @akimakinai
- @CrazyRoka
- @james7132
- @Jenya705
- @Soulghost
- @NotAFile
- @aecsocket
- @jrobsonchase
- @Brezak
- @CrazyboyQCD
- @Friz64
- @tjlaxs
- @masonk
- @janhohenheim
- @callym
- @lynn-lumen
- @chronicl
- WX\shixi
- @moOsama76
- @rparrett
- @EdJoPaTo
- @jirisvd
- @UkoeHB
- @GauravTalreja
- @tigregalis
- @ayamaev-se
- @MonaMayrhofer
- @ndarilek
- @grind086
- @andresovela
- @notmd
- @mrtracy
- @Coder-Joe458
- @StrikeForceZero
- @wilk10
- @kivi
- @mrgzi
- @ramon-bernardo
- @chescock
- @killercup
- @msvbg
- @LoweredgamesDev
- @DragonGamesStudios
- Luc Drenth
- @tomi-font
- @nicopap
- @navneetankur
- @MarcoMeijer
- @brianreavis
- @ChristopherBiscardi
- @no-materials
- @MScottMcBee
- @rafalh
- @Nihilistas
- @komadori
- @nsarlin
- @targrub
- @tomara-x
- @andriyDev
- @chompaa
- @alice-i-cecile
- @inodentry
- @ThomasAlban
- @HackerFoo
- @atlv24
- @Shfty
- @TotalKrill
- @RobWalt
- @Adamkob12
- @BenjaminBrienen
- @FarmingtonS9
- @richchurcher
- @roblesch
- @Piefayth
- @Dokkae6949
- @pablo-lua
- @KirmesBude
- @daxpedda
- @robtfm
- @Wiwip
- @JJJimbo1
- @MrGVSV
- @tim-blackbird
- @therealbnut
- @superdump
- @patrickariel
- @AllenPocketGamer
- @Neo-Zhixing
- @kettei-sproutty
- @ldubos
- @IQuick143
- @mnmaita
- @WillTheCodeWork
- @futile
- @CatThingy
- @maslabgamer
- @ShoyuVanilla
- @Shadowcat650
- @yrns
- @shanecelis
- @extrawurst
- @Stanleeeeey
- @JaySpruce
- @jdm
- @hexroll
- @matiqo15
- @Sorseg
- @TurtIeSocks
- @eckz
- @stepancheg
- Fernan Lukban
- @kumikaya
- @chrisjuchem
- @jgayfer
- @BlakeBedford
- @cryscan
- @Niashi24
- @urben1680
- @BobG1983
Full Changelog #
The changes mentioned above are only the most appealing, highest impact changes that we've made this cycle. Innumerable bug fixes, documentation changes and API usability tweaks made it in too. For a complete list of changes, check out the PRs listed below.
Accessibility #
Accessibility + Reflection #
Accessibility + Rendering #
Accessibility + UI #
Animation #
Make the component types of the new animation players clonable.
Added
get_main_animation
forAnimationTransitions
Deprecate
is_playing_animation
Fix single keyframe animations.
Fix repeated animation transition bug
Make
AnimationPlayer::start
and::play
work accordingly to documentationapply finished animations
Make ActiveAnimation::set_weight return &mut Self
Add
AnimationGraph::from_clips
and simplifyanimated_fox
exampleReorganize some of
bevy_animation
's imports into a more consistent styleDon't require going through
bevy_animation::prelude
to get to certain items inbevy_animation
Implement animation masks, allowing fine control of the targets that animations affect.
feature gate
use bevy_animation
inbevy_gltf
Include AnimationTarget directly in the animation query rather than reading it through the EntityMut
Remove
TransformCurve
Impose a more sensible ordering for animation graph evaluation.
Remove bevy_animation dependency on bevy_text
Implement additive blending for animation graphs.
Fix additive blending of quaternions
Add Support for Triggering Events via
AnimationEvent
sDon't trigger animation events when paused
Don't trigger animation events when paused 2
Fixes to animation graph evaluation
Replace
Handle&lt;AnimationGraph&gt;
component with a wrapperRemove
thiserror
frombevy_animation
Make
AnimatableCurve::curve
publicSome animation doc improvements
Fix
animation_masks
example's buttonsReplace TwoIterators with Either in bevy_animation
Undeprecate is_playing_animation
Remove the invalid system ordering in the animation example.
Incorporate all node weights in additive blending
AnimationEvent -> Event and other improvements
AnimatedField and Rework Evaluators
Fix newline in AnimationEvaluationState docs
Animation + ECS + Reflection + UI #
Animation + Math #
Remove unnecessary compute for rotation interpolation
Curve-based animation
add curve utilities to create curves interpolating/easing between two values
Eliminate redundant clamping from sample-interpolated curves
Add most common interpolations
add example for ease functions
fix some of the ease functions from interpolation
Animation + Reflection #
Reflect derived traits on all components and resources: bevy_animation
Allow animation clips to animate arbitrary properties.
Simplify
AnimatableProperty::Property
trait boundsFix dynamic linking failures from the
AnimationEventFn
change.
Animation + Rendering #
Animation + UI #
App #
Let init_non_send_resource require FromWorld instead of Default
Add missing plugins to doc of DefaultPlugins
Fix is_plugin_added::<Self>() being true during build
feat: add insert_after and insert_startup_before
Remove second generic from
.add_before
,.add_after
plugin_group!
macro (adopted)Remove need for EventLoopProxy to be NonSend
Remove deprecated
bevy_dynamic_plugin
Improve documentation on Update vs FixedUpdate schedule dichotomy
Allow ordering variable timesteps around fixed timesteps
Added
HeadlessPlugins
(#15203)Add features to switch
NativeActivity
andGameActivity
usageRemove
thiserror
frombevy_app
Headless by features
Clarify that
bevy_app::App.world()
(and mut variant) returns the mainSubApp
'sWorld
App + ECS #
Move
StateTransitionSteps
registration to states pluginUse
#[doc(fake_variadic)]
to improve docs readabilityImprove ambiguity detection example / test
App + States #
App + Windowing #
Assets #
Improve error handling for
AssetServer::add_async
bug: Don't panic. Warn on missing file_watcher path. (new branch)
Optimize common usages of
AssetReader
EmptyPathStream is only used in android/wasm32
Fix crash when an asset load failure event is processed after asset drop
add debug logging to ascertain the base path the asset server is using
catch asset loader panics
Generalized
Into&lt;AssetSourceId&gt;
andInto&lt;AssetPath&gt;
Implementations over LifetimeAllow removing asset from embedded asset registry
drop pending asset loads
Use CowArc::Static
Add basic docs to AssetMode
Depreciate
LoadAndSave
Asset ProcessorReccomend using
AssetPlugin.file_path
instead of CARGO_MANIFEST_DIRAdd module and supporting documentation to
bevy_assets
Add basic docs explaining what asset processing is and where to look
AssetServer LoadState API consistency
Fix untyped asset loads after #14808.
Fix AssetServer lifetimes
Cleanup unneeded lifetimes in bevy_asset
Replace
AsyncSeek
trait byAsyncSeekForward
forReader
to address #12880Add directory related functions to
AndroidAssetReader
bevy_asset: Improve
NestedLoader
APIdrop info locks in single threaded
Remove
thiserror
frombevy_asset
Fix potential deadlock in
AssetServer
on single-threaded modes.Remove incorrect equality comparisons for asset load error types
[Adopted] Add a method for asynchronously waiting for an asset to load
Resolve unused_qualifications warnings
Add
AsyncSeekForwardExt
trait to allows a similar API to the previous Bevy versionUpdate notify-debouncer-full requirement from 0.3.1 to 0.4.0
Support creating asset directories
Assets + Diagnostics #
Assets + ECS #
Assets + Rendering #
Improve MeshletMesh::from_mesh performance further
Make gLTF node children Handle instead of objects
Faster MeshletMesh deserialization
Add sprite and mesh alteration examples
Split
TextureAtlasSources
out ofTextureAtlasLayout
and makeTextureAtlasLayout
serializable
Assets + Scenes #
Assets + Utils #
Audio #
Audio + ECS #
Audio + Reflection #
Build-System #
run windows ci on rust 1.78
Shader code paths
example showcase: keep the order of the shaders imported
Windows CI example runner: back to using rust stable
Fix footgun for Windows users in
fast_config.toml
Use rust-lld on windows rustdoc in config_fast_builds.toml
Specify test group names in github summary for compile fail tests
Fix the dev docs robots.txt containing a literal instead of a newline
update bunny meshlet url
remove check-cfg job
Generate links to definition in source code pages on docs.rs and dev-docs.bevyengine.org
Stop website examples from linking to old URL with multiple redirects
Fix CI bench compile check
Sync flags in docs.yml with
package.metadata.docs.rs
Fix window position patch
Document private items in dev-docs
compile_fail_utils: Ignore
target
directorycompile_fail_utils: Verify path exists
Use lld for rustdoc on Linux in config_fast_builds.toml
Bump crate-ci/typos from 1.24.1 to 1.24.3
remove cfg-check in ci tool
Bump peter-evans/create-pull-request from 6 to 7
Fix Welcome Contributors CI
Broaden "Check for bevy_internal imports" CI Task
Fix action.yml syntax
send_events is ambiguous_with_all
Fix cargo-ndk build command
Add new crates to publish.sh
Add
compile-check-no-std
Command to CI Tooluse previous ubuntu version for example validation
Compare screenshots with main on PRs
Bump crate-ci/typos from 1.25.0 to 1.26.0
Fix screenshot comparison
screenshot comparison: prepare macos folder in the expected format by upload-artifact action
screenshot comparison: fix upload for macOS... again
Make
contributors
example deterministic in CIImprove and Debug CI
compile-check-no-std
CommandTypo-check .hidden files
crate publish order: bevy_animation depends on bevy_animation_derive
Build-System + Cross-Cutting #
Build-System + Dev-Tools #
Build-System + Meta #
Build-System + Rendering #
Color #
Adds back in way to convert color to u8 array, implemented for the two RGB color types, also renames Color::linear to Color::to_linear.
Allow bevy_color use without bevy_reflect support
Prevent division by zero in HWBA to HSVA conversions
Fix hue mixing for
Lcha
andOklcha
Color gradient curve
Remove
thiserror
frombevy_color
Fix
bevy_color
not compiling standalone.
Color + Gizmos #
Core #
Core + Editor #
Core + Reflection #
Cross-Cutting #
Apply Clippy lints regarding lazy evaluation and closures
Fix intra-doc links and make CI test them
Test for ambiguous system ordering in CI
Remove manual --cfg docsrs
Fix common capitalization errors in documentation
Apply unused_qualifications lint
Remove all existing system order ambiguities in
DefaultPlugins
Unify crate-level preludes
Allow to expect (adopted)
Simpler lint fixes: makes
ci lints
work but disables a lint for nowAdd
core
andalloc
overstd
LintsRemove the
Component
trait implementation fromHandle
Migrate from
Query::single
and friends toSingle
Fix most clippy lints
Adjust some example text to match visual guidelines
More
#[doc(fake_variadic)]
goodness
Cross-Cutting + Math #
A Curve trait for general interoperation — Part I
A Curve trait for general interoperation — Part II
Fix floating point math
Cross-Cutting + Meta #
Cross-Cutting + Rendering #
Cross-Cutting + UI + Windowing #
Dev-Tools #
allow more configuration for showcase from the CLI
Refactor BRP to allow for 3rd-party transports
Add content-type header to BRP HTTP responses
Include errors along side successful components in BRP
bevy&#x2F;get
methodAllow access a method handler
Add with_headers() method to RemoteHttpPlugin
simplify adding headers and improve consistency for RemoteHttpPlugin
Watching versions of
bevy&#x2F;get
andbevy&#x2F;list
with HTTP SSEImprove
fps_overlay
exampleFeature gate
bevy_remote
http transport.
Dev-Tools + Diagnostics #
Diagnostics #
Improve error handling for log filter
Document use of
NO_COLOR
inLogPlugin
Document trace_tracy_memory in profiling.md
add entity to error message
use Display for entity id in log_components
Add Mac OS tracy info to profiling docs
Add error message if states schedule missing (usually because StatesPlugin hasn't been added)
Update sysinfo version to 0.31.0
Add freebsd support for sysinfo
Add example demonstrating how to enable / disable diagnostics
Move the default LogPlugin filter to a public constant
Use oslog for ios
Visual improvements to
log_layers_ecs
exampleFixed issue with
derive_more
Display
Implementations
Diagnostics + Rendering #
Don't ignore draw errors
Updated
LogPlugin
Documentation with Performance WarningFix AsBindGroupError display for InvalidSamplerType
ECS #
rename bevy_state_macros to bevy_state_macros_official
Update crate metadata for bevy state
Add
mappings
toEntityMapper
Clear messed up feature flag on AppExitStates impl
Fix EntityCommands::despawn docs
Add more granular system sets for state transition schedule ordering
fix docs around
StateTransition
and remove references to `apply_sta…Re-name and Extend Run Conditions API
Update serialize flag for bevy_ecs
Remove extra call to clear_trackers
Split event.rs into a full module.
Revert "constrain WorldQuery::init_state argument to ComponentInitial…
Fix minor typos in query join docs
Restore overwrite capabilities of
insert_state
Generalised ECS reactivity with Observers
remove inaccurate warning from
in_state
Warn about missing
StatesPlugin
when installing statesobservers example doesn't follow standards
Use a unstable sort to sort component ids in
bevy_ecs
Update observer archetype flags for sparse components
Fix typo in
Query::single_mut
docsIntoSystemConfigs::chain_ignore_deferred
's return type fixdocs(bevy_state): fix broken links in init_state and insert_state
Fix typo in
ComponentId
docs:of
->or
Change World::inspect_entity to return an Iterator instead of Vec
improved error message when forgetting to call system apply function …
Add example enum Component usage to ecs_guide
feat(bevy_app): expose an API to perform updates for a specific sub-app.
Mark events as read during
EventReader::par_read
Don't show
.to_bits
inDisplay
impl forEntity
AnyOf soundness fix
Fair Change Detection Benchmarking
Use an opaque type for
EntityCommand::with_entity
Emit a warning if the result of
EntityCommand::with_entity
is not usedadd missing sort_unstable_by_key to QueryIter
Add missing StaticSystemParam::queue implementation.
Fix error in AnyOf
deregister events
Fix
push_children
inserting aChildren
component even when no children are suppliedadd missing mention of sort_unstable_by_key in QuerySortedIter docs
use associated type bounds in QueryManyIter and QueryIter::sort()
Component Hook functions as attributes for Component derive macro
Fix state example urls
Created an EventMutator for when you want to mutate an event before reading
Allow observer systems to have outputs
Make initial
StateTransition
run beforePreStartup
Component Lifecycle Hook & Observer Trigger for replaced values
Fix inaccurate docs for
Commands::spawn_empty
Allow non-static trigger targets
Add insert_by_id and try_insert_by_id to EntityCommands
Update
trigger_observers
to operate over slices of datadocs: Fix incorrect docs in the run conditions example
implement DoubleEndedIterator for QueryManyIter
Simplify run conditions
Require
&amp;mut self
forWorld::increment_change_tick
Add intradoc links for observer triggers
Fix typo in
World::observe
Don't debug
SystemId
's entity field twiceAdd
FilteredAccess::empty
and simplify the implementatin ofupdate_component_access
forAnyOf
/Or
Optimize cloning for Access-related structs
Track source location in change detection
Fix Entity Debug Format
B0003: Print caller
Opportunistically use dense iteration for archetypal iteration
Add link to
with_children
inwith_child
docMake
QueryState::transmute
&co validate the world of the&amp;Components
usedAdd a ComponentIndex and update QueryState creation/update to use it
Separate component and resource access
Fix soudness issue with Conflicts involving
read_all
andwrite_all
Fix access conflicts for resources
Explicit using clone_from
inline iter_combinations
Replace UnsafeCell<World> usage with UnsafeWorldCell in CombinatorSystem
Support more kinds of system params in buildable systems.
document using
ObserverState
as filter forObserver
Entity
sadd
SystemIdMarker
Component
to enable filtering forSystemId
Entity
sFix world borrow for DeferredWorld::query
Add
Command
and co. to preludeSkip empty archetype/table
Add entity
.trigger()
methodsRemove redundant
ArchetypeComponentId
lookup inRes
andResMut
Add query reborrowing
feat: add
insert_if_new
(#14397)Add
try_insert_with_new
Fix commands not being Send / Sync in 0.14
add docs explaining the two accesses of a System meta
Add
filter_map_unchanged
toMut&lt;T&gt;
Use observers for removal detection in example
Remove dead links to example code in the bevy_ecs README
Rename
Commands::register_one_shot_system
->register_system
Make the field of
ParamSetBuilder
pub so it's actually usable.Added
on_unimplemented
Diagnostic forIntoObserverSystem
Implement
std::fmt::Debug
forecs::observer::Trigger
Have EntityCommands methods consume self for easier chaining
Add
condition_changed
andcondition_became_true
tocommon_conditions
SystemParamBuilder - Support buildable Vec parameters
Fix query transmute from table to archetype iteration unsoundness
Required Components
Commands::send_event
SystemParamBuilder - Enable type inference of closure parameter when building dynamic systems
Updated
FromWorld
Documentation to mentionDefault
SystemParamBuilder - Allow deriving a SystemParamBuilder struct when deriving SystemParam.
Fix observer unregistering unsetting archetype flags
Interpolate
WorldQuery
path in docs of generated typesUse
#[doc(fake_variadic)]
forSystemParamBuilder
tuple impls.Opportunistically use dense iter for archetypal iteration in Par_iter
Make QueryFilter an unsafe trait
Add
observer
toTrigger
Use associated type bounds for
iter_many
and friendsEntityRef/Mut get_components (immutable variants only)
Removed Type Parameters from
Observer
Optimize observer unregistration
Added ordering information to observer tests (#14332)
Improve schedule note of .after/.before & encourage to use .chain ins…
Example for bevy_ecs::event::Events uses deprecated function get_reader
Remove redundent information and optimize dynamic allocations in
Table
Improve type inference in
DynSystemParam::downcast()
by making the type parameter match the return value.Add missing insert API commands
Rename push children to add children
Don't leak SEND resource, even if thread is panicking.
Rename Add to Queue for methods with deferred semantics
Add
EntityRefExcept
andEntityMutExcept
world queries, in preparation for generalized animation.Group
IntoSystemConfigs
impl
s togetherEnable
clippy::check-private-items
so thatmissing_safety_doc
will apply to private functions as wellcommand based entry api with
EntityCommands::entry
Fix memory leak in world's command queue
Fix subtle/weird UB in the multi threaded executor
Choose more descriptive field names for
ReserveEntitiesIterator
change return type of
World::resource_ref
toRef
Add World::trigger_ref and World::trigger_targets_ref
Add cached
run_system
APIReduce runtime panics through
SystemParam
validationSupport systems that take references as input
Fix system param warnings on systems that cannot run anyways
bevy_ecs: flush entities after running observers and hooks in despawn
Follow up to cached
run_system
fix observer docs
List components for QueryEntityError::QueryDoesNotMatch
Rename init_component & friends
Reduce memory usage in component fetches and change detection filters
Improve unclear docs about spawn(_batch) and ParallelCommands
QuerySingle
family of system paramsrename
QuerySingle
toSingle
System param validation for observers, system registry and run once
Reorganize SystemParamBuilder docs and examples.
Small addition to
World::flush_commands
explaining howspawn
will cause it to panic.Populated
(query) system paramAdd
register_resource_with_descriptor
Fix typos in bevy_ecs system.rs
Fix #15496 missing doc links
Runtime required components
15540 Make World::flush_commands private
Revert "Have EntityCommands methods consume self for easier chaining"
Enable
EntityRef::get_by_id
and friends to take multiple ids and get multiple pointers backImplement
SystemParam::queue()
method for blanket implementation ofParamSet
Better warnings about invalid parameters
Despawn and despawn_recursive benchmarks
Add try_despawn methods to World/Commands
Rename observe to observe_entity on EntityWorldMut
System param for dynamic resources
Add method to remove component and all required components for removed component
Allow a closure to be used as a required component default
Deprecate
Events::oldest_id
Fix QuerySingle -> Single missed in example
Allow
World::entity
family of functions to take multiple entities and get multiple references backDeprecate
get_or_spawn
bevy_ecs: Replace panics in
QueryData
derive compile errorsRemove
thiserror
frombevy_ecs
Rename
App&#x2F;World::observe
toadd_observer
,EntityWorldMut::observe_entity
toobserve
.Add
World::get_resource_or_init
as an alternative toWorld::get_resource_or_insert_with
bevy_ecs: Special-case
Entity::PLACEHOLDER
formattingAdd
insert_batch
and variationsAdd
Trigger::components
, which lists the component targets that were triggeredValidate param benchmarks
QueryEntityError: Use short name for components
Derive same attributes as MainEntity for RenderEntity
Fix fallible param notes
Adding
ScheduleGraph::contains_set
Remove unused debug identifiers for
ComponentHooks
methodsFix
alien_cake_addict
exampleundeprecate
component_reads_and_writes
Add
unregister_system
commandUse the fully qualified name for
Component
in therequire
attributeAdd missing exports in bevy_ecs
Fix bubbling of runtime requirements for
#[require(...)]
attributeFix runtime required components not registering correctly
Add a note to the on_unimplemented message for
QueryData
recommending&amp;T
and&amp;mut T
.Fix adding a subtree of required components to an existing tree replacing shallower required component constructors
Fix
Single
doc linksClarify inheritance behavior of required components
ECS + Editor #
ECS + Hierarchy #
ECS + Hierarchy + Picking #
ECS + Networking #
ECS + Networking + Reflection + Scenes #
ECS + Networking + Scenes #
ECS + Picking #
Recalibrated observe benchmark
Bubbling observers traversal should use query data
Migrate bevy picking
ECS + Reflection #
impl Reflect + Clone for StateScoped
feat: Add
World::get_reflect()
andWorld::get_reflect_mut()
Add
Reflect
toOnReplace
Use
map_unchanged
in reflection instead of creating aMut
manually.SystemParamBuilder - Support dynamic system parameters
Enhance ReflectCommandExt
Finish enhancing
ReflectCommandExt
to work with BundlesAllow registering of resources via ReflectResource / ReflectComponent
Make
SystemIdMarker
reflect-ableReflectBundle::remove
improvement
ECS + Reflection + Utils #
ECS + Rendering #
Migrate visibility to required components
The Cooler 'Retain Rendering World'
Migrate fog volumes to required components
Migrate meshes and materials to required components
Migrate motion blur, TAA, SSAO, and SSR to required components
Migrate cameras to required components
Synchronize removed components with the render world
Migrate reflection probes to required components
ECS + Time #
ECS + Transform #
Editor #
Editor + Reflection #
Gizmos #
Add cross gizmos
Use u32 for all resolution/subdivision fields in bevy_gizmos
Use CameraController in 3d_gizmos example
Making
bevy_render
an optional dependency forbevy_gizmos
Fix gizmos regression
Consistency between
Wireframe2d
andWireframe
Fix key bindings in 3d_gizmos example after camera controller
Fix Gizmos warnings and doc errors when a subset of features are selected
Fix Gizmo joint rendering in webgpu
Fix
arc_2d
GizmosUse
Isometry
inbevy_gizmos
wherever we canGizmos:
arc_2d
utility helpersImprove the gizmo for
Plane3d
, reusing gridUtilise new method for 2d circle example.
Stop using
Handle&lt;T&gt;
as a component inbevy_gizmos
Fix failing
cargo check
with only the bevy_dev_tools featureFix gizmos
Gizmos + Math #
Switch rotation & translation in grid gizmos
Curve
gizmos integrationUse circle gizmos for capsule
Implement
From
translation and rotation for isometries
Gizmos + Rendering #
view.inverse_clip_from_world should be world_from_clip
Fix 3D Gizmo webgpu rendering
Make TrackedRenderPass::set_vertex_buffer aware of slice size
Hierarchy #
Add
with_child
to simplify spawning when there will only be one childFix
with_child
not insertingParent
componentAdd more tools for traversing hierarchies
Fix
bevy_hierarchy
failing to compile withoutreflect
feature
Hierarchy + Transform #
impl
BuildChildrenTransformExt
forEntityWorldMut
Optimize transform propagation
Only propagate transforms entities with GlobalTransforms.
Input #
Fix phantom key presses in winit on focus change (#13299)
Mouse input accumulation
Expose Winit's
KeyEvent::repeat
inKeyboardInput
feature: Derive Hash for KeyboardInput.
Remove
ReceivedCharacter
Use of deprecated function in example for ButtonInput
Implement gamepads as entities
Add some missing features from the gamepads-as-entities change that were needed to update
leafwing-input-manager
.Remove
thiserror
frombevy_gilrs
Remove
thiserror
frombevy_input
Fix panic in
gamepad_viewer
example when gamepad is connectedGamepad improvements
Use
Name
component for gamepadRevert most of #16222 and add gamepad accessors
Input + Picking #
Input + Picking + UI #
Input + Picking + Windowing #
Input + Reflection #
Input + Rendering #
Input + UI #
Input + UI + Windowing #
Input + Windowing #
Math #
Add segments to
ExtrusionBuilder
Stable interpolation and smooth following
Added an illustration to the compass direction docs (issue 13664)
Custom primitives example
Use smooth_nudge in 2d_top_down_camera example
Use a well defined type for sides in RegularPolygon
Updated descriptions for some geometric primitives to include more detail
Optimize unnecessary normalizations for
Transform::local_{xyz}
Make
bevy_math::common_traits
publicMoves smooth_follow to movement dir
fix: Possible NaN due to denormalised quaternions in AABB implementations for round shapes.
Basic isometry types
Fix swapped docs for
Rot2::rotation_to&#x2F;from_y
Add
Isometry2d::from_xy
andIsometry3d::from_xyz
Add
inverse_mul
andinverse_transform_point
for isometriesbevy_math: faster sphere sampling
Improve isometry docs
Implement
Bounded2d
forAnnulus
Added
new
method to Cone 3D primitiveCyclic splines
Fast renormalize
Disallow empty cubic and rational curves
Refactor Bounded2d/Bounded3d to use isometries
Add
Dir2::from_xy_unchecked
andDir3::from_xyz_unchecked
Glam 0.28 update - adopted
Fix broken bezier curve benchmark
Make bevy_math's
libm
feature uselibm
for allf32
methods with unspecified precisionadd
const
s to curve module functionsNew utility methods on
InfinitePlane3d
Expose bevy math ops
Fix tiny seam in Annulus geometry.
Add methods to sample curves from
IntoIterator
typesadd
Interval::UNIT
constantFixing Curve trait not being object safe.
Fix
Capsule2d::sample_interior
Rotation api extension
Update
glam
to 0.29,encase
to 0.10.add more
Curve
adaptorsBasic integration of cubic spline curves with the Curve API
Add
to_inner_rectangle
,area
andperimeter
methods toCapsule2d
Curves:
FromReflect
boogaloo part 2Use
Dir2
/Dir3
instead ofVec2
/Vec3
forRay2d::new
/Ray3d::new
Simplified easing curves
remove the interpolation dependency from bevy_math
easing_functions example: draw point closer to its curve
Remove
thiserror
frombevy_math
Put curve-related stuff behind a feature
Improve
PhantomData
held by curve adaptorsAdd module-level docs for Curve
Infer
StableInterpolate
on tuplesUse
#[doc(fake_variadic)]
onStableInterpolate
Remove write access to
ConvexPolygon.vertices
Implement
Measured2d
forArc2d
-based primitives.Rename
Rot2::angle_between
toRot2::angle_to
Use normal constructors for EasingCurve, FunctionCurve, ConstantCurve
Math + Reflection #
Migrated
NonZero*
toNonZero&lt;*&gt;
bevy_reflect: Update
EulerRot
to matchglam
0.29Make
SampleCurve
/UnevenSampleCurve
succeed at reflection
Math + Rendering #
Use u32 for resolution/subdivision in primitive meshing
Uniform mesh sampling
introduction of
ConvexPolygon
andConvexPolygonMeshBuilder
Add a test for Mesh::compute_smooth_normals
Unnecessary division in compute_smooth_normals
Add a test for Mesh::triangles and fix for TriangleStrip
Math + Transform #
Conversions for Isometry3d ⟷ Transform/GlobalTransform
Use AccumulatedMouseMotion, AccumulatedMouseScroll in examples
Math + UI #
Math + Utils #
Meta #
Document the Release Candidate process
Mention updating Bevy book code validation for release candidates
meta: Add
Showcase
section to PR templateprepare next version: update regexes
Add a direct link in the docs to the Discord's #working-groups channel
remove changelog file
Deprecate old contributing documentation / information
Revert accidentally added asset docs
Fix out of date template and grammar
Fix
bevy_window
andbevy_winit
readme badgesImprovements .gitignore
Networking #
Picking #
Upstream
CorePlugin
frombevy_mod_picking
Mod picking upstream 2
rename Drop to bevy::picking::events::DragDrop to unclash std::ops:Drop
Use
FloatOrd
for sprite Z comparison and ignore sprites with NaNfeature gate picking backends
Rename UiPickingBackend to UiPickingBackendPlugin
Get rid of unnecessary mutable access in ui picking backend
Clean up the
simple_picking
exampleRename the
Pickable
component and fix incorrect documentationPicking: Filter out invisible sprites early
Add mesh picking backend and
MeshRayCast
system parameterAdd read-only access to
PointerInteraction
Fix
bevy_picking
plugin suffixesMesh picking fixes
Emit picking event streams
Picking example touchups
Add
button_just_down
andbutton_just_up
methods toPointerInput
Expose picking pointer state as a resource
Fix typos in
bevy_picking
module docsFix sprite picking backend not considering the viewport of the camera.
Fix the picking backend features not actually disabling the features
Add flags to
SpritePlugin
andUiPlugin
to allow disabling their picking backend (without needing to disable features).
Picking + Rendering #
Add
depth_ndc_to_view_z
for cpu-sideAdd bevy_picking sprite backend
Fix sprite and picking examples
Fix panic due to malformed mesh in
specialized_mesh_pipeline
Picking + UI #
hooking up observers and clicking for ui node
Ignore clicks on uinodes outside of rounded corners
Fix
bevy_ui
compile error whenbevy_picking
feature is disabled
Pointers #
ptr
: allowPtr
andPtrMut
construction for references to values of?Sized
typesRemove int2ptr cast in
bevy_ptr::dangling_with_align
and remove-Zmiri-permissive-provenance
in CIReduce compile time of bevy_ptr::OwnedPtr::make function
Reflection #
improved the error message by insert_boxed (issue #13646) (again)
bevy_reflect: Improve reflection serialization error messages
Add a test asserting that reflected cloning a Handle increments strong count
bevy_reflect: Function reflection
bevy_reflect: Re-enable reflection compile fail tests
Fix doc list indentation
bevy_reflect: Feature-gate function reflection
bevy_reflect:
TypeInfo
casting methodsbevy_reflect: Nested
TypeInfo
gettersexamples: Add
Type Data
reflection exampleImplement FromIterator/IntoIterator for dynamic types
bevy_reflect: Add
DynamicClosure
andDynamicClosureMut
bevy_reflect: Improve
DynamicFunction
ergonomicsDedicated
Reflect
implementation forSet
-like thingsbevy_reflect: Adding support for Atomic values
Fix lints in nightly
bevy_reflect: Function registry
bevy_reflect: Update serde tests for
Set
bevy_reflect: Anonymous function parsing
bevy_reflect: Update internal docs regarding anonymous function type names
bevy_reflect: Add
DynamicSet
todynamic_types
examplebevy_reflect: Function reflection benchmarks
reflect: implement the unique reflect rfc
Making
DynamicEnum::is_dynamic()
return truebevy_reflect: Store functions as
DynamicClosure&lt;&#x27;static&gt;
inFunctionRegistry
bevy_reflect: Function reflection terminology refactor
Implement
Reflect
forstd::ops::Bound
bevy_reflect: Add
Type
typeSplit
GenericTypeCell::get_or_insert
into smaller piecesReflect
SmolStr
'sDe&#x2F;Serialize
implementationbevy_reflect: Refactor
serde
modulebevy_reflect: Update
on_unimplemented
attributesbevy_reflect: Contextual serialization error messages
bevy_reflect: Mention
FunctionRegistry
inbevy_reflect::func
docsbevy_reflect: Add
DynamicTyped
traitParsedPath::try_from<&str>
Add
DynamicTyped
link toTypeInfo
docs (#15188)bevy_reflect: Add
FunctionRegistry::call
bevy_reflect: Add
Function
traitsplit
bevy_reflect::derive::utilities
into proper modulesbevy_reflect: Add
ReflectRef
/ReflectMut
/ReflectOwned
convenience casting methodsbevy_reflect: Automatic arg count validation
bevy_reflect: Add dynamic type data access and iteration to
TypeRegistration
impl_reflect!
for EulerRot instead of treating it as an opaque valuebevy_reflect: Replace "value" terminology with "opaque"
Fix
ReflectKind
description wordingRemove
Return::Unit
variantUse
HashTable
inDynamicMap
and fix bug inremove
Make
drain
take a mutable borrow instead ofBox&lt;Self&gt;
for reflectedMap
,List
, andSet
.bevy_reflect: Generic parameter info
bevy_reflect: Add
DeserializeWithRegistry
andSerializeWithRegistry
Documentation for variadics
Serialize and deserialize tuple struct with one field as newtype struct
Remove
thiserror
frombevy_reflect
bevy_reflect: Add crate level
functions
feature docsspirv_shader_passthrough
must enablewgpu&#x2F;spirv
bevy_reflect: get_represented_kind_info APIs for reflected kinds
Mute non-local definition warnings in bevy_reflect
bevy_reflect: Add
ReflectDeserializerProcessor
bevy_reflect: Add
ReflectSerializerProcessor
Reflection + Rendering #
Reflection + Rendering + Scenes #
Reflection + Scenes #
Use
FromReflect
when extracting entities in dynamic scenesfix: add reflect to
SceneInstanceReady
and other observers/events
Reflection + States #
Reflection + Text #
Reflection + UI #
Reflection + Utils #
Rendering #
Allow mix of hdr and non-hdr cameras to same render target
2D top-down camera example
Remove unused mip_bias parameter from apply_normal_mapping
Meshlet misc
Clarify error message due to missing shader file
Make FOG_ENABLED a shader_def instead of material flag
Fix meshlet vertex attribute interpolation
Add
from_color
toStandardMaterial
andColorMaterial
Revert "Make FOG_ENABLED a shader_def instead of material flag (#13783)"
Highlight dependency on shader files in examples
Use dynamic uniform buffer in post processing example
Wgpu 0.20
Made some things in bevy_render
Debug
.Reuse VisibleEntities in check_light_mesh_visibilty
Refactor check_light_mesh_visibility for performance #1
Improve MeshletMesh::from_mesh performance
Specify units in
AmbientLight::brightness
docsMake meshlet processing deterministic
Use default window and font sizes in sprite_slice example
Fix meshlet interactions with regular shading passes
Fix MeshletMesh material system ordering
don't crash without features
bevy_pbr
,ktx2
,zstd
Print warning when using llvmpipe
Refactor check_light_mesh_visibility for performance #2
Fix typo in CPU adapter warning
Fix incorrect computation of mips for cluster occlusion lookup
Allow phase items not associated with meshes to be binned.
Update ui_material example to be a slider instead
Made Material2dBindGroupId instantiable
Fix compile failure in WASM without
wgpu
backendSupport operations for render layers and fix equality comparisons
Clarify the difference between default render layers and
none
render layersAdded feature switch to default Standard Material's new anisotropy texture to off
impl Debug for ExtendedMaterial
disable gpu preprocessing on android with Adreno 730 GPU and earilier
Lighting Should Only hold Vec<Entity> instead of TypeId<Vec<Entity>>
Fix prepass batch
Start a built-in postprocessing stack, and implement chromatic aberration in it.
Fix overflow in
RenderLayers::iter_layers
Add support for skybox transformation
Clearer spatial bundle pub const docs
Fix error/typo in SMAA shader
Allow volumetric fog to be localized to specific, optionally voxelized, regions.
Pack multiple vertex and index arrays together into growable buffers.
Derive and reflect
Debug
forCameraRenderGraph
Add support for environment map transformation
Make
Viewport::default()
return a 1x1 viewportSet scissor on upscale to match camera viewport
Using Cas instead of CAS #14341
Fix breaking image 0.25.2 release.
Fix incorrect function calls to hsv_to_rgb in render debug code.
Move
Msaa
to componentUnignore
Camera.target
field for reflectionFix the example regressions from packed growable buffers.
Added
AstcBlock
andAstcChannel
to the forwarded wgpu types.Fixup Msaa docs.
fix examples after the switch for msaa to a component
Don’t prepare 2D view bind groups for 3D cameras
Made ViewUniform fields public
Fix
bevy_gltf
PBR features not enabling correspondingbevy_pbr
flagsHandle 0 height in prepare_bloom_textures
Fix TextureCache memory leak and add is_empty() method
Fix
bevy_render
'simage
dependency versionCorrect minimum range-alloc version
Disabled usage of the POLYGON_MODE_LINE gpu feature in the examples
Add example showing how to use SpecializedMeshPipeline
Don’t prepare lights (and shadow map textures) for 2D cameras
Skip batching for phase items from other pipelines
Fix num_cascades in split_screen exmample for WebGL
Add
invert_winding
for triangle list meshesAdd 2d opaque phase with depth buffer
Fix TAA on camera with viewport
Use BinnedRenderPhase for Opaque2d
Changed
Mesh::attributes*
functions to returnMeshVertexAttribute
View filter for batch_and_prepare_render_phase
Update WGPU to version 22
Expose max_mip_dimension and uv_offset in BloomSettings.
Add AlphaMask2d phase
Remove Component derive for DepthOfFieldMode
Don't ask for ResMut in
queue_view_auto_exposure_pipelines
Fix pass_span drop panic obscuring transparent 2d render errors
Add
RenderSet::FinalCleanup
forWorld::clear_entities
Add feature requirement info to image loading docs
Ignore
PipelineCache
ambiguitiesFix size of MeshVertexAttributeId to be platform independent
Add conversions between Visibility and bool
Fix mesh2_manual exapmle.
Revert "Add conversions between Visibility and bool (#14784)"
check sampler type in as_bind_group derives
Added Sprite::sized(custom_size)
Clarify docs for
RenderLayers::layer
Allow fog density texture to be scrolled over time with an offset
Fix fog density texture offset seam
Add helper methods on
Visibility
Fix underflow panic in
InitTriInfo
Rewrite screenshots.
Simplify render_to_texture examples
Replace the
wgpu_trace
feature with a field inbevy_render::settings::WgpuSettings
Refactor
AsBindGroup
to use a associatedSystemParam
.Add
VertexBufferLayout::offset_locations
Meshlet software raster + start of cleanup
ImageSampler::init_descriptor
Fix Adreno 642L crash
Remove unnecessary muts in
RenderSet::QueueMeshes
Ensure more explicit system ordering for preparing view target.
Adds
ShaderStorageBuffer
assetReturn
Result
s fromCamera
's world/viewport conversion methodsMore triangles/vertices per meshlet
Replaced implicit emissive weight with default.
Add convenience methods for constructing and setting storage buffer data
Remove deprecated
SpriteSheetBundle
andAtlasImageBundle
Fix
AsBindGroup
sampler validation.Split OrthographicProjection::default into 2d & 3d (Adopted)
Add common aspect ratio constants and improve documentation
bevy_pbr: Make choosing of diffuse indirect lighting explicit.
honour NoFrustumCulling for shadows
Remove OrthographicProjection.scale (adopted)
Add examples for orthographic and perspective zoom
Rename rendering components for improved consistency and clarity
Fix mesh 2d non indexed draw.
TrackedRenderPass
internal tracking state resetFix
MeshAllocator
panicImplement percentage-closer soft shadows (PCSS).
Cleanup legacy code from bevy_sprite
Add ASCII art to custom mesh example (#15261)
Spirv passthrough main (adopted, part deux)
Visibility range takes the model aabb into acount
Meshlet screenspace-derived tangents
Implement volumetric fog support for both point lights and spotlights
Clear view attachments before resizing window surfaces
Fix bevy_picking sprite backend panic in out of bounds atlas index
Gpu readback
Remove render_resource_wrapper
Clarify purpose of shader_instancing example
Fix some typos in custom_post_processing.rs
Migrate lights to required components
Add
sub_camera_view
, enabling sheared projectionFix Mesh allocator bug and reduce Mesh data copies by two
Added visibility bitmask as an alternative SSAO method
Split off bevy_image from bevy_render
Zero Copy Mesh
Fix deferred rendering
fix: corrected projection calculation of camera sub view
Split out bevy_mesh from bevy_render
Add
Image
methods for easy access to a pixel's colorUse global clear color for camera driver node.
Fix a system ordering issue with motion blur for skinned meshes.
Feature-gate all image formats
Screen shake example
Add Order Independent Transparency
Fix oit webgl
improve sub view example with dynamic viewports
Per-meshlet compressed vertex data
Use shader_def for oit resolve layer count
Ensure Bevy's rendering byte usage is little-endian
Fix missing Msaa::Off in scrolling_fog example
Remove
thiserror
frombevy_core_pipeline
Remove
thiserror
frombevy_image
Remove
thiserror
frombevy_mesh
Remove
thiserror
frombevy_pbr
Remove
thiserror
frombevy_render
Remove
thiserror
frombevy_sprite
Fix meshlet materials
Migrate bevy_sprite to required components
Remove
Handle&lt;T&gt;
trait implementations that are dependent onComponent
Fix mesh flags
Fix OIT shaders error with DX12 backend
Fix auto_exposure example
Type safe retained render world
Fix broken mesh2d
Implement
WorldQuery
forMainWorld
andRenderWorld
componentsAlternative fix for
mesh2d_manual
exampleMove
ImageLoader
andCompressedImageSaver
tobevy_image
.Attempt to remove component from render world if not extracted.
Despawn unused light-view entity
Move
SUPPORTED_FILE_EXTENSIONS
toImageLoader
and remove unsupported formats.Revert default mesh materials
Remove
ExtractComponent::Out
Delete
ImageLoader::COUNT
in favour ofImageLoader::SUPPORTED_FORMATS.len()
.Improve API for scaling orthographic cameras
Fix
pbr
example camera scaleFix lightmaps break when deferred rendering is enabled
examples(shaders/glsl): Update GLSL Shader Example Camera View uniform
Fix OIT depth test
Fix deactivated camera still being used in render world
Remove components if not extracted
Indices::push(u32)
Mute unreachable patterns/code warnings
Meshlet new error projection
Meshlet fix software rasterization
Fix point light count limit
Meshlet builder improvements redux
Meshlet fill cluster buffers rewritten
Reduce the clusterable object UBO size below 16384 for WebGL 2.
Place percentage-closer soft shadows behind a feature gate to save on samplers.
Adding alpha_threshold to OrderIndependentTransparencySettings for user-level optimization
Fix and improve MSAA documentation
Meshlet normal-aware LOD and meshoptimizer upgrade
move mesh uniform construction out of Core3d
Fix blending for CameraOutputMode::Skip
Fix WGSL formatting inconsistency on mesh_view_binding
Upgrade to wgpu 23
Make
BinnedRenderPhase
fields for accessing batchable and unbatchable entities publicExpose Pipeline Compilation Zero Initialize Workgroup Memory Option
Do not re-check visibility or re-render shadow maps for point and spot lights for each view
Make PCSS experimental
remove ViewUniformOffset from inactive cameras
Fix meshlet private item regression
Bind only the written parts of storage buffers.
Rename box shadow rendering variable names
Fix off-by-one error on
Image::get_color_at
andImage::set_color_at
.Refresh
RenderVisibilityRanges
when a visibility range is removed from the scene.Only use the AABB center for mesh visibility range testing if specified.
Make render world sync marker components
Copy
Use default storage for
TemporaryRenderEntity
use wgpu patch 23.0.1
Fix CAS shader alpha accessor
Fix CAS toggle broken by retained render world
Rendering + Text #
Rendering + Text + UI #
Rendering + Transform #
Rendering + UI #
reduce the antialias strength
fix panic: invalid SlotMap key used
Make default behavior for
BackgroundColor
andBorderColor
more intuitiveSkip extract UiImage When its texture is default
Fix UI texture atlas with offset
fix asymmetrical 9-slicing
Extract borders without border radius
UI texture atlas slice shader
UI texture slice texture flipping reimplementation
UI outlines radius
Add rect field to UI image
Retrieve the
stack_index
fromNode
inextract_ui_material_nodes
instead of walkingUiStack
Add a border to the UI material example
ui material node border calculations fix
Enable/disable UI anti-aliasing
RenderUiSystem::ExtractTextureSlice
use precomputed border values
Update the UI texture slice pipeline to work with the retained render world changes
Fix entity leak in
extract_uinode_borders
box shadow
promote atlas sources texture_ids to pub visibility
Dark text colors fix
Default UI shadow samples fix
Optional UI rendering
UI borders and outlines clipping fix
Fixes for a few minor borders and outlines bugs
Improved
UiImage
andSprite
scaling and slicing APIsUI anti-aliasing fix
Rendering + Windowing #
Fix screenshot example
Inverse bevy_render bevy_winit dependency and move cursor to bevy_winit
fix: Skip some rendering logics when the viewport width or height is zero
Fix linux nvidia + xwayland freeze at startup
Scenes #
Align
Scene::write_to_world_with
to matchDynamicScene::write_to_world_with
Revert Align Scene::write_to_world_with to match DynamicScene::write_to_world_with
Send
SceneInstanceReady
when spawning any kind of sceneAlign
Scene::write_to_world_with
to matchDynamicScene::write_to_world_with
Make the
GltfNode::children
links actually point to children.Change
SceneInstanceReady
to trigger an observer.Sorts the scene entries by path before serializing.
explicitly mention
component
in methods onDynamicSceneBuilder
add
allow_all
anddeny_all
methods toDynamicSceneBuilder
Fix for SceneEntityMapper + hooks panic
Migrate scenes to required components
Add scene summary
bevy_scene: Use
FromReflect
on extracted resourcesRemove
thiserror
frombevy_scene
Fix invalid scene file for
scene
example
States #
Add note on StatesPlugin requirement for state code
bevy_state: Make
reflect
module publicAdd set_state extension method to Commands
Throw real error messages on all failed attempts to get
StateTransition
schedule
Tasks #
Text #
Uncouple
DynamicTextureAtlasBuilder
from assetsfix non-exact text h-alignment
text position: use size instead of bounds
Fix inconsistency in
KeyboardInput
examples to match migration guidefeat: expose the default font bytes
Use cosmic-text shaping buffer
Reuse TextLayoutInfo in queue_text
Don't reallocate scale factors in measure_text_system
Fix some examples having different instruction text positions
Cosmetic improvements for
text_input
exampleTrim cosmic-text's shape run cache
Fixing text sizes for examples
Zero fontsize panic workaround
Make
CosmicFontSystem
andSwashCache
pub resources.Refactor TextPipeline::update_buffer to accept an interator
Fix text measurement when multiple font sizes are present
Remove
thiserror
frombevy_text
Text rework
Rename TextBlock to TextLayout
split up
TextStyle
Text Rework cleanup
Use CosmicFontSystem in public bevy_text APIs and remove cosmic_text re-export
Text2d scalefactor change detection fix
Skip empty spans when updating text buffers
Text + UI #
Cosmic text
Optimize UI text measurement
Avoid reallocating spans buffer in TextPipeline
Add the ability to control font smoothing
Remove
bevy_ui
's "bevy_text" featureFix not being able to run bevy_ui tests
Adds some helpful methods to TextFont
Time #
Fix minor doc typo
Make time_system public
Remove
thiserror
frombevy_time
Time<Real> documentation improvement
Fix overflow panic on Stopwatch at Duration::MAX
aligning public apis of Time,Timer and Stopwatch
Transform #
Use a ship in Transform::align example
Clarify GlobalTransform::transform_point
Fix tracing with
transform_hierarchy
exampleUse quaternionic
smooth_nudge
in thealign
exampleRemove
thiserror
frombevy_transform
Improved the global transform api to access rotation and scale
UI #
bug: Fix 9-slice textures with asymmetric borders.
add PartialEq to Outline
fix remaining issues with background color in examples
Using simple approx round up in ui_layout_system
Fix border color in
ui_texture_slice
andui_texture_atlas_slice
examples.Optimize ui_layout_system
Clean up UiSystem system sets
remove rounded_borders and merge with borders example
Fix button placement in
split_screen
exampleFix error in
bevy_ui
when building withoutbevy_text
Add BorderRadius field to ImageBundle
Remove
#[cfg]
from theFrom
impls ofTextSection
Added serialize flag to bevy_math dep of bevy_ui
fix issue with phantom ui node children
Remove manual
apply_deferred
inbevy_ui
#14143 - fix bevy_ui padding
Remove useless
Direction
fielddon't use padding for layout
Resolve UI outlines using the correct target's viewport size
Reduce allocations in ui_layout_system
Node::is_empty
Remove border radius scaling
Increase border_rect for TextureSlicer to match image
Implement
enabled
flag for fps overlayUI Scrolling
Simplified
ui_stack_system
Rename BreakLineOn to LineBreak
Add UI
GhostNode
Display the bounds of each text node in the
text_debug
ui exampleReplace
Handle&lt;M: UiMaterial&gt;
component withUiMaterialHandle
wrapperRemove warning for children in UI hierarchies without Style
Remove
thiserror
frombevy_ui
Filter UI traversal to only Node and GhostNode
Clip to the UI node's content box
[bevy_ui/layout] Add tests, missing asserts, and missing debug fields for UiSurface
don't clip text that is rotated
Overflow clip margin
Add shadows to
ui
exampleUI materials: don't reserve in loop when enough capacity
Mark ghost nodes as experimental and partially feature flag them
Migrate UI bundles to required components
Merge Style properties into Node. Use ComputedNode for computed properties.
Fix red background in
ui_material
exampleEnsure ghost nodes are skipped when getting parent clipping rect
Move TextureAtlas into UiImage and remove impl Component for TextureAtlas
Move
ContentSize
requirements fromNode
to the widget defining componentsRename
ComputedNode::calculated_size
tosize
Remove custom rounding
Require
ContentSize
onUiImage
againConstify
ComputedNode
Remove the
Globals
binding from the box shadow shaderExpanded
ui
exampleUiImage -> ImageNode, UiImageSize -> ImageNodeSize
Fix panic in UiSurface if Node is removed and re-added
Remove the measure func for image nodes with the default UI texture
Only use physical coords internally in
bevy_ui
fix: setting content size to rect size if image has a rect
BorderRadius::percent
fixImprove ZIndex docs
UI + Windowing #
Utils #
Fix parameter name of all_tuples's document
Remove unused type parameter in
Parallel::drain()
update
hashbrown
to0.14.2
Added Documentation to
all_tuples_with_size
Remove remnant
EntityHash
and related types frombevy_utils
Allow
bevy_utils
inno_std
ContextsRemove allocation in
get_short_name
Modify
derive_label
to supportno_std
environmentsMinor fixes for
bevy_utils
inno_std
Fix
detailed_trace
module scopeFix detailed_trace!
Windowing #
13743 app exit hang
fix typo
Correctly check physical size when updating winit
apply window scale to window size when creating it
Have WindowPosition::Centered take scale_factor_override into account
only run one update per frame drawn
update example low_power
Dirty fix for App hanging when windows are invisible on WindowsOS
Fix
bevy_window
failing withserialize
featureRemove unused
default
feature frombevy_window
Fix
bevy_winit
not building withserialize
featureProperly handle repeated window close requests
Expose winit's
MonitorHandle
Fix error when closing window in 2d_viewport_to_world example
Made the naming for commands parameter more consistent
Log monitor and window information at startup in bevy_winit
Use
try_insert
inon_remove_cursor_icon
move ANDROID_APP to bevy_window
Fix transparent_window example on wayland
Add the functions
start_drag_move
andstart_drag_resize
toWindow
Add
bevy_window::Window
options for MacOSforce last update after setting state to Suspended
Add window drag move and drag resize without decoration example.
Add docs for how to manually add a
WinitPlugin
to aMinimalPlugins
setup.Correctly feature gate
custom_cursor
properly flag using
CustomCursor::Url
in wasm
glTF #
Fix regression in
bevy_gltf
buildfeat(gltf): add name component to gltf mesh primitive
Remove
thiserror
frombevy_gltf
No area label #
rename the crate bevy_state_macros_official back to its original name
Bump crate-ci/typos from 1.21.0 to 1.22.3
Bump crate-ci/typos from 1.22.3 to 1.22.7
Fix minor typo
Fix lints introduced in Rust beta 1.80
Fixed a link to Blender's mikktspace docs
Omit font size where it closely matches the default in examples
Fix a few "repeated word" typos
Fix a couple typos in contributing docs
Make Observer::with_event (and other variants) unsafe
Bump crate-ci/typos from 1.22.7 to 1.22.9
Add Display implementation to DebugName.
fix examples color_grading and mobile after BackgroundColor changes
Deduplicate Wasm optimization instructions
fix typo processed_dir
Bump crate-ci/typos from 1.22.9 to 1.23.1
Add an example for doing movement in fixed timesteps
Fixed #14248 and other URL issues
update gltf example to use type-safe
GltfAssetLabel::Scene
Bump crate-ci/typos from 1.23.1 to 1.23.2
fix building cargo_gltf with feature dds
fix meshlet example
Bump crate-ci/typos from 1.23.2 to 1.23.5
Fix CI after #12965
time_system is ambiguous_with event_update_system
Bump crate-ci/typos from 1.23.5 to 1.23.6
Fix ecs example thread_rng duplicate creation
Bump crate-ci/typos from 1.23.6 to 1.24.1
Remove some asset examples from web showcase
Fix compile error caused by incorrect feature flag in
bevy_state
style: simplify string formatting for readability
fix imports in example ui_texture_slice_flip_and_tile
Fix comment in example
Fix error link
Bump crate-ci/typos from 1.24.3 to 1.24.5
Improve first person camera in example
fix spelling mistake
Fix "game_menu" example buttons not changing color on Interaction
Fix typo in bevy_reflect/src/reflect.rs
Micro typo in
bevy_ecs
2580 Split examples PR feedback
Reflect derived traits on all components and resources: bevy_core_pipeline
Reflect derived traits on all components and resources: bevy_ecs
Reflect derived traits on all components and resources: bevy_gizmos
Reflect derived traits on all components and resources: bevy_gltf
Reflect derived traits on all components and resources: bevy_input
Reflect derived traits on all components and resources: bevy_hierarchy
Reflect derived traits on all components and resources: bevy_pbr
Reflect derived traits on all components and resources: bevy_picking
Reflect derived traits on all components and resources: bevy_render
Reflect derived traits on all components and resources: bevy_sprite
Reflect derived traits on all components and resources: bevy_state
Reflect derived traits on all components and resources: bevy_text
Reflect derived traits on all components and resources: bevy_window
Reflect derived traits on all components and resources: bevy_ui
Reflect derived traits on all components and resources: bevy_transform
Substitute trivial fallible conversions with infallible function calls
Note on compiling on FreeBSD
Update gilrs requirement from 0.10.1 to 0.11.0
Fix typo in sprite_picking.rs example
simplify std::mem references
Fix warnings triggered by
elided_named_lifetimes
lintFix doc link import style to avoid unused_imports
Bump crate-ci/typos from 1.24.5 to 1.24.6
bump async-channel to 2.3.0
Cleanup
clearcoat
exampleFix doc comment
Fix wrong link in error
Spelling
Bump crate-ci/typos from 1.24.6 to 1.25.0
Bump actions/setup-java from 3 to 4
Update sysinfo requirement from 0.31.0 to 0.32.0
Fix
query_gltf_primitives
exampleFix
shader_prepass
exampleAdd
register_type
forUiMaterialHandle
andAnimationGraphHandle
Add
register_type
forUiAntiAlias
Remove eprintln from
2d_screen_shake
exampleUpdate multi_asset_sync.rs
Fix formatting of
irradiance_volumes
example instructionsFix
loading_screen
exampleFix
println
inmorph_targets
exampleFix
motion_blur
example instructionsFix
minimising
example minimizing every frameRemove a
dbg!
statement left over from debuggingFix typos from greyscale -> grayscale
Fix asset_settings example regression
Remove AVIF feature
OIT style tweaks
Fix some duplicate words in docs/comments
Fix a tiny mismatch between code and comment
Fix typos in
config_fast_builds.toml
remove reference to missing file in bevy_remote cargo.toml
fix bevy_dev_tools build
Cosmetic tweaks to
query_gltf_primitives
Remove
accesskit
re-export frombevy_a11y
Reflect TextLayout and ComputedTextBlock
Reduce iOS cpu usage