Skip to content

Reconciliation (allowing state-preserving hot-reload)#36

Open
villor wants to merge 16 commits intocart:next-gen-scenesfrom
villor:bsn-reconcile
Open

Reconciliation (allowing state-preserving hot-reload)#36
villor wants to merge 16 commits intocart:next-gen-scenesfrom
villor:bsn-reconcile

Conversation

@villor
Copy link
Copy Markdown

@villor villor commented Jul 25, 2025

Based on: bevyengine#20158
Depends on: #35 (commit included in this branch)

Objective

Reconciliation is the process of incrementally updating entities, components,
and relationships from a ResolvedScene by storing state from previous reconciliations.

This idea is based on previous work by @NthTensor in i-cant-believe-its-not-bsn.

Since this may be a bit controversial to merge onto the "main" BSN branch, here's how you can try it out today:

[dependencies]
bevy = { git = "https://github.com/villor/bevy.git", branch = "bsn-reconcile" }

I'll do my best to keep this up-to-date with cart/next-gen-scenes.

How does it work?

When a scene is reconciled on an entity, it will:

  1. Build the templates from the scene.
  2. Remove components that were previously inserted during reconciliation but should no longer be present.
    • This includes components that were present in the previous bundle but absent in the new one
    • and components that were explicit in the previous bundle but implicit (required components) in the new one.
  3. Insert the new components onto the entity.
    • Note: There is no diffing of values involved here, components are re-inserted on every reconciliation.
  4. Map related entities to previously reconciled entities by their ReconcileAnchors or otherwise spawn new entities
    • Entities can be explicitly "keyed" using the Name component - which makes them identifiable among their siblings. This also works with #MyEntityName syntax in bsn!.
    • Entities that are not explicitly keyed are "anonymous" and will be recycled in order, which may cause state loss.
  5. Despawn any leftover orphans from outdated relationships.
  6. Recursively reconcile related entities (1).
  7. Store the state of the reconciliation in a ReconcileReceipt component on the entity.

Why is it useful?

Non-destructive hot reloads

The obvious use case for an algorithm like this would be hot reloading of scenes. When using reconciliation, scenes can be hot reloaded while maintaing state that isn't part of the explicit scene output.

Demo from Discord: https://discord.com/channels/691052431525675048/1264881140007702558/1396963952050835527

Reactivity

On the web, reconciliation is a common technique for updating the DOM from an intermediate representation (aka VDOM). This is sometimes referred to as "coarse grained", and does have its performance penalties compared to more fine grained surgical updates. An upside to reconciliation is that it doesn't require a lot of special casing for templates - scenes can be built using familiar rust expressions.

The algorithm included here is not reactivity by itself, but it could potentially be used for future experiments in a number of ways, for example:

  • "Immediate mode reactivity" by reconciling on every frame - possibly by memoizing certain parts of the tree as an optimization.
  • Coarse/medium-grained reactivity involving observers and/or change detection to reconcile parts of the tree on-demand.

The "state vs output" problem

One issue when using reconciliation (or reactivity in general) which became very clear when trying to reconcile bevy_feathers widgets, is that we are storing mutable state and scene output on the same entities. It is convenient to include the internal state components in the bsn! to instantiate them. This poses a problem when using reconciliation, as the state components would be overwritten with their defaults on every reconciliation.

One solution to this problem is to use required components for internal state. That way those components are "implicit" and won't be overwritten on each reconciliation. I have converted the internal state of all feathers controls to use this approcach, making them usable with reconciliation.

Another solution we could consider would be syntax in bsn! for marking components as implicit/required, hinting that they should only be inserted once.

Implementation caveats/TODOs

  • Currently re-builds every Template on every reconciliation. This may cause issues with implementations that assume the template runs on a freshly spawned entity. When testing it with on/OnTemplate however, it looks like it doesn't create any duplicate observers
    • UPDATE: This is no longer true after 0.17, where duplicates are created on every reconcile. Worked around it by adding a OnHandle component to ensure observers of a certain type are added once, and removed when that component is removed. Seems to work well with reconciliation.
  • Currently not integrated with the deferred/async scene systems, all dependencies must be loaded before reconciliation.
  • Components are inserted one-by-one just like spawn_scene. Should by inserted as a single (dynamic) bundle to minimze archetype moves.
  • Plenty of room for optimization (lots of allocations)
  • Siblings that have equal Names are not handled and causes chaos - fall back to auto-inc and log a warning?

