mirror of
https://github.com/bevyengine/bevy.git
synced 2026-07-01 00:05:45 -04:00
create-pull-request/patch
130 Commits
| Author | SHA1 | Message | Date | |
|---|---|---|---|---|
|
|
6c0eb84e31 |
Reduce bsn! codegen / debug symbols by combining scene implementations (#24124)
# Objective `bsn!` currently generates a lot of new types: one for each patch, which then get assembled into a single Scene impl by composing them in a tuple. This gives the compiler more work to do (types to verify, code to generate, etc). It also makes our debug symbols bigger / harder to read. ## Solution Combine `bsn!` entries, where possible, into a single `SceneFunction<F>` implementation of `Scene`, which does all of the "scene patch" logic internally. This significantly cuts down on codegen, and also makes the functional behavior of a `bsn!` block more clear when expanded. |
||
|
|
6d9511a5f3 |
Implement From<Val> for Fontsize (#24076)
# Objective Implement `From<Val>` for `Fontsize` so that the `Val` `px`, `vh`, etc helpers to be used to set font sizes in `bsn! `. ## Solution Implement `From<Val>` for `Fontsize`, with `Val::Auto` mapped to `FontSize:Rem(1.)` and `Val::Percent(x)` mapped to `FontSize::Rem(x/100.)`. |
||
|
|
95d219f146 |
Hoist bsn expressions up to reduce ownership / type constraints (#23905)
# Objective
`bsn!` currently embeds expressions directly into the patch closure,
which requires moving all of the "expression items" into the closure.
This thrusts constraints like `Send + Sync + 'static` on anything used
in the expression:
```rust
/// Workaround 1 (used in Feathers)
fn label(text: impl Into<String>) -> impl Scene {
let text = Text::new(text.into());
bsn! {
template_value(text)
}
}
/// Workaround 2
fn label(text: impl Into<String> + Send + Sync + 'static) -> impl Scene {
bsn! {
Text(text)
}
}
```
## Solution
Hoist expression above the closures, evaluate them immediately, and move
the _results_ into the closure. This significantly reduces the burden on
developers defining scenes.
This now works! No workaround necessary!
```rust
fn label(text: impl Into<String>) -> impl Scene {
bsn! {
Text(text)
}
}
```
Borrowing like this is also allowed because of implicit `into()`, which
converts the `&str` into `String`, which is then captured in the patch
closure:
```rust
fn label(text: &str) -> impl Scene {
bsn! {
Text(text)
}
}
```
Likewise, patterns like this now work:
```rust
fn cube(meshes: &mut Assets<Mesh>) -> impl Scene {
bsn! {
Mesh3d({meshes.add(Cuboid::default())})
}
}
```
|
||
|
|
aa5322ed0f |
BSN: scene.spawn() system ergonomics (#23868)
# Objective
Spawning a scene on startup is a common pattern. Lets make it easier to
do so!
## Solution
- Add `SpawnSystem` and `SpawnListSystem` traits that are implemented
for functions that return scenes / scene lists, and return a system that
spawns the scene / handles errors.
### Before
```rust
fn main() {
App::new()
.add_plugins(DefaultPlugins)
.add_systems(Startup, setup)
.run();
}
fn setup(world: &mut World) -> Result {
world.spawn_scene_list(bsn_list![Camera2d, ui()])?;
Ok(())
}
```
### After
```rust
fn main() {
App::new()
.add_plugins(DefaultPlugins)
.add_systems(Startup, scene.spawn())
.run();
}
fn scene() -> impl SceneList {
bsn_list![Camera2d, ui()]
}
```
This cuts out some boilerplate. It also further encourages people to
define standalone "scene functions" rather than embedding them in code,
which is generally a good pattern.
|
||
|
|
06f1d917a9 |
FromTemplate for TextFont (#23724)
Support TextFont / FontSource template (see bsn.rs example) |
||
|
|
6b0fb37e2c |
Rename bevy_scene2 to bevy_scene (#23668)
# Objective Part 3 of #23619 ## Solution Renames `bevy_scene2` to `bevy_scene` and adds it to the prelude. |
||
|
|
535cf401cc |
Reframe old "scene" terminology as "world serialization" (#23630)
Part 2 of #23619 In **Bevy 0.19** we are landing a subset of Bevy's Next Generation Scene system (often known as BSN), which now lives in the `bevy_scene` / `bevy::scene` crate. However the old `bevy_scene` system still needs to stick around for a bit longer, as it provides some features that Bevy's Next Generation Scene system doesn't (yet!): 1. It is not _yet_ possible to write a World _to_ BSN, so the old system is still necessary for "round trip World serialization". 2. The GLTF scene loader has not yet been ported to BSN, so the old system is still necessary to spawn GLTF scenes in Bevy. For this reason, we have renamed the old `bevy_scene` crate to `bevy_world_serialization`. If you were referencing `bevy_scene::*` or `bevy::scene::*` types, rename those paths to `bevy_world_serialization::*` and `bevy::world_serialization::*` respectively. Additionally, to avoid confusion / conflicts with the new scene system, all "scene" terminology / types have been reframed as "world serialization": - `Scene` -> `WorldAsset` (as this was always just a World wrapper) - `SceneRoot` -> `WorldAssetRoot` - `DynamicScene` -> `DynamicWorld` - `DynamicScene::from_scene` -> `DynamicWorld::from_world_asset` - `DynamicSceneBuilder` -> `DynamicWorldBuilder` - `DynamicSceneRoot` -> `DynamicWorldRoot` - `SceneInstanceReady` -> `WorldInstanceReady` - `SceneLoader` -> `WorldAssetLoader` - `ScenePlugin` -> `WorldSerializationPlugin` - `SceneRootTemplate` -> `WorldAssetRootTemplate` - `SceneSpawner` -> `WorldInstanceSpawner` - `SceneFilter` -> `WorldFilter` - `SceneLoaderError` -> `WorldAssetLoaderError` - `SceneSpawnError` -> `WorldInstanceSpawnError` Note that I went with `bevy_world_serialization` over `bevy_ecs_serialization`, as that is what all of the internal features described themselves as. I think it is both more specific and does a better job of making itself decoupled from `bevy_ecs` proper. |
||
|
|
f4d17d9eb2 |
Next Generation Scenes: Core scene system, bsn! macro, Templates (#23413)
After much [iteration](https://github.com/bevyengine/bevy/pull/20158), [designing](https://github.com/bevyengine/bevy/discussions/14437) and [collaborating](https://discord.com/channels/691052431525675048/1264881140007702558), it is finally time to land a baseline featureset of Bevy's Next Generation Scene system, often known by its new scene format name ... BSN (Bevy Scene Notation). This PR adds the following: - **The new scene system**: The core in-memory traits, asset types, and functionality for Bevy's new scene system. Spawn `Scene`s and `SceneList`s. Inherit from other scenes. Patch component fields. Depend on assets before loading as scene. Resolve Entity references throughout your scene. - **The `bsn!` and `bsn_list!` macro**s: Define Bevy scenes in your code using a new ergonomic Rust-ey syntax, which plays nicely with Rust Analyzer and supports autocomplete, go-to definition, semantic highlighting, and doc hover. - **`Template` / `GetTemplate`**: construct types (ex: Components) from a "template context", which includes access to the current entity _and_ access to the `World`. This is a foundational piece of the scene system. Note that this _does not_ include a loader for the BSN asset format, which will be added in a future PR. See the "Whats Next?" section for a roadmap of the future. Part of #23030 ## Review Etiquette This is a big PR. _Please use threaded comments everywhere, not top level comments_. Even if what you have to say is not anchored in code, find a line to leave your comment on. ## Overview This is a reasonably comprehensive conceptual overview / feature list. This uses a "bottom up" approach to illustrate concepts, as they build on each other. If you just want to see what BSN looks like, scroll down a bit! ### Templates `Template` is a simple trait implemented for "template types", which when passed an entity/world context, can produce an output type such as a `Component` or `Bundle`: ```rust pub trait Template { type Output; fn build_template(&mut self, context: &mut TemplateContext) -> Result<Self::Output>; } ``` Template is the cornerstone of the new scene system. It allows us to define types (and hierarchies) that require no `World` context to define, but can _use_ the `World` to produce the final runtime state. Templates are notably: * **Repeatable**: Building a Template does not consume it. This allows us to reuse "baked" scenes / avoid rebuilding scenes each time we want to spawn one. If a Template produces a value this often means some form of cloning is required. * **Clone-able**: Templates can be duplicated via `Template::clone_template`, enabling scenes to be duplicated, supporting copy-on-write behaviors, etc. * **Serializable**: Templates are intended to be easily serialized and deserialized, as they are typically composed of raw data. The poster-child for templates is the asset `Handle<T>`. We now have a `HandleTemplate<T>`, which wraps an `AssetPath`. This can be used to load the requested asset and produce a strong `Handle` for it. ```rust impl<T: Asset> Template for HandleTemplate<T> { type Output = Handle<T>; fn build_template(&mut self, context: &mut TemplateContext) -> Result<Handle<T>> { Ok(context.resource::<AssetServer>().load(&self.path)) } } ``` Types that have a "canonical" `Template` can implement the `GetTemplate` trait, allowing us to correlate to something's `Template` in the type system. ```rust impl<T: Asset> GetTemplate for Handle<T> { type Template = HandleTemplate<T>; } ``` This is where things start to get interesting. `GetTemplate` can be derived for types whose fields also implement `GetTemplate`: ```rust #[derive(Component, GetTemplate)] struct Sprite { image: Handle<Image>, } ``` Internally this produces the following: ```rust #[derive(Template)] struct SpriteTemplate { image: HandleTemplate<Image>, } impl GetTemplate for Sprite { type Template = SpriteTemplate; } ``` Another common use case for templates is `Entity`. With templates we can resolve an identifier of an entity in a scene to the final `Entity` it points to (for example: an entity path or an "entity reference" ... this will be described in detail later). Both `Template` and `GetTemplate` are blanket-implemented for any type that implements both Clone and Default. This means that _most_ types are automatically usable as templates. Neat! ```rust impl<T: Clone + Default> Template for T { type Output = T; fn build_template(&mut self, context: &mut TemplateContext) -> Result<Self::Output> { Ok(self.clone()) } } impl<T: Clone + Default> GetTemplate for T { type Template = T; } ``` It is best to think of `GetTemplate` as an alternative to `Default` for types that require world/spawn context to instantiate. Note that because of the blanket impl, you _cannot_ implement `GetTemplate`, `Default`, and `Clone` together on the same type, as it would result in two conflicting GetTemplate impls. This is also why `Template` has its own `Template::clone_template` method (to avoid using the Clone impl, which would pull in the auto-impl). ### Scenes Templates on their own already check many of the boxes we need for a scene system, but they aren't enough on their own. We want to define scenes as _patches_ of Templates. This allows scenes to inherit from / write on top of other scenes without overwriting fields set in the inherited scene. We want to be able to "resolve" scenes to a final group of templates. This is where the `Scene` trait comes in: ```rust pub trait Scene: Send + Sync + 'static { fn resolve(&self, context: &mut ResolveContext, scene: &mut ResolvedScene) -> Result<(), ResolveSceneError>; fn register_dependencies(&self, _dependencies: &mut Vec<AssetPath<'static>>); } ``` The `ResolvedScene` is a collection of "final" `Template` instances which can be applied to an entity. `Scene::resolve` applies the `Scene` as a "patch" on top of the final `ResolvedScene`. It stores a flat list of templates to be applied to the top-level entity _and_ typed lists of related entities (ex: Children, Observers, etc), which each have their own ResolvedScene. `Scene`s are free to modify these lists, but in most cases they should probably just be pushing to the back of them. `ResolvedScene` can handle both repeated and unique instances of a template of a given type, depending on the context. `Scene::register_dependencies` allows the Scene to register whatever asset dependencies it needs to perform `Scene::resolve`. The scene system will ensure `Scene::resolve` is not called until all of the dependencies have loaded. `Scene` is always _one_ top level / root entity. For "lists of scenes" (such as a list of related entities), we have the `SceneList` trait, which can be used in any place where zero to many scenes are expected. These are separate traits for logical reasons: world.spawn() is a "single entity" action, scene inheritance only makes sense when both scenes are single roots, etc. ### Template Patches The `TemplatePatch` type implements `Scene`, and stores a function that mutates a template. Functionally, a `TemplatePatch` scene will initialize a `Default` value of the patched `Template` if it does not already exist in the `ResolvedScene`, then apply the patch on top of the current Template in the `ResolvedScene`. Types that implement `Template` can generate a `TemplatePatch` like this: ```rust #[derive(Template)] struct MyTemplate { value: usize, } MyTemplate::patch_template(|my_template, context| { my_template.value = 10; }); ``` Likewise, types that implement `GetTemplate` can generate a patch _for their template type_ like this: ```rust #[derive(GetTemplate)] struct Sprite { image: Handle<Image>, } Sprite::patch(|sprite_template| { // note that this is HandleTemplate<Image> sprite.image = "player.png".into(); }) ``` We can now start composing scenes by writing functions that return `impl Scene`! ```rust fn player() -> impl Scene { ( Sprite::patch(|sprite| { sprite.image = "player.png".into(); ), Transform::patch(|transform| { transform.translation.y = 4.0; }), ) } ``` ### The `on()` Observer / event handler Scene `on` is a function that returns a scene that creates an Observer template: ```rust fn player() -> impl Scene { ( Sprite::patch(|sprite| { sprite.image = "player.png".into(); ), on(|jump: On<Jump>| { info!("player jumped!"); }) ) } ``` ### The BSN Format `BSN` is a new specification for defining Bevy Scenes. It is designed to be as Rust-ey as possible, while also eliminating unnecessary syntax and context. The goal is to make defining arbitrary scenes and UIs as easy, delightful, and legible as possible. It is intended to be usable as both an asset format (ex: `level.bsn` files) _and_ defined in code via a `bsn!` macro. These are notably _compatible with each other_. You can define a BSN asset file (ex: in a visual scene editor, such as the upcoming Bevy Editor), then inherit from that and use it in `bsn!` defined in code. ```rust :"player.bsn" Player Sprite { image: "player.png" } Health(10) Transform { translation: Vec3 { y: 4.0 } } on(|jump: On<Jump>| { info!("player jumped!"); }) Children [ ( Hat Sprite { image: "cute_hat.png" } Transform { translation: Vec3 { y: 3.0 } } ) ), (:sword Transform { translation: Vec3 { x: 10. } } ] ``` Note that this PR includes the `bsn!` macro, but it does not include the BSN asset format. It _does_ include all of the in-memory / in-code support for the asset format. All that remains is defining a BSN asset loader, which will be done in a followup. ### The `bsn!` Macro `bsn!` is an _optional_ ergonomic syntax for defining `Scene` expressions. It was built in such a way that Rust Analyzer autocomplete, go-to definition, doc hover, and semantic token syntax highlighting works as expected pretty much everywhere (but there are _some_ gaps and idiosyncrasies at the moment, which I believe we can iron out). It looks like this: ```rust fn player() -> impl Scene { bsn! { Player Sprite { image: "player.png" } Health(10) Transform { translation: Vec3 { y: 4.0 } } on(|jump: On<Jump>| { info!("player jumped!"); }) Children [ ( Hat Sprite { image: "cute_hat.png" } Transform { translation: Vec3 { y: 3.0 } } ) ), (:sword Transform { translation: Vec3 { x: 10. } } ] } } fn sword() -> impl Scene { bsn! { Sword Sprite { image: "sword.png" } } } fn blue_player() -> impl Scene { bsn! { :player Team::Blue Children [ Sprite { image: "blue_shirt.png" } ] } } ``` I'll do a brief overview of each implemented `bsn!` feature now. ### `bsn!`: Patch Syntax When you see a normal "type expression", that resolves to a `TemplatePatch` as defined above. ```rust bsn! { Player { image: "player.png" } } ``` This resolve to the following: ```rust <Player as GetTemplatePatch>::patch(|template| { template.image = "player.png".into(); }) ``` This means you only need to define the fields you actually want to set! Notice the implicit `.into()`. Wherever possible, `bsn!` provides implicit `into()` behavior, which allows developers to skip defining wrapper types, such as the `HandleTemplate<Image>` expected in the example above. This also works for nested struct-style types: ```rust bsn! { Transform { translation: Vec3 { x: 1.0 } } } ``` Note that you can just define the type name if you don't care about setting specific field values / just want to add the component: ```rust bsn! { Transform } ``` To add multiple patches to the entity, just separate them with spaces or newlines: ```rust bsn! { Player Transform } ``` Enum patching is also supported: ```rust #[derive(Component, GetTemplate)] enum Emotion { Happy { amount: usize, quality: HappinessQuality }, Sad(usize), } bsn! { Emotion::Happy { amount: 10. } } ``` Notably, when you derive GetTemplate for an enum, you get default template values for _every_ variant: ```rust // We can skip fields for this variant because they have default values bsn! { Emotion::Happy } // We can also skip fields for this variant bsn! { Emotion::Sad } ``` This means that unlike the `Default` trait, enums that derive `GetTemplate` are "fully patchable". If a patched variant matches the current template variant, it will just write fields on top. If it corresponds to a different variant, it initializes that variant with default values and applies the patch on top. For practical reasons, enums only use this "fully patchable" approach when in "top-level scene entry patch position". _Nested_ enums (aka fields on patches) require specifying _every_ value. This is because the majority of types in the Rust and Bevy ecosystem will not derive `GetTemplate` and therefore will break if we try to create default variants values for them. I think this is the right constraint solve in terms of default behaviors, but we can discuss how to support both nested scenarios effectively. Constructors also work (note that constructor args are _not_ patched. you must specify every argument). A constructor patch will fully overwrite the current value of the Template. ```rust bsn! { Transform::from_xyz(1., 2., 3.) } ``` You can also use type-associated constants, which will also overwrite the current value of the template: ```rust bsn! { Transform::IDENTITY } ``` If you have a type that does not currently implement Template/GetTemplate, you have two options: ```rust bsn! { // This will return a Template that produces the returned type. // `context` has World access! template(|context| { Ok(TextFont { font: context .resource::<AssetServer>() .load("fonts/FiraSans-Bold.ttf").into(), ..default() }) }) // This will return the value as a Template template_value(Foo::Bar) } ``` ### `bsn!` Template patch syntax Types that are expressed using the syntax we learned above are expected to implement `GetTemplate`. If you want to patch a `Template` _directly_ by type name (ex: your Template is not paired with a GetTemplate type), you can do so using `@` syntax: ```rust struct MyTemplate { value: usize, } impl Template for MyTemplate { /* impl here */ } bsn! { @MyTemplate { value: 10. } } ``` In most cases, BSN encourages you to work with the _final_ type names (ex: you type `Sprite`, not `SpriteTemplate`). However in cases where you really want to work with the template type directly (such as custom / manually defined templates), "Template patch syntax" lets you do that! ### `bsn!`: Inline function syntax You can call functions that return `Scene` impls inline. The `on()` function that adds an Observer (described above) is a particularly common use case ```rust bsn! { Player on(|jump: On<Jump>| { info!("Player jumped"); }) } ``` ### `bsn!`: Relationship Syntax `bsn!` provides native support for spawning related entities, in the format `RelationshipTarget [ SCENE_0, ..., SCENE_X ]`: ```rust bsn! { Node { width: Px(10.) } Children [ Node { width: Px(4.0) }, (Node { width: Px(4.0) } BackgroundColor(srgb(1.0, 0.0, 0.0)), ] } ``` Note that related entity scenes are comma separated. Currently they can either be flat _or_ use `()` to group them: ```rust bsn! { Children [ // Child 1 Node BorderRadius::MAX, // Child 2 (Node BorderRadius::MAX), ] } ``` It is generally considered best practice to wrap related entities with more than one entry in `()` to improve legibility. ### `bsn!`: Expression Syntax `bsn!` supports expressions in a number of locations using `{}`: ```rust let x: u32 = 1; let world = "world"; bsn! { // Field position expressions Health({ x + 2 }) Message { text: {format!("hello {world}")} } } ``` Expressions in field position have implicit `into()`. Expressions are also supported in "scene entry" position, enabling nesting `bsn!` inside `bsn!`: ```rust let position = bsn! { Transform { translation: Vec3 { x: 10. } } }; bsn! { Player {position} } ``` ### `bsn!`: Inline variables You can specify variables inline: ```rust let black = Color::BLACK; bsn! { BackgroundColor(black) } ``` This also works in "scene entry" position: ```rust let position = bsn! { Transform { translation: Vec3 { x: 10. } } }; bsn! { Player position } ``` ### Inheritance `bsn!` uses `:` to designate "inheritance". Unlike defining scenes inline (as mentioned above), this will _pre-resolve_ the inherited scene, making your current scene cheaper to spawn. This is great when you inherit from large scene (ex: an asset defined by a visual editor). Scenes can only inherit from one scene at a time, and it must be defined first. You can inherit from scene assets like this: ```rust fn red_button() -> impl Scene { bsn! { :"button.bsn" BackgroundColor(RED) } } ``` Note that while there is currently no implemented `.bsn` asset format, you can still test this using `AssetServer::load_with_path`. You can also inherit from functions that return a `Scene`: ```rust fn button() -> impl Scene { bsn! { Button Children [ Text("Button") ] } } fn red_button() -> impl Scene { bsn! { :button BackgroundColor(RED) } } ``` Note that because inheritance is cached / pre-resolved, function inheritance does not support function parameters. You can still use parameterized scene functions by defining them directly in the scene (rather than using inheritance): ```rust fn button(text: &str) -> impl Scene { bsn! { Button Children [ Text(text) ] } } fn red_button() -> impl Scene { bsn! { button("Click Me") BackgroundColor(RED) } } ``` Related entities can also inherit: ```rust bsn! { Node Children [ (:button BackgroundColor(RED)), (:button BackgroundColor(BLUE)), ] } ``` Inheritance concatenates related entities: ```rust fn a() -> impl Scene { bsn! { Children [ Name("1"), Name("2"), ] } } fn b() -> impl Scene { /// this results in Children [ Name("1"), Name("2"), Name("3") ] bsn! { :a Children [ Name("3"), ] } } ``` ### `bsn_list!` / SceneList Relationship expression syntax `{}` expects a SceneList. Many things, such as `Vec<S: Scene>` implement `SceneList` allowing for some cool patterns: ```rust fn inventory() -> impl Scene { let items = (0..10usize) .map(|i| bsn! {Item { size: {i} }}) .collect::<Vec<_>>(); bsn! { Inventory [ {items} ] } } ``` The `bsn_list!` macro allows defining a list of BSN entries (using the same syntax as relationships). This returns a type that implements `SceneList`, making it useable in relationship expressions! ```rust fn container() -> impl Scene { let children = bsn_list! [ Name("Child1"), Name("Child2"), (Name("Child3") FavoriteChild), ] bsn! { Container [ {children} ] } } ``` This, when combined with inheritance, means you can build abstractions like this: ```rust fn list_widget(children: impl SceneList) -> impl Scene { bsn! { Node { width: Val::Px(1.0) } Children [ Text("My List:") {children} ] } } fn ui() -> impl Scene { bsn! { Node Children [ list_widget({bsn_list! [ Node { width: Px(4.) }, Node { width: Px(5.) }, ]}) ] } } ``` ### `bsn!`: Name Syntax You can quickly define `Name` components using `#Name` shorthand. ```rust bsn! { #Root Node Children [ (#Child1, Node), (#Child2, Node), ] } ``` `#MyName` produces the `Name("MyName")` component output. Within a given `bsn!` or `bsn_list!` scope, `#Name` can _also_ be used in _value position_ as an `Entity` Template: ```rust #[derive(Component, GetTemplate)] struct UiRoot(Entity); #[derive(Component, GetTemplate)] struct CurrentButton(Entity); bsn! { #Root CurrentButton(#MyButton) Children [ ( #MyButton, UiRoot(#Root) ) ] } ``` These behave a bit like variable names. In the context of inheritance and embedded scenes, `#Name` is only valid within the current "scene scope": ```rust fn button() -> impl Scene { bsn! { #Button Node Children [ ButtonRef(#Button) ] } } fn red_button() -> impl Scene { bsn! { :button // #Button is not valid here, but #MyButton // will refer to the same final entity as #Button #MyButton Children [ AnotherReference(#MyButton) ] } } ``` In the example above, because `#MyButton` is defined "last" / is the most "specific" `Name`, the spawned entity will have `Name("MyButton")` Name references are allowed to conflict across inheritance scopes and they will not interfere with each other. `#Name` can also be used in the context of `bsn_list!`, which enables defining graph structures: ```rust bsn_list! [ (#Node1, Sibling(#Node2)), (#Node2, Sibling(#Node1)), ] ``` ### Name Restructure The core name component has also been restructured to play nicer with `bsn!`. The impl on `main` requires `Name::new("MyName")`. By making the name string field public and internalizing the prehash logic on that field, and utilizing implicit `.into()`, we can now define names like this: ```rust bsn! { Name("Root") Children [ Name("Child1"), Name("Child2"), ] } ``` ### BSN Spawning You can spawn scenes using `World::spawn_scene` and `Commands::spawn_scene`: ```rust world.spawn_scene(bsn! { Node Children [ (Node BackgroundColor(RED)) ] })?; commands.spawn_scene(widget()); ``` The `spawn_scene` operation happens _immediately_, and therefore assumes that all of the `Scene`'s dependencies have been loaded (or alternatively, that there are no dependencies). If the scene has a dependency that hasn't been loaded yet, `World::spawn_scene` will return an error (or log an error in the context of `Commands::spawn_scene`). If your scene has dependencies, you can use `World::queue_spawn_scene` and `Commands::queue_spawn_scene`. This will spawn the entity as soon as all of the `Scene`'s dependencies have been loaded. ```rust // This will spawn the entity once the "player.bsn" asset is loaded world.queue_spawn_scene(bsn! { :"player.bsn" Transform { position: Vec3 { x: 10. } } }); ``` There are also `spawn_scene_list` variants for everything above: ```rust world.spawn_scene_list(bsn_list! [ button("Ok"), button("Cancel"), ]) ``` `EntityWorldMut` and `EntityCommands` also have some new functionality: ```rust entity.queue_spawn_related_scene::<Children>(bsn_list! [ (:"player.bsn", #Player1), (:"player.bsn", #Player2), ]); ``` ```rust entity.apply_scene(bsn! { Transform { position: Vec3 { x: 10. } } })?; ``` For scene assets, you can also just add the `ScenePatchInstance(handle)` component, just like the old Bevy scene system. ### VariantDefaults derive `GetTemplate` automatically generates default values for enum Template variants. But for types that don't use `GetTemplate`, I've also implemented a `VariantDefaults` derive that also generates these methods. ## What's Next? ### Must happen before 0.19 - [ ] **Sort out `bevy_scene` vs `bevy_scene2`**: The current plan is to rename `bevy_scene` to `bevy_ecs_serialization`, and remove "scene" terminology from it. That then frees up `bevy_scene2` to be renamed to `bevy_scene`. The current `bevy_scene` will need to exist for awhile in parallel to BSN, as BSN is not yet ready for "full world serialization" scenarios. - [x] ~~**Resolve the Default Handle situation**: Currently, to provide Template support for `Handle`, it implements `GetTemplate`. This of course conflicts with `impl Default for Handle`. This is pretty disruptive to non-BSN users (which is currently everyone). We'll want to sort out a middleground solution in the short term that ideally allows us to keep `impl Default for Handle` during the transition.~~ - Resolved this by using a [specialization trick](https://github.com/bevyengine/bevy/pull/23413#discussion_r2961341173) - [ ] Nested `bsn!` `Scene` tuples to surpass tuple impl limits ### Ideally before 0.19 We likely won't land all of these. The plan is to (ideally) land this PR before Bevy 0.19 RC1, then _maybe_ land a couple more of these before - [ ] **Feathers BSN Port**: Largely already done. Just need to reconcile with current state of main. This will help BSN land well, so landing it alongside BSN is a high priority. - [ ] **ResolvedScene-as-dynamic-bundle**: ResolvedScene should insert all of the components at once as a single bundle, rather than one-by-one, which is really bad from an archetype move perspective. Without this, using `world.spawn_scene(scene)` as a `world.spawn(bundle)` replacement will result in a pretty significant performance reduction. - [ ] **`#Name` references in more places**: The UI eventing scenario _really_ wants `#Name` to be usable in closures. This would functionally be expressed as a template that returns a closure that accesses a specific entity. This unlocks a lot of value for UI devs, so ideally it lands alongside BSN. - [ ] **Top-down vs bottom-up spawn order**: Currently BSN follows the normal bevy top-down spawn order. I think we should heavily consider spawning bottom-up, in the interest of making scene contents available to "higher level" components in their lifecycle events (ex: a `Player` component accessing nested entities like "equipment" when inserted). If we decide to keep things as they are, we probably want to introduce additional "scene ready" entity events that trigger "bottom up". - [ ] **Inline field value expressions**: Support cases such as `px(10).all() - [ ] **Add EntityPath to EntityTemplate**: Support resolving entity paths (ex: `"Root/Child1/GrandChild1"`). This is relatively low hanging fruit, especially if we switch to bottom-up spawning order. - [ ] **Function Inheritance Caching**: Currently only scene asset inheritance is pre-computed / cached. For consistency / predictability / optimizations, function inheritance (ex `:button`) should also be cached. - [ ] **`derive(GetTemplate)` generics ergonomics**: Currently this requires casting spells: `T: GetTemplate<Template: Default + Template<Output = T>>` ### Near Future - [ ] **BSN Asset Format**: Add a `.bsn` parser / AssetLoader that can produce the current `ScenePatch` assets. - [ ] **Struct-style inheritance**: It would be nice to be able to do something like `:Button { prop } ` instead of `:button(prop)`. I'd really like us to explore this being component-tied (ex: associate a scene with a Button component). - [ ] **Descendant Patching**: It should be possible to "reach in" to an inherited scene and patch one of its descendants / children. - [ ] **Optimize Related Entity Spawning**: This currently inserts the relationship component first, then spawns the related scene. This results in an unnecessary archetype move. - [ ] Observers as relationships - [ ] **Scene-owned-entities**: Currently when spawning a `Scene`, every entity defined in the scene is instantiated. Some scenarios would benefit from Scene instances _sharing_ some unique entity. For example: defining assets _inside_ of scenes (this would pair nicely with Assets as Entities) , sharing Observer entities, etc. - [ ] The `touch_type::<Nested>()` approach could be replaced with `let x: &mut Nested` for actual type safety (and probably better autocomplete). - [ ] Fix Rust Analyzer autocomplete bug that fails to resolve functions and enums for `<Transform as GetTemplate>::Template::from_transform()` - [ ] Fix Rust Analyzer autocomplete bug that also suggests function names when type struct field names. This _should_ be fixed by using irrefutable `if let` statements. And it would probably allow us to reuse macro code across enums / structs (and avoid needing to use PathType inference in this case, which has gnarly corner cases). ### Longer Term - [ ] **`bsn!` hot patching via subsecond**: [Proof of concept here](https://github.com/cart/bevy/pull/36) - [ ] **Reactivity**: This has been proven out [here](https://github.com/viridia/bevy_reactor/) - [ ] **BSN Sets**: See the [old design doc](https://github.com/bevyengine/bevy/discussions/14437) for the design space I'm talking about here * This would also allow expressing "flattened" forms of BSN, which makes diffs easier to read in some case - [ ] **World to BSN**: If we can support this, BSN can be used for things like saving Worlds to disk. This might also be useful for building scene editors. --------- Co-authored-by: andriyDev <andriydzikh@gmail.com> Co-authored-by: Nico Zweifel <34443492+NicoZweifel@users.noreply.github.com> Co-authored-by: Alice Cecile <alice.i.cecile@gmail.com> Co-authored-by: copygirl <copygirl@mcft.net> |
||
|
|
ff3c2e4c04 |
Make passing in the type registry for scene ser/de explicit. (#23401)
# Objective - Previously, you needed to insert the type registry into the scene you're serializing, which results in weird behavior since some users are extracting the resources out of the scene (which would include the type registry). ## Solution - Make the scene APIs explicitly take a type registry so that users are less likely to get it wrong, and no longer need to insert the registry into the world/scene being serialized. ## Testing - Updated tests. Tests pass - The scene example still works. |
||
|
|
2e2b29eb33 |
Allow serializing and deserializing handles during reflection. (#23329)
# Objective - Partly addresses #15518 - this is a big issue but the handle part is all resolved! - Make the existing scene system much more useful! ## Solution - Create a `ReflectSerializerProcessor` and `ReflectDeserializerProcessor` that "hijacks" the ser/de of handles to just write out the path being loaded (or the UUID), and loads the assets on deserialization. - This was made possible by #15482! Very useful! This *should* also be composable with other processors by just creating a wrapper that calls both processors in series. - Make the scene ser/de use this processor by default. Now handles just work in scenes! - Update the scene example to show a scene that includes a scene! Nested scenes!! I've left a TODO that the scene system could expose the `EphemeralHandleBehavior` so users can choose to be more strict. It's probably not a big deal to solve IMO. Also I had to call it `AssetServer::load_erased` since we already have an `AssetServer::load_untyped` which tries to guess the asset type based on the path. `AssetServer::load_erased` in contrast knows what type it wants, just at runtime. It feels like we should have a less adhoc API here, one where we know we've covered the cross product of all features. Something to think about! ## Testing - Added a test for the processors that they can roundtrip a type with handles. - Added a test that the scene system can roundtrip components with handles. - Updated the scene example and it spawns with the handle correctly! |
||
|
|
6ca4769128 |
Minimal responsive FontSize support (#22614)
# Objective
Add responsive font sizes supporting rem and viewport units to
`bevy_text` with minimal changes to the APIs and systems.
## Solution
Introduce a new `FontSize` enum:
```rust
pub enum FontSize {
/// Font Size in logical pixels.
Px(f32),
/// Font size as a percentage of the viewport width.
Vw(f32),
/// Font size as a percentage of the viewport height.
Vh(f32),
/// Font size as a percentage of the smaller of the viewport width and height.
VMin(f32),
/// Font size as a percentage of the larger of the viewport width and height.
VMax(f32),
/// Font Size relative to the value of the `RemSize` resource.
Rem(f32),
}
```
This replaces the `f32` value of `TextFont`'s `font_size` field.
The viewport variants work the same way as their respective `Val`
counterparts.
`Rem` values are multiplied by the value of the `RemSize` resource
(which newtypes an `f32`).
`FontSize` provides an `eval` method that takes a logical viewport size
and rem base size and returns an `f32` logical font size. The resolved
logical font size is then written into the `Attributes` passed to Cosmic
Text by `TextPipeline::update_buffer`.
Any text implementation using `bevy_text` must now provide viewport and
rem base values when calling `TextPipeline::update_buffer` or
`create_measure`.
`Text2d` uses the size of the primary window to resolve viewport values
(or `Vec2::splat(1000)` if no primary window is found). This is a
deliberate compromise, a single `Text2d` can be rendered to multiple
viewports using `RenderLayers`, so it's difficult to find a rule for
which viewport size should be chosen.
### Change detection
`ComputedTextBlock` has two new fields: `uses_viewport_sizes` and
`uses_rem_sizes`, which are set to true in `TextPipeline::update_buffer`
iff any text section in the block uses viewport or rem font sizes,
respectively.
The `ComputedTextBlock::needs_rerender` method has been modified to take
take two bool parameters:
```rust
pub fn needs_rerender(
&self,
is_viewport_size_changed: bool,
is_rem_size_changed: bool,
) -> bool {
self.needs_rerender
|| (is_viewport_size_changed && self.uses_viewport_sizes)
|| (is_rem_size_changed && self.uses_rem_sizes)
}
```
This ensures that text reupdates will also be scheduled if one of the text section's uses a viewport font size and the local viewport size changed, or if one of the text section's uses a rem font size and the rem size changed.
#### Limitations
There are some limitations because we don't have any sort of font style inheritance yet:
* "rem" units aren't proper rem units, and just based on the value of a resource.
* "em" units are resolved based on inherited font size, so can't be implemented without inheritance support.
#### Notes
* This PR is quite small and not very technical. Reviewers don't need to be especially familiar with `bevy_text`. Most of the changes are to the examples.
* We could consider using `Val` instead of `FontSize`, then we could use `Val`'s constructor functions which would be much nicer, but some variants might not have sensible interpretations in both UI and Text2d contexts. Also we'd have to make `Val` accessible to `bevy_text`.
## Testing
The changes to the text systems are relatively trivial and easy to understand. I already added a minor change to the `text` example to use `Vh` font size for the "hello bevy" text in the bottom right corner. If you change the size of the window, you should see the text change size in response. The text initially flickers before it updates because of some unrelated asset/image changes that mean that font textures aren't ready until the frame after the text update that changes the font size.
Most of the example migrations were automated using regular expressions, and there are bound to be mistakes in those changes. It's infeasible to check every single example thoroughly, but it's early enough in the release cycle that I don't think we should be too worried if a few bugs slip in.
---------
Co-authored-by: Kevin Chen <chen.kevin.f@gmail.com>
|
||
|
|
8e52194d70 |
CI fixes for Rust 1.89 (#20462)
Adopted from #20456 Notes: * The origin of the `dead_code` lints were coming from the `ShaderType ` derive macro. This has been reported as https://github.com/teoxoy/encase/issues/102, and a temporary workspace-wide `allow` added to the top level Cargo.toml. * One of the lints pointed out that `PartialEq` and `Eq` may not work as expected for function pointers, so `CloneBehavior` no longer implements either trait, and pattern matching is used in instead. Original PR Description: ># Objective > >Unbreak CI. > > ## Solution > > Fix the lints. > >I've opted for anonymous lifetimes in every revealed case so far; please let me know if you think I should used named lifetimes in specific cases and why. > >## Testing > >Is CI green? > >## Context > > This lint originally had a much larger splash damage, with fairly negative effects on Bevy. See https://github.com/rust-lang/rust/issues/131725. > > The more restricted former is much more helpful, despite the large diff in this PR. Bevy is a large code base! > >## TODO > >- [x] discuss proposed lifetime lint fixes >,- [x] use cargo clippy --fix to fix newly uncovered docs misformatting >- [x] fix newly revealed dead code issues >- [x] ensure CI is green --------- Co-authored-by: Alice Cecile <alice.i.cecile@gmail.com> Co-authored-by: Mike <mike.hsu@gmail.com> |
||
|
|
40b6940356 |
Remove Manual Type Registrations (#20435)
# Objective Thanks to #15030, we no longer need manual type registrations. This should only be merged after #15030. ## Solution Remove all manual (non generic) type registrations. |
||
|
|
92e65d5eb1 | Upgrade to Rust 1.88 (#19825) | ||
|
|
7398d33b79 |
Panic on failure in scene example (#17812)
# Objective Fixes #17810 Y'all picked the option I already implemented, yay. ## Solution Add a system that panics if the load state of an asset is `Failed`. ## Testing `cargo run --example scene` - Tested with valid scene file - Introduced a syntax error in the scene file - Deleted the scene file |
||
|
|
9c5f5b8455 |
docs: rewrite scene loading example documentation for clarity and depth (#17434)
This commit overhauls the documentation in the Bevy scene loading example. It adds thorough explanatory comments to guide new Rust and Bevy developers. The rewritten docs clarify how to: - Register types for reflection, enabling serialization and dynamic property access - Skip serializing certain fields with `#[reflect(skip_serializing)]` - Use `FromWorld` for components that require runtime initialization - Store and serialize `Resources` in scene files - Load scenes using a `DynamicSceneRoot` and handle updates in a system - Serialize a brand-new scene to a separate file asynchronously using `IoTaskPool` These additions aim to provide a clear, step-by-step reference that demonstrates how to implement a scene-based workflow, making it easier for beginners and experienced developers alike to use Bevy’s scene system effectively. --------- Co-authored-by: Alice Cecile <alice.i.cecile@gmail.com> |
||
|
|
3c829d7f68 |
Remove everything except Instant from bevy_utils::time (#17158)
# Objective - Contributes to #11478 - Contributes to #16877 ## Solution - Removed everything except `Instant` from `bevy_utils::time` ## Testing - CI --- ## Migration Guide If you relied on any of the following from `bevy_utils::time`: - `Duration` - `TryFromFloatSecsError` Import these directly from `core::time` regardless of platform target (WASM, mobile, etc.) If you relied on any of the following from `bevy_utils::time`: - `SystemTime` - `SystemTimeError` Instead import these directly from either `std::time` or `web_time` as appropriate for your target platform. ## Notes `Duration` and `TryFromFloatSecsError` are both re-exports from `core::time` regardless of whether they are used from `web_time` or `std::time`, so there is no value gained from re-exporting them from `bevy_utils::time` as well. As for `SystemTime` and `SystemTimeError`, no Bevy internal crates or examples rely on these types. Since Bevy doesn't have a `Time<Wall>` resource for interacting with wall-time (and likely shouldn't need one), I think removing these from `bevy_utils` entirely and waiting for a use-case to justify inclusion is a reasonable path forward. |
||
|
|
015f2c69ca |
Merge Style properties into Node. Use ComputedNode for computed properties. (#15975)
# Objective Continue improving the user experience of our UI Node API in the direction specified by [Bevy's Next Generation Scene / UI System](https://github.com/bevyengine/bevy/discussions/14437) ## Solution As specified in the document above, merge `Style` fields into `Node`, and move "computed Node fields" into `ComputedNode` (I chose this name over something like `ComputedNodeLayout` because it currently contains more than just layout info. If we want to break this up / rename these concepts, lets do that in a separate PR). `Style` has been removed. This accomplishes a number of goals: ## Ergonomics wins Specifying both `Node` and `Style` is now no longer required for non-default styles Before: ```rust commands.spawn(( Node::default(), Style { width: Val::Px(100.), ..default() }, )); ``` After: ```rust commands.spawn(Node { width: Val::Px(100.), ..default() }); ``` ## Conceptual clarity `Style` was never a comprehensive "style sheet". It only defined "core" style properties that all `Nodes` shared. Any "styled property" that couldn't fit that mold had to be in a separate component. A "real" style system would style properties _across_ components (`Node`, `Button`, etc). We have plans to build a true style system (see the doc linked above). By moving the `Style` fields to `Node`, we fully embrace `Node` as the driving concept and remove the "style system" confusion. ## Next Steps * Consider identifying and splitting out "style properties that aren't core to Node". This should not happen for Bevy 0.15. --- ## Migration Guide Move any fields set on `Style` into `Node` and replace all `Style` component usage with `Node`. Before: ```rust commands.spawn(( Node::default(), Style { width: Val::Px(100.), ..default() }, )); ``` After: ```rust commands.spawn(Node { width: Val::Px(100.), ..default() }); ``` For any usage of the "computed node properties" that used to live on `Node`, use `ComputedNode` instead: Before: ```rust fn system(nodes: Query<&Node>) { for node in &nodes { let computed_size = node.size(); } } ``` After: ```rust fn system(computed_nodes: Query<&ComputedNode>) { for computed_node in &computed_nodes { let computed_size = computed_node.size(); } } ``` |
||
|
|
6f7d0e5725 |
split up TextStyle (#15857)
# Objective
Currently text is recomputed unnecessarily on any changes to its color,
which is extremely expensive.
## Solution
Split up `TextStyle` into two separate components `TextFont` and
`TextColor`.
## Testing
I added this system to `many_buttons`:
```rust
fn set_text_colors_changed(mut colors: Query<&mut TextColor>) {
for mut text_color in colors.iter_mut() {
text_color.set_changed();
}
}
```
reports ~4fps on main, ~50fps with this PR.
## Migration Guide
`TextStyle` has been renamed to `TextFont` and its `color` field has
been moved to a separate component named `TextColor` which newtypes
`Color`.
|
||
|
|
c2c19e5ae4 |
Text rework (#15591)
**Ready for review. Examples migration progress: 100%.** # Objective - Implement https://github.com/bevyengine/bevy/discussions/15014 ## Solution This implements [cart's proposal](https://github.com/bevyengine/bevy/discussions/15014#discussioncomment-10574459) faithfully except for one change. I separated `TextSpan` from `TextSpan2d` because `TextSpan` needs to require the `GhostNode` component, which is a `bevy_ui` component only usable by UI. Extra changes: - Added `EntityCommands::commands_mut` that returns a mutable reference. This is a blocker for extension methods that return something other than `self`. Note that `sickle_ui`'s `UiBuilder::commands` returns a mutable reference for this reason. ## Testing - [x] Text examples all work. --- ## Showcase TODO: showcase-worthy ## Migration Guide TODO: very breaking ### Accessing text spans by index Text sections are now text sections on different entities in a hierarchy, Use the new `TextReader` and `TextWriter` system parameters to access spans by index. Before: ```rust fn refresh_text(mut query: Query<&mut Text, With<TimeText>>, time: Res<Time>) { let text = query.single_mut(); text.sections[1].value = format_time(time.elapsed()); } ``` After: ```rust fn refresh_text( query: Query<Entity, With<TimeText>>, mut writer: UiTextWriter, time: Res<Time> ) { let entity = query.single(); *writer.text(entity, 1) = format_time(time.elapsed()); } ``` ### Iterating text spans Text spans are now entities in a hierarchy, so the new `UiTextReader` and `UiTextWriter` system parameters provide ways to iterate that hierarchy. The `UiTextReader::iter` method will give you a normal iterator over spans, and `UiTextWriter::for_each` lets you visit each of the spans. --------- Co-authored-by: ickshonpe <david.curthoys@googlemail.com> Co-authored-by: Carter Anderson <mcanders1@gmail.com> |
||
|
|
25bfa80e60 |
Migrate cameras to required components (#15641)
# Objective Yet another PR for migrating stuff to required components. This time, cameras! ## Solution As per the [selected proposal](https://hackmd.io/tsYID4CGRiWxzsgawzxG_g#Combined-Proposal-1-Selected), deprecate `Camera2dBundle` and `Camera3dBundle` in favor of `Camera2d` and `Camera3d`. Adding a `Camera` without `Camera2d` or `Camera3d` now logs a warning, as suggested by Cart [on Discord](https://discord.com/channels/691052431525675048/1264881140007702558/1291506402832945273). I would personally like cameras to work a bit differently and be split into a few more components, to avoid some footguns and confusing semantics, but that is more controversial, and shouldn't block this core migration. ## Testing I ran a few 2D and 3D examples, and tried cameras with and without render graphs. --- ## Migration Guide `Camera2dBundle` and `Camera3dBundle` have been deprecated in favor of `Camera2d` and `Camera3d`. Inserting them will now also insert the other components required by them automatically. |
||
|
|
eb51b4c28e |
Migrate scenes to required components (#15579)
# Objective A step in the migration to required components: scenes! ## Solution As per the [selected proposal](https://hackmd.io/@bevy/required_components/%2FPJtNGVMMQhyM0zIvCJSkbA): - Deprecate `SceneBundle` and `DynamicSceneBundle`. - Add `SceneRoot` and `DynamicSceneRoot` components, which wrap a `Handle<Scene>` and `Handle<DynamicScene>` respectively. ## Migration Guide Asset handles for scenes and dynamic scenes must now be wrapped in the `SceneRoot` and `DynamicSceneRoot` components. Raw handles as components no longer spawn scenes. Additionally, `SceneBundle` and `DynamicSceneBundle` have been deprecated. Instead, use the scene components directly. Previously: ```rust let model_scene = asset_server.load(GltfAssetLabel::Scene(0).from_asset("model.gltf")); commands.spawn(SceneBundle { scene: model_scene, transform: Transform::from_xyz(-4.0, 0.0, -3.0), ..default() }); ``` Now: ```rust let model_scene = asset_server.load(GltfAssetLabel::Scene(0).from_asset("model.gltf")); commands.spawn(( SceneRoot(model_scene), Transform::from_xyz(-4.0, 0.0, -3.0), )); ``` |
||
|
|
522d82b21a |
Fixing text sizes for examples (#15190)
# Objective - Fixes #14265 ## Solution - Go through Pixel Eagle examples (and examples all in all) - If default size is used it is usually left there - If size of font is touched try dividing with 1.2 and round it to nearest whole number ## Testing - Run example before and after - Make sure examples text are readable or like before cosmic-text change --- ## Showcase Before:  After:  |
||
|
|
6f7c554daa |
Fix common capitalization errors in documentation (#14562)
WASM -> Wasm MacOS -> macOS Nothing important, just something that annoyed me for a while :) |
||
|
|
760c645de1 |
Fix TypeRegistry use in dynamic scene (#12715)
Adopted from and closes https://github.com/bevyengine/bevy/pull/9914 by @djeedai # Objective Fix the use of `TypeRegistry` instead of `TypeRegistryArc` in dynamic scene and its serializer. Rename `DynamicScene::serialize_ron()` into `serialize()` to highlight the fact this is not about serializing to RON specifically, but rather about serializing to the official Bevy scene format (`.scn` / `.scn.ron`) which the `SceneLoader` can deserialize (and which happens to be based in RON, but that not the object here). Also make the link with the documentation of `SceneLoader` so users understand the full serializing cycle of a Bevy dynamic scene. Document `SceneSerializer` with an example showing how to serialize to a custom format (here: RON), which is easily transposed to serializing into any other format. Fixes #9520 ## Changelog ### Changed * `SceneSerializer` and all related serializing helper types now take a `&TypeRegistry` instead of a `&TypeRegistryArc`. ([SceneSerializer needlessly uses specifically &TypeRegistryArc #9520](https://github.com/bevyengine/bevy/issues/9520)) * `DynamicScene::serialize_ron()` was renamed to `serialize()`. ## Migration Guide * `SceneSerializer` and all related serializing helper types now take a `&TypeRegistry` instead of a `&TypeRegistryArc`. You can upgrade by getting the former from the latter with `TypeRegistryArc::read()`, _e.g._ ```diff let registry_arc: TypeRegistryArc = [...]; - let serializer = SceneSerializer(&scene, ®istry_arc); + let registry = registry_arc.read(); + let serializer = SceneSerializer(&scene, ®istry); ``` * Rename `DynamicScene::serialize_ron()` to `serialize()`. --------- Co-authored-by: Jerome Humbert <djeedai@gmail.com> Co-authored-by: Alice Cecile <alice.i.cecil@gmail.com> Co-authored-by: Gino Valente <49806985+MrGVSV@users.noreply.github.com> Co-authored-by: James Liu <contact@jamessliu.com> |
||
|
|
2b7a3b2a55 |
reflect: treat proxy types correctly when serializing (#12024)
# Objective - Fixes #12001. - Note this PR doesn't change any feature flags, however flaky the issue revealed they are. ## Solution - Use `FromReflect` to convert proxy types to concrete ones in `ReflectSerialize::get_serializable`. - Use `get_represented_type_info() -> type_id()` to get the correct type id to interact with the registry in `bevy_reflect::serde::ser::get_serializable`. --- ## Changelog - Registering `ReflectSerialize` now imposes additional `FromReflect` and `TypePath` bounds. ## Migration Guide - If `ReflectSerialize` is registered on a type, but `TypePath` or `FromReflect` implementations are omitted (perhaps by `#[reflect(type_path = false)` or `#[reflect(from_reflect = false)]`), the traits must now be implemented. --------- Co-authored-by: Alice Cecile <alice.i.cecile@gmail.com> Co-authored-by: Gino Valente <49806985+MrGVSV@users.noreply.github.com> |
||
|
|
961d49f906 |
scene example: remove unnecessary reflect(FromWorld) (#11938)
# Objective - `#[reflect(FromWorld)]` was added to `ComponentB` somewhere between `v0.12.1` and `v0.13.0`, but it is unnecessary ## Solution - remove the unnecessary `FromWorld` |
||
|
|
eff96e20a0 |
Add ReflectFromWorld and replace the FromWorld requirement on ReflectComponent and ReflectBundle with FromReflect (#9623)
# Objective
- `FromType<T>` for `ReflectComponent` and `ReflectBundle` currently
require `T: FromWorld` for two reasons:
- they include a `from_world` method;
- they create dummy `T`s using `FromWorld` and then `apply` a `&dyn
Reflect` to it to simulate `FromReflect`.
- However `FromWorld`/`Default` may be difficult/weird/impractical to
implement, while `FromReflect` is easier and also more natural for the
job.
- See also
https://discord.com/channels/691052431525675048/1146022009554337792
## Solution
- Split `from_world` from `ReflectComponent` and `ReflectBundle` into
its own `ReflectFromWorld` struct.
- Replace the requirement on `FromWorld` in `ReflectComponent` and
`ReflectBundle` with `FromReflect`
---
## Changelog
- `ReflectComponent` and `ReflectBundle` no longer offer a `from_world`
method.
- `ReflectComponent` and `ReflectBundle`'s `FromType<T>` implementation
no longer requires `T: FromWorld`, but now requires `FromReflect`.
- `ReflectComponent::insert`, `ReflectComponent::apply_or_insert` and
`ReflectComponent::copy` now take an extra `&TypeRegistry` parameter.
- There is now a new `ReflectFromWorld` struct.
## Migration Guide
- Existing uses of `ReflectComponent::from_world` and
`ReflectBundle::from_world` will have to be changed to
`ReflectFromWorld::from_world`.
- Users of `#[reflect(Component)]` and `#[reflect(Bundle)]` will need to
also implement/derive `FromReflect`.
- Users of `#[reflect(Component)]` and `#[reflect(Bundle)]` may now want
to also add `FromWorld` to the list of reflected traits in case their
`FromReflect` implementation may fail.
- Users of `ReflectComponent` will now need to pass a `&TypeRegistry` to
its `insert`, `apply_or_insert` and `copy` methods.
|
||
|
|
e581d74f7d |
Mention DynamicSceneBuilder in scene example (#10441)
# Objective Make ```DynamicSceneBuilder``` more visible to new bevy learners! ```DynamicSceneBuilder``` is likely to be the most appropriate tool to use when creating dynamic scenes in all but the simplest scenarios. However, it's not mentioned in the scene example. This PR aims to fix this. ## Solution I've modified the comment above where the ```DynamicScene``` is created to note that ```DynamicSceneBuilder``` can also be used to create the scene. I believe this is the best approach to introduce ```DynamicSceneBuilder``` without adding additional complexity to the example. |
||
|
|
1918608b02 |
Update default ClearColor to better match Bevy's branding (#10339)
# Objective - Changes the default clear color to match the code block color on Bevy's website. ## Solution - Changed the clear color, updated text in examples to ensure adequate contrast. Inconsistent usage of white text color set to use the default color instead, which is already white. - Additionally, updated the `3d_scene` example to make it look a bit better, and use bevy's branding colors.  |
||
|
|
35073cf7aa |
Multiple Asset Sources (#9885)
This adds support for **Multiple Asset Sources**. You can now register a
named `AssetSource`, which you can load assets from like you normally
would:
```rust
let shader: Handle<Shader> = asset_server.load("custom_source://path/to/shader.wgsl");
```
Notice that `AssetPath` now supports `some_source://` syntax. This can
now be accessed through the `asset_path.source()` accessor.
Asset source names _are not required_. If one is not specified, the
default asset source will be used:
```rust
let shader: Handle<Shader> = asset_server.load("path/to/shader.wgsl");
```
The behavior of the default asset source has not changed. Ex: the
`assets` folder is still the default.
As referenced in #9714
## Why?
**Multiple Asset Sources** enables a number of often-asked-for
scenarios:
* **Loading some assets from other locations on disk**: you could create
a `config` asset source that reads from the OS-default config folder
(not implemented in this PR)
* **Loading some assets from a remote server**: you could register a new
`remote` asset source that reads some assets from a remote http server
(not implemented in this PR)
* **Improved "Binary Embedded" Assets**: we can use this system for
"embedded-in-binary assets", which allows us to replace the old
`load_internal_asset!` approach, which couldn't support asset
processing, didn't support hot-reloading _well_, and didn't make
embedded assets accessible to the `AssetServer` (implemented in this pr)
## Adding New Asset Sources
An `AssetSource` is "just" a collection of `AssetReader`, `AssetWriter`,
and `AssetWatcher` entries. You can configure new asset sources like
this:
```rust
app.register_asset_source(
"other",
AssetSource::build()
.with_reader(|| Box::new(FileAssetReader::new("other")))
)
)
```
Note that `AssetSource` construction _must_ be repeatable, which is why
a closure is accepted.
`AssetSourceBuilder` supports `with_reader`, `with_writer`,
`with_watcher`, `with_processed_reader`, `with_processed_writer`, and
`with_processed_watcher`.
Note that the "asset source" system replaces the old "asset providers"
system.
## Processing Multiple Sources
The `AssetProcessor` now supports multiple asset sources! Processed
assets can refer to assets in other sources and everything "just works".
Each `AssetSource` defines an unprocessed and processed `AssetReader` /
`AssetWriter`.
Currently this is all or nothing for a given `AssetSource`. A given
source is either processed or it is not. Later we might want to add
support for "lazy asset processing", where an `AssetSource` (such as a
remote server) can be configured to only process assets that are
directly referenced by local assets (in order to save local disk space
and avoid doing extra work).
## A new `AssetSource`: `embedded`
One of the big features motivating **Multiple Asset Sources** was
improving our "embedded-in-binary" asset loading. To prove out the
**Multiple Asset Sources** implementation, I chose to build a new
`embedded` `AssetSource`, which replaces the old `load_interal_asset!`
system.
The old `load_internal_asset!` approach had a number of issues:
* The `AssetServer` was not aware of (or capable of loading) internal
assets.
* Because internal assets weren't visible to the `AssetServer`, they
could not be processed (or used by assets that are processed). This
would prevent things "preprocessing shaders that depend on built in Bevy
shaders", which is something we desperately need to start doing.
* Each "internal asset" needed a UUID to be defined in-code to reference
it. This was very manual and toilsome.
The new `embedded` `AssetSource` enables the following pattern:
```rust
// Called in `crates/bevy_pbr/src/render/mesh.rs`
embedded_asset!(app, "mesh.wgsl");
// later in the app
let shader: Handle<Shader> = asset_server.load("embedded://bevy_pbr/render/mesh.wgsl");
```
Notice that this always treats the crate name as the "root path", and it
trims out the `src` path for brevity. This is generally predictable, but
if you need to debug you can use the new `embedded_path!` macro to get a
`PathBuf` that matches the one used by `embedded_asset`.
You can also reference embedded assets in arbitrary assets, such as WGSL
shaders:
```rust
#import "embedded://bevy_pbr/render/mesh.wgsl"
```
This also makes `embedded` assets go through the "normal" asset
lifecycle. They are only loaded when they are actually used!
We are also discussing implicitly converting asset paths to/from shader
modules, so in the future (not in this PR) you might be able to load it
like this:
```rust
#import bevy_pbr::render::mesh::Vertex
```
Compare that to the old system!
```rust
pub const MESH_SHADER_HANDLE: Handle<Shader> = Handle::weak_from_u128(3252377289100772450);
load_internal_asset!(app, MESH_SHADER_HANDLE, "mesh.wgsl", Shader::from_wgsl);
// The mesh asset is the _only_ accessible via MESH_SHADER_HANDLE and _cannot_ be loaded via the AssetServer.
```
## Hot Reloading `embedded`
You can enable `embedded` hot reloading by enabling the
`embedded_watcher` cargo feature:
```
cargo run --features=embedded_watcher
```
## Improved Hot Reloading Workflow
First: the `filesystem_watcher` cargo feature has been renamed to
`file_watcher` for brevity (and to match the `FileAssetReader` naming
convention).
More importantly, hot asset reloading is no longer configured in-code by
default. If you enable any asset watcher feature (such as `file_watcher`
or `rust_source_watcher`), asset watching will be automatically enabled.
This removes the need to _also_ enable hot reloading in your app code.
That means you can replace this:
```rust
app.add_plugins(DefaultPlugins.set(AssetPlugin::default().watch_for_changes()))
```
with this:
```rust
app.add_plugins(DefaultPlugins)
```
If you want to hot reload assets in your app during development, just
run your app like this:
```
cargo run --features=file_watcher
```
This means you can use the same code for development and deployment! To
deploy an app, just don't include the watcher feature
```
cargo build --release
```
My intent is to move to this approach for pretty much all dev workflows.
In a future PR I would like to replace `AssetMode::ProcessedDev` with a
`runtime-processor` cargo feature. We could then group all common "dev"
cargo features under a single `dev` feature:
```sh
# this would enable file_watcher, embedded_watcher, runtime-processor, and more
cargo run --features=dev
```
## AssetMode
`AssetPlugin::Unprocessed`, `AssetPlugin::Processed`, and
`AssetPlugin::ProcessedDev` have been replaced with an `AssetMode` field
on `AssetPlugin`.
```rust
// before
app.add_plugins(DefaultPlugins.set(AssetPlugin::Processed { /* fields here */ })
// after
app.add_plugins(DefaultPlugins.set(AssetPlugin { mode: AssetMode::Processed, ..default() })
```
This aligns `AssetPlugin` with our other struct-like plugins. The old
"source" and "destination" `AssetProvider` fields in the enum variants
have been replaced by the "asset source" system. You no longer need to
configure the AssetPlugin to "point" to custom asset providers.
## AssetServerMode
To improve the implementation of **Multiple Asset Sources**,
`AssetServer` was made aware of whether or not it is using "processed"
or "unprocessed" assets. You can check that like this:
```rust
if asset_server.mode() == AssetServerMode::Processed {
/* do something */
}
```
Note that this refactor should also prepare the way for building "one to
many processed output files", as it makes the server aware of whether it
is loading from processed or unprocessed sources. Meaning we can store
and read processed and unprocessed assets differently!
## AssetPath can now refer to folders
The "file only" restriction has been removed from `AssetPath`. The
`AssetServer::load_folder` API now accepts an `AssetPath` instead of a
`Path`, meaning you can load folders from other asset sources!
## Improved AssetPath Parsing
AssetPath parsing was reworked to support sources, improve error
messages, and to enable parsing with a single pass over the string.
`AssetPath::new` was replaced by `AssetPath::parse` and
`AssetPath::try_parse`.
## AssetWatcher broken out from AssetReader
`AssetReader` is no longer responsible for constructing `AssetWatcher`.
This has been moved to `AssetSourceBuilder`.
## Duplicate Event Debouncing
Asset V2 already debounced duplicate filesystem events, but this was
_input_ events. Multiple input event types can produce the same _output_
`AssetSourceEvent`. Now that we have `embedded_watcher`, which does
expensive file io on events, it made sense to debounce output events
too, so I added that! This will also benefit the AssetProcessor by
preventing integrity checks for duplicate events (and helps keep the
noise down in trace logs).
## Next Steps
* **Port Built-in Shaders**: Currently the primary (and essentially
only) user of `load_interal_asset` in Bevy's source code is "built-in
shaders". I chose not to do that in this PR for a few reasons:
1. We need to add the ability to pass shader defs in to shaders via meta
files. Some shaders (such as MESH_VIEW_TYPES) need to pass shader def
values in that are defined in code.
2. We need to revisit the current shader module naming system. I think
we _probably_ want to imply modules from source structure (at least by
default). Ideally in a way that can losslessly convert asset paths
to/from shader modules (to enable the asset system to resolve modules
using the asset server).
3. I want to keep this change set minimal / get this merged first.
* **Deprecate `load_internal_asset`**: we can't do that until we do (1)
and (2)
* **Relative Asset Paths**: This PR significantly increases the need for
relative asset paths (which was already pretty high). Currently when
loading dependencies, it is assumed to be an absolute path, which means
if in an `AssetLoader` you call `context.load("some/path/image.png")` it
will assume that is the "default" asset source, _even if the current
asset is in a different asset source_. This will cause breakage for
AssetLoaders that are not designed to add the current source to whatever
paths are being used. AssetLoaders should generally not need to be aware
of the name of their current asset source, or need to think about the
"current asset source" generally. We should build apis that support
relative asset paths and then encourage using relative paths as much as
possible (both via api design and docs). Relative paths are also
important because they will allow developers to move folders around
(even across providers) without reprocessing, provided there is no path
breakage.
|
||
|
|
1a7fc57a8c |
Fix comment in scene example FromResources (#9743)
Comment references non existent `FromResources` instead of `FromWorld` --------- Co-authored-by: Nicola Papale <nicopap@users.noreply.github.com> Co-authored-by: Alice Cecile <alice.i.cecile@gmail.com> Co-authored-by: François <mockersf@gmail.com> |
||
|
|
5eb292dc10 |
Bevy Asset V2 (#8624)
# Bevy Asset V2 Proposal ## Why Does Bevy Need A New Asset System? Asset pipelines are a central part of the gamedev process. Bevy's current asset system is missing a number of features that make it non-viable for many classes of gamedev. After plenty of discussions and [a long community feedback period](https://github.com/bevyengine/bevy/discussions/3972), we've identified a number missing features: * **Asset Preprocessing**: it should be possible to "preprocess" / "compile" / "crunch" assets at "development time" rather than when the game starts up. This enables offloading expensive work from deployed apps, faster asset loading, less runtime memory usage, etc. * **Per-Asset Loader Settings**: Individual assets cannot define their own loaders that override the defaults. Additionally, they cannot provide per-asset settings to their loaders. This is a huge limitation, as many asset types don't provide all information necessary for Bevy _inside_ the asset. For example, a raw PNG image says nothing about how it should be sampled (ex: linear vs nearest). * **Asset `.meta` files**: assets should have configuration files stored adjacent to the asset in question, which allows the user to configure asset-type-specific settings. These settings should be accessible during the pre-processing phase. Modifying a `.meta` file should trigger a re-processing / re-load of the asset. It should be possible to configure asset loaders from the meta file. * **Processed Asset Hot Reloading**: Changes to processed assets (or their dependencies) should result in re-processing them and re-loading the results in live Bevy Apps. * **Asset Dependency Tracking**: The current bevy_asset has no good way to wait for asset dependencies to load. It punts this as an exercise for consumers of the loader apis, which is unreasonable and error prone. There should be easy, ergonomic ways to wait for assets to load and block some logic on an asset's entire dependency tree loading. * **Runtime Asset Loading**: it should be (optionally) possible to load arbitrary assets dynamically at runtime. This necessitates being able to deploy and run the asset server alongside Bevy Apps on _all platforms_. For example, we should be able to invoke the shader compiler at runtime, stream scenes from sources like the internet, etc. To keep deployed binaries (and startup times) small, the runtime asset server configuration should be configurable with different settings compared to the "pre processor asset server". * **Multiple Backends**: It should be possible to load assets from arbitrary sources (filesystems, the internet, remote asset serves, etc). * **Asset Packing**: It should be possible to deploy assets in compressed "packs", which makes it easier and more efficient to distribute assets with Bevy Apps. * **Asset Handoff**: It should be possible to hold a "live" asset handle, which correlates to runtime data, without actually holding the asset in memory. Ex: it must be possible to hold a reference to a GPU mesh generated from a "mesh asset" without keeping the mesh data in CPU memory * **Per-Platform Processed Assets**: Different platforms and app distributions have different capabilities and requirements. Some platforms need lower asset resolutions or different asset formats to operate within the hardware constraints of the platform. It should be possible to define per-platform asset processing profiles. And it should be possible to deploy only the assets required for a given platform. These features have architectural implications that are significant enough to require a full rewrite. The current Bevy Asset implementation got us this far, but it can take us no farther. This PR defines a brand new asset system that implements most of these features, while laying the foundations for the remaining features to be built. ## Bevy Asset V2 Here is a quick overview of the features introduced in this PR. * **Asset Preprocessing**: Preprocess assets at development time into more efficient (and configurable) representations * **Dependency Aware**: Dependencies required to process an asset are tracked. If an asset's processed dependency changes, it will be reprocessed * **Hot Reprocessing/Reloading**: detect changes to asset source files, reprocess them if they have changed, and then hot-reload them in Bevy Apps. * **Only Process Changes**: Assets are only re-processed when their source file (or meta file) has changed. This uses hashing and timestamps to avoid processing assets that haven't changed. * **Transactional and Reliable**: Uses write-ahead logging (a technique commonly used by databases) to recover from crashes / forced-exits. Whenever possible it avoids full-reprocessing / only uncompleted transactions will be reprocessed. When the processor is running in parallel with a Bevy App, processor asset writes block Bevy App asset reads. Reading metadata + asset bytes is guaranteed to be transactional / correctly paired. * **Portable / Run anywhere / Database-free**: The processor does not rely on an in-memory database (although it uses some database techniques for reliability). This is important because pretty much all in-memory databases have unsupported platforms or build complications. * **Configure Processor Defaults Per File Type**: You can say "use this processor for all files of this type". * **Custom Processors**: The `Processor` trait is flexible and unopinionated. It can be implemented by downstream plugins. * **LoadAndSave Processors**: Most asset processing scenarios can be expressed as "run AssetLoader A, save the results using AssetSaver X, and then load the result using AssetLoader B". For example, load this png image using `PngImageLoader`, which produces an `Image` asset and then save it using `CompressedImageSaver` (which also produces an `Image` asset, but in a compressed format), which takes an `Image` asset as input. This means if you have an `AssetLoader` for an asset, you are already half way there! It also means that you can share AssetSavers across multiple loaders. Because `CompressedImageSaver` accepts Bevy's generic Image asset as input, it means you can also use it with some future `JpegImageLoader`. * **Loader and Saver Settings**: Asset Loaders and Savers can now define their own settings types, which are passed in as input when an asset is loaded / saved. Each asset can define its own settings. * **Asset `.meta` files**: configure asset loaders, their settings, enable/disable processing, and configure processor settings * **Runtime Asset Dependency Tracking** Runtime asset dependencies (ex: if an asset contains a `Handle<Image>`) are tracked by the asset server. An event is emitted when an asset and all of its dependencies have been loaded * **Unprocessed Asset Loading**: Assets do not require preprocessing. They can be loaded directly. A processed asset is just a "normal" asset with some extra metadata. Asset Loaders don't need to know or care about whether or not an asset was processed. * **Async Asset IO**: Asset readers/writers use async non-blocking interfaces. Note that because Rust doesn't yet support async traits, there is a bit of manual Boxing / Future boilerplate. This will hopefully be removed in the near future when Rust gets async traits. * **Pluggable Asset Readers and Writers**: Arbitrary asset source readers/writers are supported, both by the processor and the asset server. * **Better Asset Handles** * **Single Arc Tree**: Asset Handles now use a single arc tree that represents the lifetime of the asset. This makes their implementation simpler, more efficient, and allows us to cheaply attach metadata to handles. Ex: the AssetPath of a handle is now directly accessible on the handle itself! * **Const Typed Handles**: typed handles can be constructed in a const context. No more weird "const untyped converted to typed at runtime" patterns! * **Handles and Ids are Smaller / Faster To Hash / Compare**: Typed `Handle<T>` is now much smaller in memory and `AssetId<T>` is even smaller. * **Weak Handle Usage Reduction**: In general Handles are now considered to be "strong". Bevy features that previously used "weak `Handle<T>`" have been ported to `AssetId<T>`, which makes it statically clear that the features do not hold strong handles (while retaining strong type information). Currently Handle::Weak still exists, but it is very possible that we can remove that entirely. * **Efficient / Dense Asset Ids**: Assets now have efficient dense runtime asset ids, which means we can avoid expensive hash lookups. Assets are stored in Vecs instead of HashMaps. There are now typed and untyped ids, which means we no longer need to store dynamic type information in the ID for typed handles. "AssetPathId" (which was a nightmare from a performance and correctness standpoint) has been entirely removed in favor of dense ids (which are retrieved for a path on load) * **Direct Asset Loading, with Dependency Tracking**: Assets that are defined at runtime can still have their dependencies tracked by the Asset Server (ex: if you create a material at runtime, you can still wait for its textures to load). This is accomplished via the (currently optional) "asset dependency visitor" trait. This system can also be used to define a set of assets to load, then wait for those assets to load. * **Async folder loading**: Folder loading also uses this system and immediately returns a handle to the LoadedFolder asset, which means folder loading no longer blocks on directory traversals. * **Improved Loader Interface**: Loaders now have a specific "top level asset type", which makes returning the top-level asset simpler and statically typed. * **Basic Image Settings and Processing**: Image assets can now be processed into the gpu-friendly Basic Universal format. The ImageLoader now has a setting to define what format the image should be loaded as. Note that this is just a minimal MVP ... plenty of additional work to do here. To demo this, enable the `basis-universal` feature and turn on asset processing. * **Simpler Audio Play / AudioSink API**: Asset handle providers are cloneable, which means the Audio resource can mint its own handles. This means you can now do `let sink_handle = audio.play(music)` instead of `let sink_handle = audio_sinks.get_handle(audio.play(music))`. Note that this might still be replaced by https://github.com/bevyengine/bevy/pull/8424. **Removed Handle Casting From Engine Features**: Ex: FontAtlases no longer use casting between handle types ## Using The New Asset System ### Normal Unprocessed Asset Loading By default the `AssetPlugin` does not use processing. It behaves pretty much the same way as the old system. If you are defining a custom asset, first derive `Asset`: ```rust #[derive(Asset)] struct Thing { value: String, } ``` Initialize the asset: ```rust app.init_asset:<Thing>() ``` Implement a new `AssetLoader` for it: ```rust #[derive(Default)] struct ThingLoader; #[derive(Serialize, Deserialize, Default)] pub struct ThingSettings { some_setting: bool, } impl AssetLoader for ThingLoader { type Asset = Thing; type Settings = ThingSettings; fn load<'a>( &'a self, reader: &'a mut Reader, settings: &'a ThingSettings, load_context: &'a mut LoadContext, ) -> BoxedFuture<'a, Result<Thing, anyhow::Error>> { Box::pin(async move { let mut bytes = Vec::new(); reader.read_to_end(&mut bytes).await?; // convert bytes to value somehow Ok(Thing { value }) }) } fn extensions(&self) -> &[&str] { &["thing"] } } ``` Note that this interface will get much cleaner once Rust gets support for async traits. `Reader` is an async futures_io::AsyncRead. You can stream bytes as they come in or read them all into a `Vec<u8>`, depending on the context. You can use `let handle = load_context.load(path)` to kick off a dependency load, retrieve a handle, and register the dependency for the asset. Then just register the loader in your Bevy app: ```rust app.init_asset_loader::<ThingLoader>() ``` Now just add your `Thing` asset files into the `assets` folder and load them like this: ```rust fn system(asset_server: Res<AssetServer>) { let handle = Handle<Thing> = asset_server.load("cool.thing"); } ``` You can check load states directly via the asset server: ```rust if asset_server.load_state(&handle) == LoadState::Loaded { } ``` You can also listen for events: ```rust fn system(mut events: EventReader<AssetEvent<Thing>>, handle: Res<SomeThingHandle>) { for event in events.iter() { if event.is_loaded_with_dependencies(&handle) { } } } ``` Note the new `AssetEvent::LoadedWithDependencies`, which only fires when the asset is loaded _and_ all dependencies (and their dependencies) have loaded. Unlike the old asset system, for a given asset path all `Handle<T>` values point to the same underlying Arc. This means Handles can cheaply hold more asset information, such as the AssetPath: ```rust // prints the AssetPath of the handle info!("{:?}", handle.path()) ``` ### Processed Assets Asset processing can be enabled via the `AssetPlugin`. When developing Bevy Apps with processed assets, do this: ```rust app.add_plugins(DefaultPlugins.set(AssetPlugin::processed_dev())) ``` This runs the `AssetProcessor` in the background with hot-reloading. It reads assets from the `assets` folder, processes them, and writes them to the `.imported_assets` folder. Asset loads in the Bevy App will wait for a processed version of the asset to become available. If an asset in the `assets` folder changes, it will be reprocessed and hot-reloaded in the Bevy App. When deploying processed Bevy apps, do this: ```rust app.add_plugins(DefaultPlugins.set(AssetPlugin::processed())) ``` This does not run the `AssetProcessor` in the background. It behaves like `AssetPlugin::unprocessed()`, but reads assets from `.imported_assets`. When the `AssetProcessor` is running, it will populate sibling `.meta` files for assets in the `assets` folder. Meta files for assets that do not have a processor configured look like this: ```rust ( meta_format_version: "1.0", asset: Load( loader: "bevy_render::texture::image_loader::ImageLoader", settings: ( format: FromExtension, ), ), ) ``` This is metadata for an image asset. For example, if you have `assets/my_sprite.png`, this could be the metadata stored at `assets/my_sprite.png.meta`. Meta files are totally optional. If no metadata exists, the default settings will be used. In short, this file says "load this asset with the ImageLoader and use the file extension to determine the image type". This type of meta file is supported in all AssetPlugin modes. If in `Unprocessed` mode, the asset (with the meta settings) will be loaded directly. If in `ProcessedDev` mode, the asset file will be copied directly to the `.imported_assets` folder. The meta will also be copied directly to the `.imported_assets` folder, but with one addition: ```rust ( meta_format_version: "1.0", processed_info: Some(( hash: 12415480888597742505, full_hash: 14344495437905856884, process_dependencies: [], )), asset: Load( loader: "bevy_render::texture::image_loader::ImageLoader", settings: ( format: FromExtension, ), ), ) ``` `processed_info` contains `hash` (a direct hash of the asset and meta bytes), `full_hash` (a hash of `hash` and the hashes of all `process_dependencies`), and `process_dependencies` (the `path` and `full_hash` of every process_dependency). A "process dependency" is an asset dependency that is _directly_ used when processing the asset. Images do not have process dependencies, so this is empty. When the processor is enabled, you can use the `Process` metadata config: ```rust ( meta_format_version: "1.0", asset: Process( processor: "bevy_asset::processor::process::LoadAndSave<bevy_render::texture::image_loader::ImageLoader, bevy_render::texture::compressed_image_saver::CompressedImageSaver>", settings: ( loader_settings: ( format: FromExtension, ), saver_settings: ( generate_mipmaps: true, ), ), ), ) ``` This configures the asset to use the `LoadAndSave` processor, which runs an AssetLoader and feeds the result into an AssetSaver (which saves the given Asset and defines a loader to load it with). (for terseness LoadAndSave will likely get a shorter/friendlier type name when [Stable Type Paths](#7184) lands). `LoadAndSave` is likely to be the most common processor type, but arbitrary processors are supported. `CompressedImageSaver` saves an `Image` in the Basis Universal format and configures the ImageLoader to load it as basis universal. The `AssetProcessor` will read this meta, run it through the LoadAndSave processor, and write the basis-universal version of the image to `.imported_assets`. The final metadata will look like this: ```rust ( meta_format_version: "1.0", processed_info: Some(( hash: 905599590923828066, full_hash: 9948823010183819117, process_dependencies: [], )), asset: Load( loader: "bevy_render::texture::image_loader::ImageLoader", settings: ( format: Format(Basis), ), ), ) ``` To try basis-universal processing out in Bevy examples, (for example `sprite.rs`), change `add_plugins(DefaultPlugins)` to `add_plugins(DefaultPlugins.set(AssetPlugin::processed_dev()))` and run with the `basis-universal` feature enabled: `cargo run --features=basis-universal --example sprite`. To create a custom processor, there are two main paths: 1. Use the `LoadAndSave` processor with an existing `AssetLoader`. Implement the `AssetSaver` trait, register the processor using `asset_processor.register_processor::<LoadAndSave<ImageLoader, CompressedImageSaver>>(image_saver.into())`. 2. Implement the `Process` trait directly and register it using: `asset_processor.register_processor(thing_processor)`. You can configure default processors for file extensions like this: ```rust asset_processor.set_default_processor::<ThingProcessor>("thing") ``` There is one more metadata type to be aware of: ```rust ( meta_format_version: "1.0", asset: Ignore, ) ``` This will ignore the asset during processing / prevent it from being written to `.imported_assets`. The AssetProcessor stores a transaction log at `.imported_assets/log` and uses it to gracefully recover from unexpected stops. This means you can force-quit the processor (and Bevy Apps running the processor in parallel) at arbitrary times! `.imported_assets` is "local state". It should _not_ be checked into source control. It should also be considered "read only". In practice, you _can_ modify processed assets and processed metadata if you really need to test something. But those modifications will not be represented in the hashes of the assets, so the processed state will be "out of sync" with the source assets. The processor _will not_ fix this for you. Either revert the change after you have tested it, or delete the processed files so they can be re-populated. ## Open Questions There are a number of open questions to be discussed. We should decide if they need to be addressed in this PR and if so, how we will address them: ### Implied Dependencies vs Dependency Enumeration There are currently two ways to populate asset dependencies: * **Implied via AssetLoaders**: if an AssetLoader loads an asset (and retrieves a handle), a dependency is added to the list. * **Explicit via the optional Asset::visit_dependencies**: if `server.load_asset(my_asset)` is called, it will call `my_asset.visit_dependencies`, which will grab dependencies that have been manually defined for the asset via the Asset trait impl (which can be derived). This means that defining explicit dependencies is optional for "loaded assets". And the list of dependencies is always accurate because loaders can only produce Handles if they register dependencies. If an asset was loaded with an AssetLoader, it only uses the implied dependencies. If an asset was created at runtime and added with `asset_server.load_asset(MyAsset)`, it will use `Asset::visit_dependencies`. However this can create a behavior mismatch between loaded assets and equivalent "created at runtime" assets if `Assets::visit_dependencies` doesn't exactly match the dependencies produced by the AssetLoader. This behavior mismatch can be resolved by completely removing "implied loader dependencies" and requiring `Asset::visit_dependencies` to supply dependency data. But this creates two problems: * It makes defining loaded assets harder and more error prone: Devs must remember to manually annotate asset dependencies with `#[dependency]` when deriving `Asset`. For more complicated assets (such as scenes), the derive likely wouldn't be sufficient and a manual `visit_dependencies` impl would be required. * Removes the ability to immediately kick off dependency loads: When AssetLoaders retrieve a Handle, they also immediately kick off an asset load for the handle, which means it can start loading in parallel _before_ the asset finishes loading. For large assets, this could be significant. (although this could be mitigated for processed assets if we store dependencies in the processed meta file and load them ahead of time) ### Eager ProcessorDev Asset Loading I made a controversial call in the interest of fast startup times ("time to first pixel") for the "processor dev mode configuration". When initializing the AssetProcessor, current processed versions of unchanged assets are yielded immediately, even if their dependencies haven't been checked yet for reprocessing. This means that non-current-state-of-filesystem-but-previously-valid assets might be returned to the App first, then hot-reloaded if/when their dependencies change and the asset is reprocessed. Is this behavior desirable? There is largely one alternative: do not yield an asset from the processor to the app until all of its dependencies have been checked for changes. In some common cases (load dependency has not changed since last run) this will increase startup time. The main question is "by how much" and is that slower startup time worth it in the interest of only yielding assets that are true to the current state of the filesystem. Should this be configurable? I'm starting to think we should only yield an asset after its (historical) dependencies have been checked for changes + processed as necessary, but I'm curious what you all think. ### Paths Are Currently The Only Canonical ID / Do We Want Asset UUIDs? In this implementation AssetPaths are the only canonical asset identifier (just like the previous Bevy Asset system and Godot). Moving assets will result in re-scans (and currently reprocessing, although reprocessing can easily be avoided with some changes). Asset renames/moves will break code and assets that rely on specific paths, unless those paths are fixed up. Do we want / need "stable asset uuids"? Introducing them is very possible: 1. Generate a UUID and include it in .meta files 2. Support UUID in AssetPath 3. Generate "asset indices" which are loaded on startup and map UUIDs to paths. 4 (maybe). Consider only supporting UUIDs for processed assets so we can generate quick-to-load indices instead of scanning meta files. The main "pro" is that assets referencing UUIDs don't need to be migrated when a path changes. The main "con" is that UUIDs cannot be "lazily resolved" like paths. They need a full view of all assets to answer the question "does this UUID exist". Which means UUIDs require the AssetProcessor to fully finish startup scans before saying an asset doesnt exist. And they essentially require asset pre-processing to use in apps, because scanning all asset metadata files at runtime to resolve a UUID is not viable for medium-to-large apps. It really requires a pre-generated UUID index, which must be loaded before querying for assets. I personally think this should be investigated in a separate PR. Paths aren't going anywhere ... _everyone_ uses filesystems (and filesystem-like apis) to manage their asset source files. I consider them permanent canonical asset information. Additionally, they behave well for both processed and unprocessed asset modes. Given that Bevy is supporting both, this feels like the right canonical ID to start with. UUIDS (and maybe even other indexed-identifier types) can be added later as necessary. ### Folder / File Naming Conventions All asset processing config currently lives in the `.imported_assets` folder. The processor transaction log is in `.imported_assets/log`. Processed assets are added to `.imported_assets/Default`, which will make migrating to processed asset profiles (ex: a `.imported_assets/Mobile` profile) a non-breaking change. It also allows us to create top-level files like `.imported_assets/log` without it being interpreted as an asset. Meta files currently have a `.meta` suffix. Do we like these names and conventions? ### Should the `AssetPlugin::processed_dev` configuration enable `watch_for_changes` automatically? Currently it does (which I think makes sense), but it does make it the only configuration that enables watch_for_changes by default. ### Discuss on_loaded High Level Interface: This PR includes a very rough "proof of concept" `on_loaded` system adapter that uses the `LoadedWithDependencies` event in combination with `asset_server.load_asset` dependency tracking to support this pattern ```rust fn main() { App::new() .init_asset::<MyAssets>() .add_systems(Update, on_loaded(create_array_texture)) .run(); } #[derive(Asset, Clone)] struct MyAssets { #[dependency] picture_of_my_cat: Handle<Image>, #[dependency] picture_of_my_other_cat: Handle<Image>, } impl FromWorld for ArrayTexture { fn from_world(world: &mut World) -> Self { picture_of_my_cat: server.load("meow.png"), picture_of_my_other_cat: server.load("meeeeeeeow.png"), } } fn spawn_cat(In(my_assets): In<MyAssets>, mut commands: Commands) { commands.spawn(SpriteBundle { texture: my_assets.picture_of_my_cat.clone(), ..default() }); commands.spawn(SpriteBundle { texture: my_assets.picture_of_my_other_cat.clone(), ..default() }); } ``` The implementation is _very_ rough. And it is currently unsafe because `bevy_ecs` doesn't expose some internals to do this safely from inside `bevy_asset`. There are plenty of unanswered questions like: * "do we add a Loadable" derive? (effectively automate the FromWorld implementation above) * Should `MyAssets` even be an Asset? (largely implemented this way because it elegantly builds on `server.load_asset(MyAsset { .. })` dependency tracking). We should think hard about what our ideal API looks like (and if this is a pattern we want to support). Not necessarily something we need to solve in this PR. The current `on_loaded` impl should probably be removed from this PR before merging. ## Clarifying Questions ### What about Assets as Entities? This Bevy Asset V2 proposal implementation initially stored Assets as ECS Entities. Instead of `AssetId<T>` + the `Assets<T>` resource it used `Entity` as the asset id and Asset values were just ECS components. There are plenty of compelling reasons to do this: 1. Easier to inline assets in Bevy Scenes (as they are "just" normal entities + components) 2. More flexible queries: use the power of the ECS to filter assets (ex: `Query<Mesh, With<Tree>>`). 3. Extensible. Users can add arbitrary component data to assets. 4. Things like "component visualization tools" work out of the box to visualize asset data. However Assets as Entities has a ton of caveats right now: * We need to be able to allocate entity ids without a direct World reference (aka rework id allocator in Entities ... i worked around this in my prototypes by just pre allocating big chunks of entities) * We want asset change events in addition to ECS change tracking ... how do we populate them when mutations can come from anywhere? Do we use Changed queries? This would require iterating over the change data for all assets every frame. Is this acceptable or should we implement a new "event based" component change detection option? * Reconciling manually created assets with asset-system managed assets has some nuance (ex: are they "loaded" / do they also have that component metadata?) * "how do we handle "static" / default entity handles" (ties in to the Entity Indices discussion: https://github.com/bevyengine/bevy/discussions/8319). This is necessary for things like "built in" assets and default handles in things like SpriteBundle. * Storing asset information as a component makes it easy to "invalidate" asset state by removing the component (or forcing modifications). Ideally we have ways to lock this down (some combination of Rust type privacy and ECS validation) In practice, how we store and identify assets is a reasonably superficial change (porting off of Assets as Entities and implementing dedicated storage + ids took less than a day). So once we sort out the remaining challenges the flip should be straightforward. Additionally, I do still have "Assets as Entities" in my commit history, so we can reuse that work. I personally think "assets as entities" is a good endgame, but it also doesn't provide _significant_ value at the moment and it certainly isn't ready yet with the current state of things. ### Why not Distill? [Distill](https://github.com/amethyst/distill) is a high quality fully featured asset system built in Rust. It is very natural to ask "why not just use Distill?". It is also worth calling out that for awhile, [we planned on adopting Distill / I signed off on it](https://github.com/bevyengine/bevy/issues/708). However I think Bevy has a number of constraints that make Distill adoption suboptimal: * **Architectural Simplicity:** * Distill's processor requires an in-memory database (lmdb) and RPC networked API (using Cap'n Proto). Each of these introduces API complexity that increases maintenance burden and "code grokability". Ignoring tests, documentation, and examples, Distill has 24,237 lines of Rust code (including generated code for RPC + database interactions). If you ignore generated code, it has 11,499 lines. * Bevy builds the AssetProcessor and AssetServer using pluggable AssetReader/AssetWriter Rust traits with simple io interfaces. They do not necessitate databases or RPC interfaces (although Readers/Writers could use them if that is desired). Bevy Asset V2 (at the time of writing this PR) is 5,384 lines of Rust code (ignoring tests, documentation, and examples). Grain of salt: Distill does have more features currently (ex: Asset Packing, GUIDS, remote-out-of-process asset processor). I do plan to implement these features in Bevy Asset V2 and I personally highly doubt they will meaningfully close the 6115 lines-of-code gap. * This complexity gap (which while illustrated by lines of code, is much bigger than just that) is noteworthy to me. Bevy should be hackable and there are pillars of Distill that are very hard to understand and extend. This is a matter of opinion (and Bevy Asset V2 also has complicated areas), but I think Bevy Asset V2 is much more approachable for the average developer. * Necessary disclaimer: counting lines of code is an extremely rough complexity metric. Read the code and form your own opinions. * **Optional Asset Processing:** Not all Bevy Apps (or Bevy App developers) need / want asset preprocessing. Processing increases the complexity of the development environment by introducing things like meta files, imported asset storage, running processors in the background, waiting for processing to finish, etc. Distill _requires_ preprocessing to work. With Bevy Asset V2 processing is fully opt-in. The AssetServer isn't directly aware of asset processors at all. AssetLoaders only care about converting bytes to runtime Assets ... they don't know or care if the bytes were pre-processed or not. Processing is "elegantly" (forgive my self-congratulatory phrasing) layered on top and builds on the existing Asset system primitives. * **Direct Filesystem Access to Processed Asset State:** Distill stores processed assets in a database. This makes debugging / inspecting the processed outputs harder (either requires special tooling to query the database or they need to be "deployed" to be inspected). Bevy Asset V2, on the other hand, stores processed assets in the filesystem (by default ... this is configurable). This makes interacting with the processed state more natural. Note that both Godot and Unity's new asset system store processed assets in the filesystem. * **Portability**: Because Distill's processor uses lmdb and RPC networking, it cannot be run on certain platforms (ex: lmdb is a non-rust dependency that cannot run on the web, some platforms don't support running network servers). Bevy should be able to process assets everywhere (ex: run the Bevy Editor on the web, compile + process shaders on mobile, etc). Distill does partially mitigate this problem by supporting "streaming" assets via the RPC protocol, but this is not a full solve from my perspective. And Bevy Asset V2 can (in theory) also stream assets (without requiring RPC, although this isn't implemented yet) Note that I _do_ still think Distill would be a solid asset system for Bevy. But I think the approach in this PR is a better solve for Bevy's specific "asset system requirements". ### Doesn't async-fs just shim requests to "sync" `std::fs`? What is the point? "True async file io" has limited / spotty platform support. async-fs (and the rust async ecosystem generally ... ex Tokio) currently use async wrappers over std::fs that offload blocking requests to separate threads. This may feel unsatisfying, but it _does_ still provide value because it prevents our task pools from blocking on file system operations (which would prevent progress when there are many tasks to do, but all threads in a pool are currently blocking on file system ops). Additionally, using async APIs for our AssetReaders and AssetWriters also provides value because we can later add support for "true async file io" for platforms that support it. _And_ we can implement other "true async io" asset backends (such as networked asset io). ## Draft TODO - [x] Fill in missing filesystem event APIs: file removed event (which is expressed as dangling RenameFrom events in some cases), file/folder renamed event - [x] Assets without loaders are not moved to the processed folder. This breaks things like referenced `.bin` files for GLTFs. This should be configurable per-non-asset-type. - [x] Initial implementation of Reflect and FromReflect for Handle. The "deserialization" parity bar is low here as this only worked with static UUIDs in the old impl ... this is a non-trivial problem. Either we add a Handle::AssetPath variant that gets "upgraded" to a strong handle on scene load or we use a separate AssetRef type for Bevy scenes (which is converted to a runtime Handle on load). This deserves its own discussion in a different pr. - [x] Populate read_asset_bytes hash when run by the processor (a bit of a special case .. when run by the processor the processed meta will contain the hash so we don't need to compute it on the spot, but we don't want/need to read the meta when run by the main AssetServer) - [x] Delay hot reloading: currently filesystem events are handled immediately, which creates timing issues in some cases. For example hot reloading images can sometimes break because the image isn't finished writing. We should add a delay, likely similar to the [implementation in this PR](https://github.com/bevyengine/bevy/pull/8503). - [x] Port old platform-specific AssetIo implementations to the new AssetReader interface (currently missing Android and web) - [x] Resolve on_loaded unsafety (either by removing the API entirely or removing the unsafe) - [x] Runtime loader setting overrides - [x] Remove remaining unwraps that should be error-handled. There are number of TODOs here - [x] Pretty AssetPath Display impl - [x] Document more APIs - [x] Resolve spurious "reloading because it has changed" events (to repro run load_gltf with `processed_dev()`) - [x] load_dependency hot reloading currently only works for processed assets. If processing is disabled, load_dependency changes are not hot reloaded. - [x] Replace AssetInfo dependency load/fail counters with `loading_dependencies: HashSet<UntypedAssetId>` to prevent reloads from (potentially) breaking counters. Storing this will also enable "dependency reloaded" events (see [Next Steps](#next-steps)) - [x] Re-add filesystem watcher cargo feature gate (currently it is not optional) - [ ] Migration Guide - [ ] Changelog ## Followup TODO - [ ] Replace "eager unchanged processed asset loading" behavior with "don't returned unchanged processed asset until dependencies have been checked". - [ ] Add true `Ignore` AssetAction that does not copy the asset to the imported_assets folder. - [ ] Finish "live asset unloading" (ex: free up CPU asset memory after uploading an image to the GPU), rethink RenderAssets, and port renderer features. The `Assets` collection uses `Option<T>` for asset storage to support its removal. (1) the Option might not actually be necessary ... might be able to just remove from the collection entirely (2) need to finalize removal apis - [ ] Try replacing the "channel based" asset id recycling with something a bit more efficient (ex: we might be able to use raw atomic ints with some cleverness) - [ ] Consider adding UUIDs to processed assets (scoped just to helping identify moved assets ... not exposed to load queries ... see [Next Steps](#next-steps)) - [ ] Store "last modified" source asset and meta timestamps in processed meta files to enable skipping expensive hashing when the file wasn't changed - [ ] Fix "slow loop" handle drop fix - [ ] Migrate to TypeName - [x] Handle "loader preregistration". See #9429 ## Next Steps * **Configurable per-type defaults for AssetMeta**: It should be possible to add configuration like "all png image meta should default to using nearest sampling" (currently this hard-coded per-loader/processor Settings::default() impls). Also see the "Folder Meta" bullet point. * **Avoid Reprocessing on Asset Renames / Moves**: See the "canonical asset ids" discussion in [Open Questions](#open-questions) and the relevant bullet point in [Draft TODO](#draft-todo). Even without canonical ids, folder renames could avoid reprocessing in some cases. * **Multiple Asset Sources**: Expand AssetPath to support "asset source names" and support multiple AssetReaders in the asset server (ex: `webserver://some_path/image.png` backed by an Http webserver AssetReader). The "default" asset reader would use normal `some_path/image.png` paths. Ideally this works in combination with multiple AssetWatchers for hot-reloading * **Stable Type Names**: this pr removes the TypeUuid requirement from assets in favor of `std::any::type_name`. This makes defining assets easier (no need to generate a new uuid / use weird proc macro syntax). It also makes reading meta files easier (because things have "friendly names"). We also use type names for components in scene files. If they are good enough for components, they are good enough for assets. And consistency across Bevy pillars is desirable. However, `std::any::type_name` is not guaranteed to be stable (although in practice it is). We've developed a [stable type path](https://github.com/bevyengine/bevy/pull/7184) to resolve this, which should be adopted when it is ready. * **Command Line Interface**: It should be possible to run the asset processor in a separate process from the command line. This will also require building a network-server-backed AssetReader to communicate between the app and the processor. We've been planning to build a "bevy cli" for awhile. This seems like a good excuse to build it. * **Asset Packing**: This is largely an additive feature, so it made sense to me to punt this until we've laid the foundations in this PR. * **Per-Platform Processed Assets**: It should be possible to generate assets for multiple platforms by supporting multiple "processor profiles" per asset (ex: compress with format X on PC and Y on iOS). I think there should probably be arbitrary "profiles" (which can be separate from actual platforms), which are then assigned to a given platform when generating the final asset distribution for that platform. Ex: maybe devs want a "Mobile" profile that is shared between iOS and Android. Or a "LowEnd" profile shared between web and mobile. * **Versioning and Migrations**: Assets, Loaders, Savers, and Processors need to have versions to determine if their schema is valid. If an asset / loader version is incompatible with the current version expected at runtime, the processor should be able to migrate them. I think we should try using Bevy Reflect for this, as it would allow us to load the old version as a dynamic Reflect type without actually having the old Rust type. It would also allow us to define "patches" to migrate between versions (Bevy Reflect devs are currently working on patching). The `.meta` file already has its own format version. Migrating that to new versions should also be possible. * **Real Copy-on-write AssetPaths**: Rust's actual Cow (clone-on-write type) currently used by AssetPath can still result in String clones that aren't actually necessary (cloning an Owned Cow clones the contents). Bevy's asset system requires cloning AssetPaths in a number of places, which result in actual clones of the internal Strings. This is not efficient. AssetPath internals should be reworked to exhibit truer cow-like-behavior that reduces String clones to the absolute minimum. * **Consider processor-less processing**: In theory the AssetServer could run processors "inline" even if the background AssetProcessor is disabled. If we decide this is actually desirable, we could add this. But I don't think its a priority in the short or medium term. * **Pre-emptive dependency loading**: We could encode dependencies in processed meta files, which could then be used by the Asset Server to kick of dependency loads as early as possible (prior to starting the actual asset load). Is this desirable? How much time would this save in practice? * **Optimize Processor With UntypedAssetIds**: The processor exclusively uses AssetPath to identify assets currently. It might be possible to swap these out for UntypedAssetIds in some places, which are smaller / cheaper to hash and compare. * **One to Many Asset Processing**: An asset source file that produces many assets currently must be processed into a single "processed" asset source. If labeled assets can be written separately they can each have their own configured savers _and_ they could be loaded more granularly. Definitely worth exploring! * **Automatically Track "Runtime-only" Asset Dependencies**: Right now, tracking "created at runtime" asset dependencies requires adding them via `asset_server.load_asset(StandardMaterial::default())`. I think with some cleverness we could also do this for `materials.add(StandardMaterial::default())`, making tracking work "everywhere". There are challenges here relating to change detection / ensuring the server is made aware of dependency changes. This could be expensive in some cases. * **"Dependency Changed" events**: Some assets have runtime artifacts that need to be re-generated when one of their dependencies change (ex: regenerate a material's bind group when a Texture needs to change). We are generating the dependency graph so we can definitely produce these events. Buuuuut generating these events will have a cost / they could be high frequency for some assets, so we might want this to be opt-in for specific cases. * **Investigate Storing More Information In Handles**: Handles can now store arbitrary information, which makes it cheaper and easier to access. How much should we move into them? Canonical asset load states (via atomics)? (`handle.is_loaded()` would be very cool). Should we store the entire asset and remove the `Assets<T>` collection? (`Arc<RwLock<Option<Image>>>`?) * **Support processing and loading files without extensions**: This is a pretty arbitrary restriction and could be supported with very minimal changes. * **Folder Meta**: It would be nice if we could define per folder processor configuration defaults (likely in a `.meta` or `.folder_meta` file). Things like "default to linear filtering for all Images in this folder". * **Replace async_broadcast with event-listener?** This might be approximately drop-in for some uses and it feels more light weight * **Support Running the AssetProcessor on the Web**: Most of the hard work is done here, but there are some easy straggling TODOs (make the transaction log an interface instead of a direct file writer so we can write a web storage backend, implement an AssetReader/AssetWriter that reads/writes to something like LocalStorage). * **Consider identifying and preventing circular dependencies**: This is especially important for "processor dependencies", as processing will silently never finish in these cases. * **Built-in/Inlined Asset Hot Reloading**: This PR regresses "built-in/inlined" asset hot reloading (previously provided by the DebugAssetServer). I'm intentionally punting this because I think it can be cleanly implemented with "multiple asset sources" by registering a "debug asset source" (ex: `debug://bevy_pbr/src/render/pbr.wgsl` asset paths) in combination with an AssetWatcher for that asset source and support for "manually loading pats with asset bytes instead of AssetReaders". The old DebugAssetServer was quite nasty and I'd love to avoid that hackery going forward. * **Investigate ways to remove double-parsing meta files**: Parsing meta files currently involves parsing once with "minimal" versions of the meta file to extract the type name of the loader/processor config, then parsing again to parse the "full" meta. This is suboptimal. We should be able to define custom deserializers that (1) assume the loader/processor type name comes first (2) dynamically looks up the loader/processor registrations to deserialize settings in-line (similar to components in the bevy scene format). Another alternative: deserialize as dynamic Reflect objects and then convert. * **More runtime loading configuration**: Support using the Handle type as a hint to select an asset loader (instead of relying on AssetPath extensions) * **More high level Processor trait implementations**: For example, it might be worth adding support for arbitrary chains of "asset transforms" that modify an in-memory asset representation between loading and saving. (ex: load a Mesh, run a `subdivide_mesh` transform, followed by a `flip_normals` transform, then save the mesh to an efficient compressed format). * **Bevy Scene Handle Deserialization**: (see the relevant [Draft TODO item](#draft-todo) for context) * **Explore High Level Load Interfaces**: See [this discussion](#discuss-on_loaded-high-level-interface) for one prototype. * **Asset Streaming**: It would be great if we could stream Assets (ex: stream a long video file piece by piece) * **ID Exchanging**: In this PR Asset Handles/AssetIds are bigger than they need to be because they have a Uuid enum variant. If we implement an "id exchanging" system that trades Uuids for "efficient runtime ids", we can cut down on the size of AssetIds, making them more efficient. This has some open design questions, such as how to spawn entities with "default" handle values (as these wouldn't have access to the exchange api in the current system). * **Asset Path Fixup Tooling**: Assets that inline asset paths inside them will break when an asset moves. The asset system provides the functionality to detect when paths break. We should build a framework that enables formats to define "path migrations". This is especially important for scene files. For editor-generated files, we should also consider using UUIDs (see other bullet point) to avoid the need to migrate in these cases. --------- Co-authored-by: BeastLe9enD <beastle9end@outlook.de> Co-authored-by: Mike <mike.hsu@gmail.com> Co-authored-by: Nicola Papale <nicopap@users.noreply.github.com> |
||
|
|
d96933ad9c |
bevy_scene: Add SceneFilter (#6793)
# Objective
Currently, `DynamicScene`s extract all components listed in the given
(or the world's) type registry. This acts as a quasi-filter of sorts.
However, it can be troublesome to use effectively and lacks decent
control.
For example, say you need to serialize only the following component over
the network:
```rust
#[derive(Reflect, Component, Default)]
#[reflect(Component)]
struct NPC {
name: Option<String>
}
```
To do this, you'd need to:
1. Create a new `AppTypeRegistry`
2. Register `NPC`
3. Register `Option<String>`
If we skip Step 3, then the entire scene might fail to serialize as
`Option<String>` requires registration.
Not only is this annoying and easy to forget, but it can leave users
with an impossible task: serializing a third-party type that contains
private types.
Generally, the third-party crate will register their private types
within a plugin so the user doesn't need to do it themselves. However,
this means we are now unable to serialize _just_ that type— we're forced
to allow everything!
## Solution
Add the `SceneFilter` enum for filtering components to extract.
This filter can be used to optionally allow or deny entire sets of
components/resources. With the `DynamicSceneBuilder`, users have more
control over how their `DynamicScene`s are built.
To only serialize a subset of components, use the `allow` method:
```rust
let scene = builder
.allow::<ComponentA>()
.allow::<ComponentB>()
.extract_entity(entity)
.build();
```
To serialize everything _but_ a subset of components, use the `deny`
method:
```rust
let scene = builder
.deny::<ComponentA>()
.deny::<ComponentB>()
.extract_entity(entity)
.build();
```
Or create a custom filter:
```rust
let components = HashSet::from([type_id]);
let filter = SceneFilter::Allowlist(components);
// let filter = SceneFilter::Denylist(components);
let scene = builder
.with_filter(Some(filter))
.extract_entity(entity)
.build();
```
Similar operations exist for resources:
<details>
<summary>View Resource Methods</summary>
To only serialize a subset of resources, use the `allow_resource`
method:
```rust
let scene = builder
.allow_resource::<ResourceA>()
.extract_resources()
.build();
```
To serialize everything _but_ a subset of resources, use the
`deny_resource` method:
```rust
let scene = builder
.deny_resource::<ResourceA>()
.extract_resources()
.build();
```
Or create a custom filter:
```rust
let resources = HashSet::from([type_id]);
let filter = SceneFilter::Allowlist(resources);
// let filter = SceneFilter::Denylist(resources);
let scene = builder
.with_resource_filter(Some(filter))
.extract_resources()
.build();
```
</details>
### Open Questions
- [x] ~~`allow` and `deny` are mutually exclusive. Currently, they
overwrite each other. Should this instead be a panic?~~ Took @soqb's
suggestion and made it so that the opposing method simply removes that
type from the list.
- [x] ~~`DynamicSceneBuilder` extracts entity data as soon as
`extract_entity`/`extract_entities` is called. Should this behavior
instead be moved to the `build` method to prevent ordering mixups (e.g.
`.allow::<Foo>().extract_entity(entity)` vs
`.extract_entity(entity).allow::<Foo>()`)? The tradeoff would be
iterating over the given entities twice: once at extraction and again at
build.~~ Based on the feedback from @Testare it sounds like it might be
better to just keep the current functionality (if anything we can open a
separate PR that adds deferred methods for extraction, so the
choice/performance hit is up to the user).
- [ ] An alternative might be to remove the filter from
`DynamicSceneBuilder` and have it as a separate parameter to the
extraction methods (either in the existing ones or as added
`extract_entity_with_filter`-type methods). Is this preferable?
- [x] ~~Should we include constructors that include common types to
allow/deny? For example, a `SceneFilter::standard_allowlist` that
includes things like `Parent` and `Children`?~~ Consensus suggests we
should. I may split this out into a followup PR, though.
- [x] ~~Should we add the ability to remove types from the filter
regardless of whether an allowlist or denylist (e.g.
`filter.remove::<Foo>()`)?~~ See the first list item
- [x] ~~Should `SceneFilter` be an enum? Would it make more sense as a
struct that contains an `is_denylist` boolean?~~ With the added
`SceneFilter::None` state (replacing the need to wrap in an `Option` or
rely on an empty `Denylist`), it seems an enum is better suited now
- [x] ~~Bikeshed: Do we like the naming convention? Should we instead
use `include`/`exclude` terminology?~~ Sounds like we're sticking with
`allow`/`deny`!
- [x] ~~Does this feature need a new example? Do we simply include it in
the existing one (maybe even as a comment?)? Should this be done in a
followup PR instead?~~ Example will be added in a followup PR
### Followup Tasks
- [ ] Add a dedicated `SceneFilter` example
- [ ] Possibly add default types to the filter (e.g. deny things like
`ComputedVisibility`, allow `Parent`, etc)
---
## Changelog
- Added the `SceneFilter` enum for filtering components and resources
when building a `DynamicScene`
- Added methods:
- `DynamicSceneBuilder::with_filter`
- `DynamicSceneBuilder::allow`
- `DynamicSceneBuilder::deny`
- `DynamicSceneBuilder::allow_all`
- `DynamicSceneBuilder::deny_all`
- `DynamicSceneBuilder::with_resource_filter`
- `DynamicSceneBuilder::allow_resource`
- `DynamicSceneBuilder::deny_resource`
- `DynamicSceneBuilder::allow_all_resources`
- `DynamicSceneBuilder::deny_all_resources`
- Removed methods:
- `DynamicSceneBuilder::from_world_with_type_registry`
- `DynamicScene::from_scene` and `DynamicScene::from_world` no longer
require an `AppTypeRegistry` reference
## Migration Guide
- `DynamicScene::from_scene` and `DynamicScene::from_world` no longer
require an `AppTypeRegistry` reference:
```rust
// OLD
let registry = world.resource::<AppTypeRegistry>();
let dynamic_scene = DynamicScene::from_world(&world, registry);
// let dynamic_scene = DynamicScene::from_scene(&scene, registry);
// NEW
let dynamic_scene = DynamicScene::from_world(&world);
// let dynamic_scene = DynamicScene::from_scene(&scene);
```
- Removed `DynamicSceneBuilder::from_world_with_type_registry`. Now the
registry is automatically taken from the given world:
```rust
// OLD
let registry = world.resource::<AppTypeRegistry>();
let builder = DynamicSceneBuilder::from_world_with_type_registry(&world,
registry);
// NEW
let builder = DynamicSceneBuilder::from_world(&world);
```
|
||
|
|
17f045e2a0 |
Delay asset hot reloading (#8503)
# Objective - Fix #5631 ## Solution - Wait 50ms (configurable) after the last modification event before reloading an asset. --- ## Changelog - `AssetPlugin::watch_for_changes` is now a `ChangeWatcher` instead of a `bool` - Fixed https://github.com/bevyengine/bevy/issues/5631 ## Migration Guide - Replace `AssetPlugin::watch_for_changes: true` with e.g. `ChangeWatcher::with_delay(Duration::from_millis(200))` --------- Co-authored-by: François <mockersf@gmail.com> |
||
|
|
e0e5f3acd4 |
add a default font (#8445)
# Objective - Have a default font ## Solution - Add a font based on FiraMono containing only ASCII characters and use it as the default font - It is behind a feature `default_font` enabled by default - I also updated examples to use it, but not UI examples to still show how to use a custom font --- ## Changelog * If you display text without using the default handle provided by `TextStyle`, the text will be displayed |
||
|
|
7b38de0a64 |
(De) serialize resources in scenes (#6846)
# Objective Co-Authored-By: davier [bricedavier@gmail.com](mailto:bricedavier@gmail.com) Fixes #3576. Adds a `resources` field in scene serialization data to allow de/serializing resources that have reflection enabled. ## Solution Most of this code is taken from a previous closed PR: https://github.com/bevyengine/bevy/pull/3580. Most of the credit goes to @Davier , what I did was mostly getting it to work on the latest main branch of Bevy, along with adding a few asserts in the currently existing tests to be sure everything is working properly. This PR changes the scene format to include resources in this way: ``` ( resources: { // List of resources here, keyed by resource type name. }, entities: [ // Previous scene format here ], ) ``` An example taken from the tests: ``` ( resources: { "bevy_scene::serde::tests::MyResource": ( foo: 123, ), }, entities: { // Previous scene format here }, ) ``` For this, a `resources` fields has been added on the `DynamicScene` and the `DynamicSceneBuilder` structs. The latter now also has a method named `extract_resources` to properly extract the existing resources registered in the local type registry, in a similar way to `extract_entities`. --- ## Changelog Added: Reflect resources registered in the type registry used by dynamic scenes will now be properly de/serialized in scene data. ## Migration Guide Since the scene format has been changed, the user may not be able to use scenes saved prior to this PR due to the `resources` scene field being missing. ~~To preserve backwards compatibility, I will try to make the `resources` fully optional so that old scenes can be loaded without issue.~~ ## TODOs - [x] I may have to update a few doc blocks still referring to dynamic scenes as mere container of entities, since they now include resources as well. - [x] ~~I want to make the `resources` key optional, as specified in the Migration Guide, so that old scenes will be compatible with this change.~~ Since this would only be trivial for ron format, I think it might be better to consider it in a separate PR/discussion to figure out if it could be done for binary serialization too. - [x] I suppose it might be a good idea to add a resources in the scene example so that users will quickly notice they can serialize resources just like entities. --------- Co-authored-by: Carter Anderson <mcanders1@gmail.com> |
||
|
|
aefe1f0739 |
Schedule-First: the new and improved add_systems (#8079)
Co-authored-by: Mike <mike.hsu@gmail.com> |
||
|
|
fd1af7c8b8 |
Replace multiple calls to add_system with add_systems (#8001)
|
||
|
|
a8a62fcf3d |
[Fixes #6059] `Entity`'s “ID” should be named “index” instead (#6107)
# Objective Fixes #6059, changing all incorrect occurrences of ``id`` in the ``entity`` module to ``index``: * struct level documentation, * ``id`` struct field, * ``id`` method and its documentation. ## Solution Renaming and verifying using CI. Co-authored-by: Edvin Kjell <43633999+Edwox@users.noreply.github.com> |
||
|
|
e71c4d2802 |
fix nightly clippy warnings (#6395)
# Objective
- fix new clippy lints before they get stable and break CI
## Solution
- run `clippy --fix` to auto-fix machine-applicable lints
- silence `clippy::should_implement_trait` for `fn HandleId::default<T: Asset>`
## Changes
- always prefer `format!("{inline}")` over `format!("{}", not_inline)`
- prefer `Box::default` (or `Box::<T>::default` if necessary) over `Box::new(T::default())`
|
||
|
|
1bb751cb8d |
Plugins own their settings. Rework PluginGroup trait. (#6336)
# Objective Fixes #5884 #2879 Alternative to #2988 #5885 #2886 "Immutable" Plugin settings are currently represented as normal ECS resources, which are read as part of plugin init. This presents a number of problems: 1. If a user inserts the plugin settings resource after the plugin is initialized, it will be silently ignored (and use the defaults instead) 2. Users can modify the plugin settings resource after the plugin has been initialized. This creates a false sense of control over settings that can no longer be changed. (1) and (2) are especially problematic and confusing for the `WindowDescriptor` resource, but this is a general problem. ## Solution Immutable Plugin settings now live on each Plugin struct (ex: `WindowPlugin`). PluginGroups have been reworked to support overriding plugin values. This also removes the need for the `add_plugins_with` api, as the `add_plugins` api can use the builder pattern directly. Settings that can be used at runtime continue to be represented as ECS resources. Plugins are now configured like this: ```rust app.add_plugin(AssetPlugin { watch_for_changes: true, ..default() }) ``` PluginGroups are now configured like this: ```rust app.add_plugins(DefaultPlugins .set(AssetPlugin { watch_for_changes: true, ..default() }) ) ``` This is an alternative to #2988, which is similar. But I personally prefer this solution for a couple of reasons: * ~~#2988 doesn't solve (1)~~ #2988 does solve (1) and will panic in that case. I was wrong! * This PR directly ties plugin settings to Plugin types in a 1:1 relationship, rather than a loose "setup resource" <-> plugin coupling (where the setup resource is consumed by the first plugin that uses it). * I'm not a huge fan of overloading the ECS resource concept and implementation for something that has very different use cases and constraints. ## Changelog - PluginGroups can now be configured directly using the builder pattern. Individual plugin values can be overridden by using `plugin_group.set(SomePlugin {})`, which enables overriding default plugin values. - `WindowDescriptor` plugin settings have been moved to `WindowPlugin` and `AssetServerSettings` have been moved to `AssetPlugin` - `app.add_plugins_with` has been replaced by using `add_plugins` with the builder pattern. ## Migration Guide The `WindowDescriptor` settings have been moved from a resource to `WindowPlugin::window`: ```rust // Old (Bevy 0.8) app .insert_resource(WindowDescriptor { width: 400.0, ..default() }) .add_plugins(DefaultPlugins) // New (Bevy 0.9) app.add_plugins(DefaultPlugins.set(WindowPlugin { window: WindowDescriptor { width: 400.0, ..default() }, ..default() })) ``` The `AssetServerSettings` resource has been removed in favor of direct `AssetPlugin` configuration: ```rust // Old (Bevy 0.8) app .insert_resource(AssetServerSettings { watch_for_changes: true, ..default() }) .add_plugins(DefaultPlugins) // New (Bevy 0.9) app.add_plugins(DefaultPlugins.set(AssetPlugin { watch_for_changes: true, ..default() })) ``` `add_plugins_with` has been replaced by `add_plugins` in combination with the builder pattern: ```rust // Old (Bevy 0.8) app.add_plugins_with(DefaultPlugins, |group| group.disable::<AssetPlugin>()); // New (Bevy 0.9) app.add_plugins(DefaultPlugins.build().disable::<AssetPlugin>()); ``` |
||
|
|
7989cb2650 |
Add global time scaling (#5752)
# Objective - Make `Time` API more consistent. - Support time accel/decel/pause. ## Solution This is just the `Time` half of #3002. I was told that part isn't controversial. - Give the "delta time" and "total elapsed time" methods `f32`, `f64`, and `Duration` variants with consistent naming. - Implement accelerating / decelerating the passage of time. - Implement stopping time. --- ## Changelog - Changed `time_since_startup` to `elapsed` because `time.time_*` is just silly. - Added `relative_speed` and `set_relative_speed` methods. - Added `is_paused`, `pause`, `unpause` , and methods. (I'd prefer `resume`, but `unpause` matches `Timer` API.) - Added `raw_*` variants of the "delta time" and "total elapsed time" methods. - Added `first_update` method because there's a non-zero duration between startup and the first update. ## Migration Guide - `time.time_since_startup()` -> `time.elapsed()` - `time.seconds_since_startup()` -> `time.elapsed_seconds_f64()` - `time.seconds_since_startup_wrapped_f32()` -> `time.elapsed_seconds_wrapped()` If you aren't sure which to use, most systems should continue to use "scaled" time (e.g. `time.delta_seconds()`). The realtime "unscaled" time measurements (e.g. `time.raw_delta_seconds()`) are mostly for debugging and profiling. |
||
|
|
dc3f801239 |
Exclusive Systems Now Implement System. Flexible Exclusive System Params (#6083)
# Objective The [Stageless RFC](https://github.com/bevyengine/rfcs/pull/45) involves allowing exclusive systems to be referenced and ordered relative to parallel systems. We've agreed that unifying systems under `System` is the right move. This is an alternative to #4166 (see rationale in the comments I left there). Note that this builds on the learnings established there (and borrows some patterns). ## Solution This unifies parallel and exclusive systems under the shared `System` trait, removing the old `ExclusiveSystem` trait / impls. This is accomplished by adding a new `ExclusiveFunctionSystem` impl similar to `FunctionSystem`. It is backed by `ExclusiveSystemParam`, which is similar to `SystemParam`. There is a new flattened out SystemContainer api (which cuts out a lot of trait and type complexity). This means you can remove all cases of `exclusive_system()`: ```rust // before commands.add_system(some_system.exclusive_system()); // after commands.add_system(some_system); ``` I've also implemented `ExclusiveSystemParam` for `&mut QueryState` and `&mut SystemState`, which makes this possible in exclusive systems: ```rust fn some_exclusive_system( world: &mut World, transforms: &mut QueryState<&Transform>, state: &mut SystemState<(Res<Time>, Query<&Player>)>, ) { for transform in transforms.iter(world) { println!("{transform:?}"); } let (time, players) = state.get(world); for player in players.iter() { println!("{player:?}"); } } ``` Note that "exclusive function systems" assume `&mut World` is present (and the first param). I think this is a fair assumption, given that the presence of `&mut World` is what defines the need for an exclusive system. I added some targeted SystemParam `static` constraints, which removed the need for this: ``` rust fn some_exclusive_system(state: &mut SystemState<(Res<'static, Time>, Query<&'static Player>)>) {} ``` ## Related - #2923 - #3001 - #3946 ## Changelog - `ExclusiveSystem` trait (and implementations) has been removed in favor of sharing the `System` trait. - `ExclusiveFunctionSystem` and `ExclusiveSystemParam` were added, enabling flexible exclusive function systems - `&mut SystemState` and `&mut QueryState` now implement `ExclusiveSystemParam` - Exclusive and parallel System configuration is now done via a unified `SystemDescriptor`, `IntoSystemDescriptor`, and `SystemContainer` api. ## Migration Guide Calling `.exclusive_system()` is no longer required (or supported) for converting exclusive system functions to exclusive systems: ```rust // Old (0.8) app.add_system(some_exclusive_system.exclusive_system()); // New (0.9) app.add_system(some_exclusive_system); ``` Converting "normal" parallel systems to exclusive systems is done by calling the exclusive ordering apis: ```rust // Old (0.8) app.add_system(some_system.exclusive_system().at_end()); // New (0.9) app.add_system(some_system.at_end()); ``` Query state in exclusive systems can now be cached via ExclusiveSystemParams, which should be preferred for clarity and performance reasons: ```rust // Old (0.8) fn some_system(world: &mut World) { let mut transforms = world.query::<&Transform>(); for transform in transforms.iter(world) { } } // New (0.9) fn some_system(world: &mut World, transforms: &mut QueryState<&Transform>) { for transform in transforms.iter(world) { } } ``` |
||
|
|
01aedc8431 |
Spawn now takes a Bundle (#6054)
# Objective Now that we can consolidate Bundles and Components under a single insert (thanks to #2975 and #6039), almost 100% of world spawns now look like `world.spawn().insert((Some, Tuple, Here))`. Spawning an entity without any components is an extremely uncommon pattern, so it makes sense to give spawn the "first class" ergonomic api. This consolidated api should be made consistent across all spawn apis (such as World and Commands). ## Solution All `spawn` apis (`World::spawn`, `Commands:;spawn`, `ChildBuilder::spawn`, and `WorldChildBuilder::spawn`) now accept a bundle as input: ```rust // before: commands .spawn() .insert((A, B, C)); world .spawn() .insert((A, B, C); // after commands.spawn((A, B, C)); world.spawn((A, B, C)); ``` All existing instances of `spawn_bundle` have been deprecated in favor of the new `spawn` api. A new `spawn_empty` has been added, replacing the old `spawn` api. By allowing `world.spawn(some_bundle)` to replace `world.spawn().insert(some_bundle)`, this opened the door to removing the initial entity allocation in the "empty" archetype / table done in `spawn()` (and subsequent move to the actual archetype in `.insert(some_bundle)`). This improves spawn performance by over 10%:  To take this measurement, I added a new `world_spawn` benchmark. Unfortunately, optimizing `Commands::spawn` is slightly less trivial, as Commands expose the Entity id of spawned entities prior to actually spawning. Doing the optimization would (naively) require assurances that the `spawn(some_bundle)` command is applied before all other commands involving the entity (which would not necessarily be true, if memory serves). Optimizing `Commands::spawn` this way does feel possible, but it will require careful thought (and maybe some additional checks), which deserves its own PR. For now, it has the same performance characteristics of the current `Commands::spawn_bundle` on main. **Note that 99% of this PR is simple renames and refactors. The only code that needs careful scrutiny is the new `World::spawn()` impl, which is relatively straightforward, but it has some new unsafe code (which re-uses battle tested BundlerSpawner code path).** --- ## Changelog - All `spawn` apis (`World::spawn`, `Commands:;spawn`, `ChildBuilder::spawn`, and `WorldChildBuilder::spawn`) now accept a bundle as input - All instances of `spawn_bundle` have been deprecated in favor of the new `spawn` api - World and Commands now have `spawn_empty()`, which is equivalent to the old `spawn()` behavior. ## Migration Guide ```rust // Old (0.8): commands .spawn() .insert_bundle((A, B, C)); // New (0.9) commands.spawn((A, B, C)); // Old (0.8): commands.spawn_bundle((A, B, C)); // New (0.9) commands.spawn((A, B, C)); // Old (0.8): let entity = commands.spawn().id(); // New (0.9) let entity = commands.spawn_empty().id(); // Old (0.8) let entity = world.spawn().id(); // New (0.9) let entity = world.spawn_empty(); ``` |
||
|
|
cd15f0f5be |
Accept Bundles for insert and remove. Deprecate insert/remove_bundle (#6039)
# Objective Take advantage of the "impl Bundle for Component" changes in #2975 / add the follow up changes discussed there. ## Solution - Change `insert` and `remove` to accept a Bundle instead of a Component (for both Commands and World) - Deprecate `insert_bundle`, `remove_bundle`, and `remove_bundle_intersection` - Add `remove_intersection` --- ## Changelog - Change `insert` and `remove` now accept a Bundle instead of a Component (for both Commands and World) - `insert_bundle` and `remove_bundle` are deprecated ## Migration Guide Replace `insert_bundle` with `insert`: ```rust // Old (0.8) commands.spawn().insert_bundle(SomeBundle::default()); // New (0.9) commands.spawn().insert(SomeBundle::default()); ``` Replace `remove_bundle` with `remove`: ```rust // Old (0.8) commands.entity(some_entity).remove_bundle::<SomeBundle>(); // New (0.9) commands.entity(some_entity).remove::<SomeBundle>(); ``` Replace `remove_bundle_intersection` with `remove_intersection`: ```rust // Old (0.8) world.entity_mut(some_entity).remove_bundle_intersection::<SomeBundle>(); // New (0.9) world.entity_mut(some_entity).remove_intersection::<SomeBundle>(); ``` Consider consolidating as many operations as possible to improve ergonomics and cut down on archetype moves: ```rust // Old (0.8) commands.spawn() .insert_bundle(SomeBundle::default()) .insert(SomeComponent); // New (0.9) - Option 1 commands.spawn().insert(( SomeBundle::default(), SomeComponent, )) // New (0.9) - Option 2 commands.spawn_bundle(( SomeBundle::default(), SomeComponent, )) ``` ## Next Steps Consider changing `spawn` to accept a bundle and deprecate `spawn_bundle`. |
||
|
|
28205fd3f4 |
Remove AssetServer::watch_for_changes() (#5968)
# Objective `AssetServer::watch_for_changes()` is racy and redundant with `AssetServerSettings`. Closes #5964. ## Changelog * Remove `AssetServer::watch_for_changes()` * Add `AssetServerSettings` to the prelude. * Minor cleanup. ## Migration Guide `AssetServer::watch_for_changes()` was removed. Instead, use the `AssetServerSettings` resource. ```rust app // AssetServerSettings must be inserted before adding the AssetPlugin or DefaultPlugins. .insert_resource(AssetServerSettings { watch_for_changes: true, ..default() }) ``` Co-authored-by: devil-ira <justthecooldude@gmail.com> |
||
|
|
ac1aebed5e |
Add reflect(skip_serializing) which retains reflection but disables automatic serialization (#5250)
# Objective - To address problems outlined in https://github.com/bevyengine/bevy/issues/5245 ## Solution - Introduce `reflect(skip_serializing)` on top of `reflect(ignore)` which disables automatic serialisation to scenes, but does not disable reflection of the field. --- ## Changelog - Adds: - `bevy_reflect::serde::type_data` module - `SerializationData` structure for describing which fields are to be/not to be ignored, automatically registers as type_data for struct-based types - the `skip_serialization` flag for `#[reflect(...)]` - Removes: - ability to ignore Enum variants in serialization, since that didn't work anyway ## Migration Guide - Change `#[reflect(ignore)]` to `#[reflect(skip_serializing)]` where disabling reflection is not the intended effect. - Remove ignore/skip attributes from enum variants as these won't do anything anymore |
||
|
|
1b9720526e |
Scene example: write file in a task (#5952)
# Objective - Fix #5951 - Improve on #5949 by not risking blocking a system ## Solution - Wrap file writing in a task |
||
|
|
f83a9c23f2 |
Add writing of scene data to Scene example (#5949)
# Objective Alice says to make this PR: https://discord.com/channels/691052431525675048/745805740274614303/1018554340841107477 - The "scene" example in the examples folder has a TODO comment about writing the serialized data to a file. This PR implements that. ## Solution The `AssetIo` trait in the `AssetServer` only supports reading data, not writing it. So, I used `std::io::File` for the implementation. This way, every time you run the example, it will mutate the file in-place. I had thought about adding a UUID string to the example Component, so that every time you run the example, the file will be guaranteed to change (currently, it just writes the same numbers over and over). However, I didn't bother because it was beyond the scope of the TODO comment. One thing to note is that the logic for serializing the scene into RON data has changed since the existing RON file was created, and so even though the data is the same, it's rendered in a different order for whatever reason. I left the changed output to the example file, because it's presumably trivial. I can remove it and force-push if you don't want that included in here. |