Example: Subsecond hot reload

This is the example from the demo on Discord, try it out with subsecond hot patching enabled!

use bevy::{
    color::palettes::tailwind,
    core_widgets::CoreWidgetsPlugins,
    feathers::{
        controls::{checkbox, CheckboxProps},
        dark_theme::create_dark_theme,
        theme::UiTheme,
        FeathersPlugin,
    },
    input_focus::{
        tab_navigation::{TabGroup, TabNavigationPlugin},
        InputDispatchPlugin,
    },
    prelude::*,
    scene2::prelude::{Scene, *},
};

fn main() {
    App::new()
        .add_plugins((
            DefaultPlugins,
            CoreWidgetsPlugins,
            InputDispatchPlugin,
            TabNavigationPlugin,
            FeathersPlugin,
        ))
        .insert_resource(UiTheme(create_dark_theme()))
        .add_systems(Startup, setup)
        .add_systems(Update, reconcile_ui)
        .run();
}

#[derive(Component, Clone, Default)]
struct UiRoot;

fn setup(mut commands: Commands) {
    commands.spawn(Camera2d);
    commands.spawn(UiRoot);
}

fn reconcile_ui(ui_root: Single<Entity, With<UiRoot>>, mut commands: Commands) {
    commands.entity(*ui_root).reconcile_scene(demo_root());
}

fn demo_root() -> impl Scene {
    bsn! {
        Node {
            width: Val::Percent(100.0),
            height: Val::Percent(100.0),
            align_items: AlignItems::Center,
            justify_content: JustifyContent::Center,
        }
        TabGroup
        BackgroundColor(tailwind::NEUTRAL_900)
        [
            Node {
                flex_direction: FlexDirection::Column,
                row_gap: Val::Px(8.0),
            } [
                #A :todo_item("Write BSN"),
                #B :todo_item("Hot reload it!"),
                #E :todo_item("Move things around"),
                #C :todo_item("Add checkboxes"),
                #D :todo_item("Try some styling"),
            ]
        ]
    }
}

fn todo_item(title: &'static str) -> impl Scene {
    bsn! {
        :checkbox(CheckboxProps::default())
        Node {
            column_gap: Val::Px(10.0),
            padding: UiRect::all(Val::Px(6.0)),
            border: UiRect::all(Val::Px(1.0)),
            align_items: AlignItems::Center,
        }
        BorderColor::all(tailwind::NEUTRAL_700.into())
        BorderRadius::all(Val::Px(5.0))
        BackgroundColor(tailwind::NEUTRAL_800)
        [
            Text(title)
            TextColor(tailwind::NEUTRAL_100) TextFont { font_size: 16.0 }
        ]
    }
}

@villor villor changed the title Reconciliation(allowing state-preserving hot-reload) Reconciliation (allowing state-preserving hot-reload) Jul 25, 2025
github-merge-queue bot pushed a commit to bevyengine/bevy that referenced this pull request Mar 27, 2026
…23413)

After much [iteration](#20158),
[designing](#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](#23413 (comment))
- [ ] 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](cart#36)
- [ ] **Reactivity**: This has been proven out
[here](https://github.com/viridia/bevy_reactor/)
- [ ] **BSN Sets**: See the [old design
doc](#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>
github-merge-queue bot pushed a commit to bevyengine/bevy that referenced this pull request Mar 27, 2026
…23413)

After much [iteration](#20158),
[designing](#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](#23413 (comment))
- [ ] 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](cart#36)
- [ ] **Reactivity**: This has been proven out
[here](https://github.com/viridia/bevy_reactor/)
- [ ] **BSN Sets**: See the [old design
doc](#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>
splo pushed a commit to splo/bevy that referenced this pull request Mar 31, 2026
…evyengine#23413)

After much [iteration](bevyengine#20158),
[designing](bevyengine#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 bevyengine#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](bevyengine#23413 (comment))
- [ ] 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](cart#36)
- [ ] **Reactivity**: This has been proven out
[here](https://github.com/viridia/bevy_reactor/)
- [ ] **BSN Sets**: See the [old design
doc](bevyengine#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>
Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment

Labels

None yet

Projects

None yet

Development

Successfully merging this pull request may close these issues.

2 participants