diff --git a/Cargo.toml b/Cargo.toml index 15b2caa91ee63..1b8efc3ef0c72 100644 --- a/Cargo.toml +++ b/Cargo.toml @@ -5208,6 +5208,9 @@ rustdoc-args = [ "docs-rs/trait-tags.html", ] all-features = true +# approximately equal comand for docs.rs like build locally +# excluding example scraping and adding mergable info for compile speed +# cargo doc --lib -Zrustdoc-mergeable-info --config 'build.rustflags=["--cfg", "docsrs_dep"]' -Zhost-config -Ztarget-applies-to-host --config 'host.rustflags=["--cfg", "docsrs_dep"]' --config 'build.rustdocflags=["-Zunstable-options", "--generate-link-to-definition", "--generate-macro-expansion", "--html-after-content", "./docs-rs/trait-tags.html"]' --no-deps -p bevy --all-features [[example]] name = "monitor_info" diff --git a/crates/bevy_ecs/src/template.rs b/crates/bevy_ecs/src/template.rs index 60cc707abcba2..05262abbf381e 100644 --- a/crates/bevy_ecs/src/template.rs +++ b/crates/bevy_ecs/src/template.rs @@ -416,6 +416,8 @@ impl FromTemplate for T { pub trait SpecializeFromTemplate: Sized {} /// A [`Template`] reference to an [`Entity`]. +/// +/// This is only valid during scene spawning and should **never** be used as a [`Component`](bevy_ecs::prelude::Component) field. #[derive(Copy, Clone, Default, Debug)] pub enum EntityTemplate { /// A reference to a specific [`Entity`] diff --git a/crates/bevy_scene/macros/src/lib.rs b/crates/bevy_scene/macros/src/lib.rs index 66e5bacd79eac..88b31e4dedff1 100644 --- a/crates/bevy_scene/macros/src/lib.rs +++ b/crates/bevy_scene/macros/src/lib.rs @@ -4,497 +4,54 @@ mod scene_component; use proc_macro::TokenStream; use syn::{parse_macro_input, DeriveInput}; -/// Creates a `Scene` using BSN (Bevy Scene Notation) syntax. +/// Macro which returns a [`Scene`](https://docs.rs/bevy/latest/bevy/prelude/trait.Scene.html), comprehensive docs at [`bevy_scene`](https://docs.rs/bevy/latest/bevy/scene/index.html) /// -/// BSN is a concise DSL for defining Bevy scenes as hierarchical collections -/// of entities and components. -/// While BSN's syntax largely follows Rust, it has quite a few features. -/// Don't feel like you need to master all of it before you begin. -/// Start simple and check back on the documentation as you run into problems. -/// -/// Trying to decipher a strange combination of glyphs? Jump to the **Syntax Reference** section below. -/// -/// ## Basic usage -/// -/// Let's begin by spawning a single named entity with a couple of components: -/// -/// ```rust, ignore -/// #[derive(Component, Default, Clone)] -/// struct Score(u32); -/// -/// #[derive(Component, FromTemplate)] -/// struct Health { current: u32, max: u32 } -/// -/// // Spawns a single entity named "Player" with a Score and Health component. -/// world.spawn_scene(bsn! { -/// #Player -/// Score(0) -/// Health { current: 100, max: 100 } -/// }); -/// ``` -/// -/// Each `bsn!` block describes a single root entity. -/// -/// ## Children and relationships -/// -/// You can add child entities with the `Children` component. -/// To begin a new child entity, separate it from the previous one with a comma. -/// Separate components on the same entity with whitespace. -/// -/// ```rust, ignore -/// // ONE child entity with both the Head and Body components: -/// bsn! { -/// #Player -/// Life -/// Children [Head Body] -/// } -/// -/// // TWO child entities — one with Head, one with Body: +/// Example macro showcasing most syntax (complex, most scenes won't look like this): +/// ```rust,ignore /// bsn! { -/// #Player -/// Life -/// Children [Head, Body] -/// } -/// ``` -/// -/// **Warning:** Separating items with whitespace places them on the **same** entity; -/// separating them with commas creates **separate** entities. -/// This is a common source of problems for new BSN users, -/// so be sure to check your commas if an entity seems to be missing! -/// -/// Child entities can themselves have components and children, so scenes nest arbitrarily deep: -/// -/// ```rust, ignore -/// bsn! { -/// #Town -/// Children [ -/// // Parentheses and indentation help clarify the structure of nested scenes, -/// // but are optional — this scene would be the same without them. -/// ( -/// #Tavern -/// Children [ -/// #Innkeeper, -/// #Barkeep, -/// ] -/// ), -/// ( -/// #Blacksmith -/// Children [ -/// #Anvil, -/// ] -/// ), -/// ] -/// } -/// ``` -/// -/// Swap `Children` for any `RelationshipTarget` component to define custom relationships. -/// -/// To attach entities to an *existing* entity not created by this scene, use the `ChildOf` component directly. -/// The following example uses [`bsn_list!`] to spawn two entities both attached to a pre-existing root: -/// -/// ```rust, ignore -/// let root: Entity = /* some pre-existing entity */; -/// bsn_list! { -/// ( #Child1 ChildOf(root) ), -/// ( #Child2 ChildOf(#Child1) ), -/// } -/// ``` -/// -/// `ChildOf` accepts either a plain `Entity` value or a named `#Name` reference from the same scope. -/// -/// ## Named entity references -/// -/// The `#Name` syntax does two things at once: it adds a `Name("Name")` component to the entity -/// and registers it so that other entities in the same scene scope can refer to it by name. -/// To use a named entity as a value — for example, as a component field that holds an `Entity` id — -/// write `#Name` in the value position: -/// -/// ```rust, ignore -/// #[derive(Component, FromTemplate)] -/// struct Link(Entity); -/// -/// // Spawn a parent named "Hub" with two children that each hold a back-reference to it. -/// bsn! { -/// #Hub -/// Children [ -/// Link(#Hub), -/// Link(#Hub), -/// ] -/// } -/// ``` -/// -/// References can point in any direction within the scene: a parent can reference a descendant, -/// a child can reference a parent, and siblings within the same `Children [...]` block can -/// reference each other. -/// All names in a single `bsn!` call share one scope; names from composed or cached scenes -/// (`my_scene()`, `:my_scene`) live in their own separate scopes and are not visible here. -/// If two scopes both define the same name (e.g. both use `#Player`), each `#Player` resolves -/// to its own entity — there is no conflict or shadowing. -/// -/// For dynamic names computed at runtime, use `#{expr}`: -/// -/// ```rust, ignore -/// fn reference_named_entity(name: &str) -> impl Scene { -/// bsn! { #{name} } -/// } -/// ``` -/// -/// In [`bsn_list!`], all root entries share one scope, so sibling root entities can -/// cross-reference each other — see the `bevy_scene` crate docs for more details. -/// -/// ## Defaults and patching -/// -/// BSN supports *patching*: writing `Health { current: 100 }` creates a patch that sets only -/// `current`. Unmentioned fields keep their values from earlier patches or the type's defaults, -/// and multiple patches to the same component merge rather than overwrite. This works for both -/// `Clone + Default` and `FromTemplate` types. -/// -/// The difference between the two is about what values a field can hold at spawn time: -/// -/// - **`Clone + Default` types** (e.g. `#[derive(Component, Default, Clone)]`): the simple -/// case. This just works in BSN with no extra derives. All field values must be plain Rust values — the -/// template cannot fill them in correctly based on world or context state. -/// -/// - **`FromTemplate` types** (e.g. `#[derive(Component, FromTemplate)]`): needed when a field -/// requires spawn-time context. Use this when a field's type itself implements `FromTemplate` -/// — for example, `Handle` fields that resolve asset path strings, or `Entity` fields that -/// reference named entities in the scene. -/// -/// Because each approach generates a different `Template` implementation, `Clone + Default` and -/// `FromTemplate` cannot both be derived on the same type. This would create incoherent trait implementations! -/// Use `Clone + Default` by default, and switch to `FromTemplate` only when you need the extra flexibility it provides. -/// -/// ## Expressions and dynamic values -/// -/// BSN supports embedding arbitrary Rust expressions anywhere a value is expected, -/// using `{...}` (curly braces): -/// -/// ```rust, ignore -/// fn enemy(name: &str, hp: u32) -> impl Scene { -/// let sprite_path = name.to_string() + ".png"; -/// bsn! { -/// #{name} -/// Health { current: {hp}, max: {hp} } -/// Sprite { image: {sprite_path} } -/// } -/// } -/// ``` -/// -/// A `{...}` block can also hold an expression that implements `Scene` or, inside a -/// `Children [...]` block, one that implements `SceneList`: -/// -/// ```rust, ignore -/// fn unit_with_armor(unit_base: impl Scene) -> impl Scene { -/// bsn! { -/// {unit_base} -/// Armor(50) +/// some_scene() // include a scene function +/// #SomeName // entity name, will insert Name("SomeName") +/// ComponentA // component without fields: will use the default field values +/// ComponentB(0.0) // when setting a field, unmentioned fields will use defaults +/// Node { +/// height: px(0.1) // same with named fields, unmentioned ones stay default /// } -/// } -/// ``` -/// -/// `{...}` blocks can also be `unsafe` or `const` (but not both at once without nesting): -/// -/// ```rust, ignore -/// fn friendly(people: &[&'static str]) -> impl Scene { -/// bsn! { -/// Friend { -/// name: const {"John"} -/// /// SAFETY: Jesus take the wheel -/// father: unsafe {people.get_unchecked(0)} +/// on(|evt: On, mut query: Query<&mut ComponentB>| { // add an observer +/// let mut b = query.get_mut(evt.entity).unwrap(); +/// b.0 += evt.value; +/// }) +/// Children [ // spawning multiple related entities using a RelationshipTarget component +/// #Child1 ComponentA // whitespace doesn't have to be newlines +/// , // entities are comma-separated +/// (other_scene() #Child3), // parentheses around a single entity are optional +/// Link(#SomeName), // passing a entity reference to a component as `Entity`, component has to implement FromTemplate +/// @MySceneComponent { // components which derive SceneComponent have scenes and can be inherited from +/// @some_prop: 3, // props, look like fields prefixed with @ but end up passed to the components scene as arguments +/// normal_field: 5 // while normal fields are the actual fields of the component +/// }, +/// Node { +/// width: some_var // variables can be assigned to field values +/// } +/// ComponentB({some_variable + 3.}) // values can be expressions, when wrapped in {} +/// @Container { +/// @items: { +/// bsn_list![ // sometimes you may need to nest macro calls +/// #Item1 SomeComponent, // note: the name #Item1 here is in its own scope +/// some_scene() #Item2 +/// ] +/// } /// } -/// } -/// } -/// ``` -/// -/// **Note:** `.bsn` asset files will not support arbitrary Rust expressions, -/// as we do not intend to require Bevy games to ship a Rust compiler. -/// -/// ## Automatic type conversion -/// -/// BSN performs some automatic type conversion for you, -/// reducing boilerplate when creating scenes. -/// -/// The `bsn!` macro appends `.into()` to expressions (`{...}`), bare identifiers, closures, and string -/// literals when they appear as field values. This means any field assignment that would be -/// valid via Rust's standard [`From`]/[`Into`] traits works transparently in BSN — no explicit -/// cast needed: -/// -/// ```rust, ignore -/// #[derive(Component, Default, Clone)] -/// struct Label(String); -/// -/// let greeting: &'static str = "Hello"; -/// bsn! { -/// // &str → String via Into, no .to_string() required -/// Label(greeting) -/// } -/// ``` -/// -/// A related, more advanced trick is what makes string literals work as asset paths for `Handle` fields. -/// See the docs on `HandleTemplate` for more information! -/// -/// Note: non-string literals are used as-is. Only string literals get `.into()` appended; -/// all other literals (integers, floats, booleans) are emitted directly, -/// so `Health { current: 100 }` assigns `100` without any conversion. -/// If the types don't match (e.g. you forgot to append a decimal point to a float), -/// you'll get a normal Rust type error. -/// -/// ## Asset loading -/// -/// When a component field is a `Handle`, BSN accepts a string literal in its place. -/// The string is resolved to an asset handle at spawn time via the asset server, reusing -/// an existing handle if the asset is already loaded: -/// -/// ```rust, ignore -/// commands.spawn_scene(bsn! { -/// Sprite { image: "player.png" } -/// }); -/// ``` -/// -/// This works for your own `FromTemplate`-derived components too — any `Handle` field -/// automatically accepts asset path strings: -/// -/// ```rust, ignore -/// #[derive(Component, FromTemplate)] -/// struct Icon { -/// image: Handle, -/// tint: Color, -/// } -/// -/// commands.spawn_scene(bsn! { -/// Icon { image: "icon.png", tint: Color::WHITE } -/// }); -/// ``` -/// -/// ## Observers -/// -/// Use `on` inside `bsn!` to attach an entity observer — a closure that fires when a given -/// `EntityEvent` targets the entity. The first parameter's type determines which event is -/// observed. Multiple observers can be stacked on the same entity, and the closure has full -/// access to the ECS via system parameters: -/// -/// ```rust, ignore -/// #[derive(EntityEvent)] -/// struct Damage(u32); -/// -/// fn player() -> impl Scene { -/// bsn! { -/// Health { max: 100, current: 100 } -/// on(|ev: On, mut query: Query<&mut Health>| { -/// let mut health = query.get_mut(ev.target()).unwrap(); -/// health.current = health.current.saturating_sub(ev.0); -/// }) -/// } -/// } -/// -/// // `move` closures work too, capturing variables from the enclosing scope: -/// fn enemy(bonus_damage: u32) -> impl Scene { -/// bsn! { -/// on(move |ev: On, mut query: Query<&mut Health>| { -/// let mut health = query.get_mut(ev.target()).unwrap(); -/// health.current = health.current.saturating_sub(ev.0 + bonus_damage); -/// }) -/// } -/// } -/// ``` -/// -/// ## Scene composition and inheritance -/// -/// There are two ways to build on an existing scene: **inline composition** and **inheritance**. -/// -/// **Inline composition** calls a scene function directly inside `bsn!`. The parent's unresolved -/// templates are merged with the child's and everything resolves together in one pass: -/// -/// ```rust, ignore -/// fn base_enemy() -> impl Scene { -/// bsn! { -/// Health { current: 100, max: 100 } -/// Power(10) -/// } -/// } -/// -/// // Compose base_enemy() and patch just the fields that differ. -/// fn boss() -> impl Scene { -/// bsn! { -/// base_enemy() -/// Health { max: 500 } -/// Power(50) -/// } -/// } -/// ``` -/// -/// **Inheritance** uses the `:` prefix. The parent is *pre-resolved* first — its templates are -/// fully flattened into a `ResolvedScene` — and the child's patches are applied on top. -/// When the scene is parameterless, this will "cache" the scene and share it across all inheriting scenes. -/// For larger scenes that are cached and spawned many times, this can be much faster than re-computing -/// the scene each time. -/// -/// ```rust, ignore -/// fn boss() -> impl Scene { -/// bsn! { -/// :base_enemy -/// Health { max: 500 } -/// Power(50) -/// } -/// } -/// -/// // Asset inheritance (.bsn format not yet released, sorry!): -/// bsn! { -/// :"enemy.bsn" -/// Health { max: 500 } -/// } -/// ``` -/// -/// | | Function inheritance `:my_scene` | Asset inheritance `:"my_scene.bsn"` | Inline composition `my_scene()` | -/// |---------------------------|-------------------------------------|-------------------------------------|----------------------------------| -/// | Accepts parameters | Yes | No | Yes | -/// | Asset-based | No | Yes | No | -/// | Cached resolution | Parameterless scenes only | Yes | No | -/// -/// Prefer scene inheritance over inline composition in general: the expensive scene resolution is cached, saving work during reuse. -/// Inline composition should be reserved for parameterized scenes that vary based on a given input, -/// small scenes that are shared across contexts (like styles), -/// or one-off scenes that do not require reuse. -/// -/// /// ## Formatting BSN -/// -/// Whitespace, parentheses, and comments have no effect on the generated scene — -/// they exist purely to help you organize and read your code. -/// -/// **Whitespace** (spaces, newlines, tabs) separates items on the *same* entity. -/// Use it freely for alignment and to make groupings of both components and entities clearer. -/// -/// **Parentheses** group a set of items into one logical unit inside a `[...]` list. -/// Trailing commas are the delimiter used to end one entity and start another, -/// but these can be hard to spot in deeply nested or one-line scenes. -/// Parentheses (often with associated indentation) make the boundary explicit: -/// -/// ```rust, ignore -/// bsn! { -/// Children [ -/// // Hard to see where one entity ends and the next begins: -/// ComponentA -/// ComponentB, -/// ComponentA -/// ComponentC, -/// -/// // Much clearer: -/// ( -/// ComponentA -/// ComponentB -/// ), -/// ( -/// ComponentA -/// ComponentC -/// ), /// ] -/// } -/// ``` -/// -/// ```rust, ignore -/// // Without parentheses, one-line definitions of children are prone to subtle mistakes: -/// bsn! { Children [ComponentA ComponentB, ComponentC ComponentD] } -/// -/// // With parentheses, the structure is clear: -/// bsn! { Children [ (ComponentA ComponentB), (ComponentC ComponentD) ] } +/// }; /// ``` -/// -/// **Comments** (`//` line comments and `/* */` block comments) work exactly as in -/// normal Rust and are stripped by the macro before parsing. -/// -/// ## Syntax Reference -/// -/// ### Components on the same entity vs. separate entities -/// -/// | Syntax | Meaning | Explanation | -/// |--------|---------|-------------| -/// | `CompA CompB CompC` | Same entity | **Whitespace** between items — all go on the **same** entity | -/// | `A, B, C` | Separate entities | **Commas** inside `[…]` — each becomes its **own** entity | -/// | `(CompA CompB)` | Entity group | Parentheses group components for readability; equivalent to whitespace alone | -/// -/// ### Naming entities -/// -/// | Syntax | Meaning | Explanation | -/// |--------|---------|-------------| -/// | `#Name` | Named entity | Adds a `Name("Name")` component and registers the entity for cross-referencing within this scope | -/// | `#{ expr }` | Dynamic name | Names an entity using the result of a Rust expression | -/// -/// ### Relationships -/// -/// | Syntax | Meaning | Explanation | -/// |--------|---------|-------------| -/// | `Children [s1, s2]` | Add children | Spawns each entry as a child **of this** entity | -/// | `ChildOf(entity)` | Set parent | Makes **this** entity a child of `entity`; accepts a plain `Entity` or a `#Name` reference | -/// | `MyRel [s1, s2]` | Custom relationship | Like `Children`, but uses any `RelationshipTarget` component | -/// -/// ### Dynamic values -/// -/// | Syntax | Meaning | Explanation | -/// |--------|---------|-------------| -/// | `{ expr }` | Rust expression | Evaluated at spawn time; may be a component, `impl Scene`, or (inside `[…]`) `impl SceneList` | -/// | `field: { expr }` | Expression in field | Embeds a Rust expression as the value of a named field | -/// -/// ### Composition and inheritance -/// -/// | Syntax | Meaning | Explanation | -/// |--------|---------|-------------| -/// | `my_scene()` | Inline composition | Merges `my_scene`'s unresolved templates into this entity | -/// | `:my_scene` | Inheritance | Pre-resolves `my_scene` first, then patches on top | -/// | `:"path.bsn"` | Asset inheritance | Inherits from a `.bsn` asset file; requires `queue_spawn_scene` | -/// -/// ### Observers -/// -/// | Syntax | Meaning | Explanation | -/// |--------|---------|-------------| -/// | `on(\|ev: On\| { … })` | Observer | Attaches an entity observer that fires when `Ev` targets this entity | -/// -/// ### Other Rust syntax -/// -/// If you're new to Rust, you might struggle with some of its syntax when you see it in BSN. -/// Here are the most important syntax patterns to be aware of: -/// -/// | Syntax | Meaning | Explanation | -/// |--------|---------|-------------| -/// | `MyComponent` | Unit or defaulted struct | A struct with no fields, or in BSN only, with all fields at their defaults | -/// | `MyComponent { field: val, field2: val2 }` | Struct with named fields | Sets named fields; unmentioned fields keep their defaults or values from prior patches | -/// | `MyComponent(val1, val2)` | Tuple struct | Constructs a tuple-struct component | -/// | `MyEnum::Variant` | Enum variant | A value of the `MyEnum` type with the `Variant` variant | -/// | `module::MyComponent` | Path | A module path to a struct type | -/// | `GREEN`` | Constant | A constant value | -/// | `f32::PI` | Associated constant | A constant (here, `PI`) associated with a type (here, `f32`) | -/// | `\|param\| { … }` | Closure | A closure; effectively an unnamed function | -/// | `Vec` | Generic type | A type with a generic type parameter `T` | -/// | `spawn_enemy("orc", 10, true)` | Function call | Calls a function with the given arguments by position | -/// | `spawn_player()` | Argumentless function call | Calls a function that takes no arguments | -/// | `//` | Comment | Line comment; all text after `//` on the same line is ignored | -/// | `/* */` | Block comment | Standard Rust block comment; all text inside `/* */` is ignored | -/// | `bsn! { … }` | Macro call | Calls a macro on the value inside of the braces | -/// -/// ## Further reading -/// -/// See [`bsn_list!`] if you want to create multiple scenes at once, -/// or want to have multiple root entities. -/// -/// See the `bevy_scene` crate docs for a high-level overview of the key concepts. +#[doc(hidden)] #[proc_macro] pub fn bsn(input: TokenStream) -> TokenStream { crate::bsn::bsn(input) } -/// Creates a `SceneList` using BSN (Bevy Scene Notation) syntax. -/// -/// This is useful when you want multiple root entities in your scene -/// that do not share a common parent, or if you want to create multiple scenes at once. -/// -/// Like in [`bsn!`], commas separate entities, -/// while whitespace separates components on the same entity. -/// -/// All root entries in a [`bsn_list!`] share a single name scope, so sibling root entities -/// can cross-reference each other by `#Name`. -/// This is not possible with separate [`bsn!`] calls, and is a key motivation for using [`bsn_list!`]. /// -/// See [`bsn!`] for more details on syntax. -/// See the `bevy_scene` crate docs for a high-level overview of the key concepts. +#[doc(hidden)] #[proc_macro] pub fn bsn_list(input: TokenStream) -> TokenStream { crate::bsn::bsn_list(input) diff --git a/crates/bevy_scene/src/lib.rs b/crates/bevy_scene/src/lib.rs index cb46595b41459..0363359f9a271 100644 --- a/crates/bevy_scene/src/lib.rs +++ b/crates/bevy_scene/src/lib.rs @@ -6,7 +6,7 @@ //! A 2D character might need a distinct sprite entity for weapon, hat and boots. //! A UI popup might need text and multiple buttons for accept, cancel, minimize and close actions. //! Spawning these collections as individual, disjointed entities is tedious, error-prone, and hard to reuse. -//! A **scene** lets you describe a conceptual **object** — an entity, its components, children, and assets — once +//! A **scene** lets you describe a conceptual **object**, made of an entity, its components, children, and assets once //! and spawn it wherever you need it. //! //! Any scene system must overcome three challenges: @@ -26,7 +26,12 @@ //! by providing a terse syntax for defining [`Scene`]s inline. //! This brevity is essential: making it easier to review and understand scenes at a glance, //! resolve merge conflicts and keep file sizes under control. -//! The macro includes full Rust Analyzer support (autocomplete, go-to-definition, hover docs)! +//! The macro includes best-effort Rust-Analyzer support. Autocomplete, go-to-definition, and hover docs +//! should work basically everywhere! +//! +//! ## BSN syntax reference +//! +//! For a quick rundown on how to read and write BSN syntax, see the docs for [`bsn!`]. //! //! ## Quick Start //! @@ -58,8 +63,7 @@ //! // #Player adds a `Name("Player")` component to the root entity. //! // Children spawns two child entities: one with Sword, one with Shield. //! world.spawn_scene(bsn! { -//! // This names the entity "Player" -//! #Player +//! #Player // This names the entity "Player" //! Score(0) //! Children [ //! Sword, @@ -70,36 +74,33 @@ //! //! ## Core Concepts //! -//! - **[`Scene`]**: the main authoring type. A [`Scene`] is made up of [`Template`]s (one -//! per component, plus any related entities such as children). Use the [`bsn!`] macro to -//! create a scene. -//! - **[`SceneList`]**: a list of scenes, each producing a separate root entity. -//! Think of it as the `Vec` analogue to [`Scene`]'s single `Entity`. -//! Use the [`bsn_list!`] macro to create a scene list. -//! - **[`Template`]**: a data description that produces a component value at spawn time, -//! given access to the entity and world (e.g. resolving asset paths into handles, or -//! named entity references into [`Entity`] ids). Each component in a [`Scene`] is -//! represented by a [`Template`] rather than a concrete value. -//! - **[`FromTemplate`]**: associates a type with its canonical [`Template`]. Derive -//! [`FromTemplate`] on your components to generate a companion template type where each -//! field is independently set-or-unset, enabling per-field overrides. -//! - **[`ScenePatch`]**: an [`Asset`] that wraps a [`Scene`] together with its dependencies -//! and its [`ResolvedScene`] (once loaded). You'll encounter this when using asset -//! inheritance (`:"enemy.bsn"`), or when you want to treat a scene as a loadable, -//! hot-reloadable prefab. See also [`ScenePatchInstance`] for applying one to an entity. -//! - **[`ResolvedScene`]**: the fully-resolved, ready-to-spawn result produced by resolving -//! one or more [`Scene`]s. User code rarely interacts with this directly. +//! - **[`Scene`]**: Describes what a spawned [`Entity`] should look like, created using [`bsn!`] or, +//! in the future, `.bsn` asset files. Conceptually, a [`Scene`] contains a list of "entries" to apply to an [`Entity`]. +//! - **[`SceneList`]**: A list of scenes, returned by [`bsn_list!`]. +//! Each [`Scene`] in the list produces one [`Entity`]. +//! - **Scene Composition**: Composition works by including scenes in other scenes. The included scenes "entries" will be +//! treated as if they were written in the outer scene. +//! - **[`Template`]**: A [`Template`] is something that, given a spawn context (target [`Entity`], [`World`], etc), can produce some output. Think of it +//! as a "superpowered ECS-aware constructor" for a type. In the context of scenes, [`Template`]s are used to produce [`Component`]s and [`Bundle`]s. This +//! enables defining scenes without needing to pass in a bunch of their dependencies (such as assets). The [`FromTemplate`] trait is used to associate some +//! final output type (ex: a [`Component`]) with a canonical [`Template`] that produces it. [`FromTemplate`] / [`Template`] is automatically implemented for +//! types that implement [`Default`] + [`Clone`], which is generally preferred. You should manually derive [`FromTemplate`] when a type needs custom template logic +//! (ex: one of its fields is an "asset handle", which has custom template logic). +//! - **[`RelatedScenes`]**: These add a [`SceneList`] as related to this [`Scene`] by a specific [`relationship`](bevy_ecs::relationship::Relationship). +//! This kind of change is added to the [`Scene`] by specifying a [`RelationshipTarget`] component like [`Children`], followed by a [`SceneList`]. //! //! ## Spawning Scenes //! //! There are two approaches to spawning scenes: //! //! - **Immediate**: [`World::spawn_scene`] and [`Commands::spawn_scene`] -//! resolve and spawn in one step. Returns an error if any asset dependencies are not yet loaded. -//! Use this when your scene has no asset dependencies. +//! resolve and spawn in one step. +//! Returns an error if any asset dependencies are not yet loaded. //! - **Queued**: [`World::queue_spawn_scene`] and [`Commands::queue_spawn_scene`] //! register the scene's dependencies and wait for them to load before resolving and spawning. -//! Use this when inheriting from asset-based scenes. +//! When the dependencies are loaded (or there are no dependencies), the scene will spawn during +//! that frame's [`SpawnScene`] schedule, between [`Update`](bevy_app::main_schedule::Update) and +//! [`PostUpdate`](bevy_app::main_schedule::PostUpdate). //! //! In all cases, your `*_spawn_scene` method call should wrap an invocation of the [`bsn!`] macro, //! or call a function which returns a [`Scene`]. @@ -110,18 +111,18 @@ //! ## Entity Hierarchies and Relationships //! //! Use `Children [scene1, scene2]` inside [`bsn!`] to spawn child entities. -//! Children (and entities within [`bsn_list!`]) are separated by commas; +//! [`Children`] (and entities within [`bsn_list!`]) are separated by commas; //! add multiple components to the same entity by listing them without a comma: //! //! ```ignore -//! // Spawns one child entity -//! bsn! { #Parent Children [ComponentA ComponentB ComponentC] } +//! // Spawns one child entity with components A, B and C +//! bsn! { #Parent Children [A B C] } //! -//! // Spawns two child entities due to the added comma -//! bsn! { #Parent Children [ComponentA ComponentB, ComponentC] } +//! // Spawns two child entities, one with A and B, the other with C, due to the added comma +//! bsn! { #Parent Children [A B, C] } //! -//! // Spawns two child entities, but more clearly -//! bsn! { #Parent Children [(ComponentA ComponentB), ComponentC] } +//! // Spawns two child entities, but more clearly separated due to parentheses. +//! bsn! { #Parent Children [(A B), C] } //! ``` //! //! These invocations can be nested to build deeper hierarchies. @@ -130,16 +131,12 @@ //! bsn! { //! #Parent //! Children [ -//! #Child1 -//! ComponentA -//! ComponentB, +//! #Child1 SomeComponent, //! #Child2 -//! ComponentA +//! SomeComponent //! Children [ -//! #GrandChild1 -//! ComponentA, +//! #GrandChild1 SomeComponent, //! #GrandChild2 -//! ComponentB //! ] //! ] //! } @@ -153,17 +150,18 @@ //! Children [ //! ( //! #Child1 -//! ComponentA -//! ComponentB +//! SomeComponent //! ), //! ( //! #Child2 -//! ComponentA //! Children [ -//! #GrandChild1 -//! ComponentA, -//! #GrandChild2 -//! ComponentB +//! ( +//! #GrandChild1 +//! SomeComponent +//! ), +//! ( +//! #GrandChild2 +//! ) //! ] //! ), //! ] @@ -176,23 +174,58 @@ //! //! ## Named Entity References //! -//! The `#Name` syntax assigns a [`Name`] to an entity and registers it for cross-referencing. -//! Other entities in the same **scope** can refer to a named entity by its `#Name`, -//! receiving the resolved [`Entity`] id at spawn time. +//! The `#Name` syntax assigns a [`Name`] to an entity and registers it for cross-referencing within the same macro invocation. +//! In a few others places in the same bsn! invocation / scope, its possible to refer to a named entity by its `#Name`: +//! +//! ```ignore +//! bsn! { +//! #Name +//! my_scene(#Name) +//! ComponentA(#Name) +//! ComponentB { entity: #Name } +//! Children [ +//! ComponentC(#Name) +//! ] +//! } +//! ``` +//! +//! Notice that the "child entity" was able to access the parent entity via `#Name`. It is also possible for ancestors to access +//! their descendants: +//! +//! ```ignore +//! bsn! { +//! #Root +//! ComponentA(#Child1) +//! Children [ +//! #Child1, +//! #Child2, +//! ] +//! } +//! ``` +//! +//! Using `#Name` as a value in [`bsn!`] will result in an [`EntityTemplate`], which is a [`Template`] that resolves to an [`Entity`] +//! [`Component`]s with [`Entity`] fields should generally derive [`FromTemplate`], because [`Entity`] uses [`FromTemplate`] to map to [`EntityTemplate`]. //! //! ### Scope rules //! //! Each [`bsn!`] invocation creates its own name scope. A name is visible to the root -//! entity, its children, and any deeper descendants — as long as the reference is written -//! in the same [`bsn!`] call. Composed or cached scenes (via `my_scene()` or `:my_scene`) -//! each bring their own separate scope, so names do not leak across scene boundaries. +//! entity, its children, and any deeper descendants in the same call. The reverse is also +//! true: descendants can "look up" the hierarchy. +//! Composed scenes (via `my_scene(#Name)`) or [`SceneComponents`](SceneComponent) +//! each contain their own [`bsn!`] invocation and therefore their own scope, +//! so re-using the same name across multiple different scenes is fine. +//! However, the results of a named entity reference, the [`EntityTemplate`], +//! can be passed to other scenes. It is valid only during the spawning of a scene. +//! That means [`Components`](Component) should never store [`EntityTemplate`] fields, +//! they should store the resolved [`Entity`] instead and +//! derive [`FromTemplate`] to convert [`EntityTemplate`] automatically. //! //! If both a parent and a composed child define the same name (e.g. both use `#X`), -//! each scope's `#X` resolves to its own entity — there is no conflict or shadowing. +//! each scope's `#X` resolves to its own entity, avoiding conflicts or potentially unintuitive shadowing. //! //! In a [`bsn_list!`], all root entities share a single name scope, so sibling scenes //! can reference each other by name. This is useful for wiring up relationships between -//! entities that are spawned together — for example, a group of UI panels where each +//! entities that are spawned together. For example, a group of UI panels where each //! panel needs a relationship to its neighbor: //! //! ```ignore @@ -204,16 +237,53 @@ //! } //! ``` //! -//! ## Composition and Patching +//! ### Dynamic Name Values and Entity References +//! +//! `#SomeName` syntax will set `Name("SomeName")` in addition to making the entity reference-able in [`bsn!`]. `#Name` syntax is _always_ scoped and +//! doesn't support "dynamic" names. If you would like to _both_ reference an entity in [`bsn!`] _and_ provide a dynamic name, you can do this: +//! +//! ```ignore +//! let i = 0; +//! bsn! { +//! #Root +//! Name({format!("Entity {i}")}) +//! Children [ +//! Reference(#Root) +//! ] +//! } +//! ``` +//! Adding `Name("desired name")` after the `#SomeName` reference will patch over the `Name` component created by the reference to give it a custom name. //! -//! When you insert a component in normal ECS code, the entire pre-existing value is replaced. -//! If a base scene sets `Button { width: 100, height: 300 }` and a caller wants to +//! ## Patching +//! +//! When you insert a component into an [`Entity`] in normal ECS code, the entire pre-existing value is replaced. +//! If a scene sets `Button { width: 100, height: 300 }` and a caller wants to //! change just `width`, ordinary component insertion would force them to respecify `height` too. //! //! **Patching** avoids this. When you write `Button { width: 200 }` in [`bsn!`], it creates //! a *patch* that sets only the `width` field. Unmentioned fields keep their existing values -//! (from a parent scene, an earlier patch, or the type's defaults). Multiple patches to the -//! same component merge together rather than overwriting each other. +//! (from a included scene, an earlier patch, or the type's defaults). Multiple patches to the +//! same component and its values are applied in order, only overwriting the fields they changed. +//! +//! The following scenes all end up with a button which is 200 wide and 300 high. +//! ```ignore +//! impl Default for Button { +//! fn default() -> Self { +//! Button { width: 100, height: 300 } +//! } +//! } +//! +//! bsn! { Button { width: 200, height: 300 } } // fully specified +//! bsn! { Button { width: 200 } } // only changing width, height defaults to 300 +//! +//! bsn! { +//! Button // inserts defaults +//! Button { width: 200 } // changes width +//! Button { height: 300 } // changes height +//! } +//! ``` +//! +//! ### Required Traits //! //! To make a component available in [`bsn!`], derive either [`Default`] + [`Clone`], or [`FromTemplate`]. //! Both support patching: unmentioned fields keep their values from earlier patches or the @@ -221,21 +291,32 @@ //! //! The distinction is about what values a field can hold at spawn time: //! -//! - **`Clone + Default`** covers the simple case, and should be your default choice. The blanket [`Template`] impl handles patching -//! automatically with no extra derives needed. +//! - **[`Clone`] + [`Default`]** (e.g. `#[derive(Component, Default, Clone)]`): covers the simple case, and should be your default choice. +//! - **[`FromTemplate`]** (e.g. `#[derive(Component, FromTemplate)]`) is needed when a field requires spawn-time context. +//! Examples include [`Handle`] fields which need [`AssetServer`] to resolve asset paths, or [`Entity`] +//! fields which resolve [`EntityTemplate`]s from named entity references. If any of your fields' types +//! implement [`FromTemplate`] manually / have custom template logic, you should derive it for the parent type as well if you want your type +//! to use that logic. +//! +//! Deriving [`FromTemplate`] and [`Default`] on the same type is not allowed, as both would supply a [`FromTemplate`] impl and conflict. +//! [`FromTemplate`] derivers still have access to a default constructor of sorts though: the derive generates a companion struct +//! for `YourType` named `YourTypeTemplate` which implements `Default`, so `YourTypeTemplate::default()` serves the same purpose. +//! +//! #### Enums in bsn //! -//! - **[`FromTemplate`]** is needed when a field requires spawn-time context — for example, -//! a `Handle` field that resolves an asset path, or an [`Entity`] field that references -//! a named entity. If any of your fields' types implement [`FromTemplate`], -//! you must derive it for the parent type as well. +//! Enums are special cased due to their complexity regarding [`Default`] or similar traits: [`bsn!`] needs to have defaults for *all* variants accessible. //! -//! Deriving [`FromTemplate`] and [`Default`] on the same type is not allowed — -//! both would supply a [`FromTemplate`] impl and conflict. -//! You still have access to a default constructor of sorts though: the derive generates a companion -//! `YourTypeTemplate` struct that implements `Default`, so `YourTypeTemplate::default()` serves the same purpose. +//! For this reason, there is a custom "derive" which isn't actually a Trait, called [`VariantDefaults`](bevy_ecs::VariantDefaults) +//! which creates an impl block with one static "default" method for each variant, in the schema `default_{variant_lower}`. //! -//! You compose scenes by writing functions that return `impl Scene` and calling them -//! inside [`bsn!`]: +//! [`bsn!`] will use these when encountering a Enum instead of [`Default`]. Alternatively, [`FromTemplate`] also works. +//! +//! ## Composition +//! +//! Composition relies on patching to work nicely, allowing you to include other scenes in the current ones. +//! All of their patches will be applied at the position they're included. +//! +//! Example: //! //! ``` //! # use bevy_app::App; @@ -260,7 +341,7 @@ //! bsn! { Health { current: 100, max: 100 } } //! } //! -//! // Compose `enemy()` and patch just the `max` field: +//! // Include `enemy()` and patch just the `max` field: //! world.spawn_scene(bsn! { //! enemy() //! Health { max: 200 } @@ -271,59 +352,48 @@ //! while `current` retains the value from `enemy()`. Tuples of [`Scene`]s also implement //! [`Scene`], so patches from multiple sources merge into a single [`ResolvedScene`]. //! -//! For programmatic patching outside of [`bsn!`], see the [`PatchFromTemplate`] and +//! For programmatic patching outside of [`bsn!`], see the [`PatchFromTemplate`] and //! [`PatchTemplate`] traits. //! -//! ## Scene Inheritance +//! ## Scene Caching +//! +//!
//! -//! There are two ways to build on an existing scene: **inline composition** and **inheritance**. -//! Both let you patch fields on top of the parent, and both merge children from parent -//! and child (parent's children appear first). They differ in *when* the parent is resolved -//! and what kinds of parents they support. +//! Note: Caching is currently only implemented for scene assets. It hasn't yet been wired up for "function scenes" or [`SceneComponent`]s. Attempting to use +//! it in those cases will result in a compile error. //! -//! **Inline composition** (shown in the example above) calls a function directly inside -//! [`bsn!`]. The parent's templates are merged *unresolved* alongside the child's, and -//! everything resolves together in one pass: +//!
//! +//! Scenes can be cached, improving performance. Since this can change the semantics in some cases, its an explicit opt-in. +//! Caching works by resolving the included scene and storing the resulting [`ResolvedScene`] for future use. When the outer scene is spawned again, +//! it will not need to resolve the included scene again, instead patching on top of the cached version (using copy-on-write semantics for each [`Template`]). +//! This means caching can only be used if the scene is the first scene entry. +//! +//! This scene includes an uncached "enemy" scene: //! ```ignore -//! // Inline composition: call with parentheses, no `:` //! bsn! { //! enemy() //! Health { max: 200 } //! } //! ``` //! -//! **Inheritance** uses the `:` prefix. The parent is **pre-resolved** — its templates are -//! fully flattened into a [`ResolvedScene`] *before* the child's patches apply on top: -//! +//! This scene caches the "enemy" scene by adding the `:` prefix (however caching scene functions like this is not currently supported) //! ```ignore -//! // Inheritance: `:` prefix, no parentheses or arguments //! bsn! { //! :enemy //! Health { max: 200 } //! } +//! ``` //! -//! // Asset inheritance: `:` prefix with a string path to a ScenePatch asset -//! // DISCLAIMER: .bsn file format is not yet released! +//! Scene assets always need to be cached using the `:` prefix. +//! Note that the `.bsn` file format is not yet released. (This already works, assuming theres a loader for the asset format) +//! ```ignore //! bsn! { //! :"enemy.bsn" //! Health { max: 200 } //! } //! ``` //! -//! ### Which composition pattern should I choose? -//! -//! | | Function inheritance `:my_scene` | Asset inheritance `:"my_scene.bsn"` | Inline composition `my_scene()` | -//! |---------------------------|-------------------------------------|-------------------------------------|----------------------------------| -//! | Accepts parameters | Yes | No | Yes | -//! | Asset-based | No | Yes | No | -//! | Cached resolution | Parameterless scenes only | Yes | No | -//! -//! Prefer scene inheritance over inline composition in general: the expensive scene resolution is cached, saving work during reuse. -//! Inline composition should be reserved for parameterized scenes that vary based on a given input, -//! small scenes that are shared across contexts (like styles), -//! or one-off scenes that do not require reuse. -//! //! ## Loading Assets into Scenes //! //! Without the use of scenes, loading an asset requires referencing the [`AssetServer`] explicitly: @@ -333,8 +403,8 @@ //! commands.spawn(Sprite { image: handle, ..default() }); //! ``` //! -//! This can be particularly frustrating when defining helper functions, -//! requiring you to pipe asset handles or collections through multiple layers of function calls. +//! This can be particularly frustrating when defining helper functions for spawning entities, +//! which require you to pass [`AssetServer`] or handles through multiple layers of function calls. //! //! In [`bsn!`], asset paths work directly as field values. When a component field is a //! [`Handle`], the [`bsn!`] macro accepts a string literal in its place. Under the hood, @@ -348,8 +418,7 @@ //! }); //! ``` //! -//! This also works for components you define yourself. Any `Handle` field on a -//! [`FromTemplate`]-derived component automatically accepts asset path strings: +//! A [`Component`] must also derive [`FromTemplate`] to accept asset paths: //! //! ```ignore //! #[derive(Component, FromTemplate)] @@ -371,38 +440,46 @@ //! //! ## Observers //! -//! Use [`on`] inside [`bsn!`] to attach an entity observer — a closure that runs when a -//! given [`EntityEvent`] fires on that entity. The first parameter's type determines -//! which event is observed. You can attach multiple observers to the same entity, and -//! the closure has full access to the ECS via system parameters: +//! Use [`on()`](on) inside [`bsn!`] to attach an entity [`Observer`]. Entity observers are closures or +//! functions which fire when a given [`EntityEvent`] is triggered and targets this entity. +//! The first parameter's type determines which event is observed. Multiple observers can be added to +//! the same entity, and the observer has full access to the ECS via [system parameters](bevy_ecs::system#system-parameter-list): //! //! ```ignore //! #[derive(EntityEvent)] -//! struct Damage(u32); +//! struct Damage { +//! entity: Entity, +//! amount: u32, +//! } //! //! #[derive(EntityEvent)] -//! struct Heal(u32); +//! struct Heal { +//! entity: Entity, +//! amount: u32, +//! } //! //! fn player() -> impl Scene { //! bsn! { //! Health { max: 100, current: 100 } //! // Each `on(...)` attaches a separate observer. //! on(|damage: On, mut query: Query<&mut Health>| { -//! let mut health = query.get_mut(damage.target()).unwrap(); -//! health.current = health.current.saturating_sub(damage.0); -//! }) -//! on(|heal: On, mut query: Query<&mut Health>| { -//! let mut health = query.get_mut(heal.target()).unwrap(); -//! health.current = (health.current + heal.0).min(health.max); +//! let mut health = query.get_mut(damage.entity).unwrap(); +//! health.current = health.current.saturating_sub(damage.amount); //! }) +//! on(on_heal) //! } //! } +//! +//! fn on_heal(heal: On, query: Query<&Health>){ +//! let mut health = query.get_mut(heal.entity).unwrap(); +//! health.current = (health.current + heal.amount).min(health.max); +//! } //! ``` //! //! This is useful for self-contained logic like click handlers, damage reactions, //! or scripting-style triggers. -//! Closures passed to `on` work like any Rust closure: -//! you can use `move` and capture variables from the enclosing scope normally. +//! Closures passed to [`on`] work like any Rust closure: +//! you can use [`move`](https://doc.rust-lang.org/std/keyword.move.html) and capture variables from the enclosing scope normally. //! //! ## Using Dynamic Expressions in Scenes //! @@ -412,26 +489,57 @@ //! //! ```ignore //! fn enemy(hp: u32, name: &str) -> impl Scene { -//! let sprite_path = name.to_string() + ".png"; -//! +//! let name_string = name.to_string(); //! bsn! { //! #{name} -//! Health { current: {hp}, max: {hp} } -//! Sprite { image: {sprite_path} } +//! Health { current: {hp / 2}, max: hp } +//! Sprite { image: {name_string + ".png"} } //! } //! } //! //! // Call it like an ordinary Rust function -//! commands.spawn_scene(bsn! { enemy(200, "goblin.png") }); +//! commands.spawn_scene(bsn! { enemy(200, "goblin") }); //! ``` //! //! Braces are required when the macro would otherwise misparse the expression //! and for complex expressions like `{hp * 2}`. -//! Variables used as positional or named fields (like `hp` above) also need braces. //! -//! ### Dynamic children +//! ### Dynamic template values (component values) //! -//! You can splice a runtime [`SceneList`] into a `Children [...]` block with `{...}`: +//! A [`Template`] value, like a instance of a Component, cannot be directly passed in to a `bsn!` block, as `bsn!` +//! expects "scene variables" in that position. Instead use `template_value(...)` which accepts a given component [`Template`] value +//! and returns a [`Scene`] implementation for it. +//! +//! ```ignore +//! fn enemy(translation: Vec3){ +//! let transform = Transform::from_translation(translation); +//! bsn! { +//! #Foo +//! template_value(transform) +//! } +//! +//! } +//! ``` +//! +//! ### Ad-hoc template functions +//! +//! Sometimes you need custom behavior or world access to create a [`Template`]. +//! If this is the case, you can use [`template`](fn@template) instead of a custom [`FromTemplate`] or [`Template`] implementation. +//! In [`template`](fn@template) you get access to a [`TemplateContext`](bevy_ecs::template::TemplateContext) which +//! contains the [`EntityWorldMut`] and a collection of named entity references. +//! +//! ```ignore +//! bsn! { +//! #Foo +//! template(|ctx| { +//! Foo(ctx.resource::().get("generated_asset_name")) +//! }) +//! } +//! ``` +//! +//! ### Expressions as scenes +//! +//! You can insert a [`Scene`] or [`SceneList`] in another Scene using curly-bracketed expressions: //! //! ```ignore //! fn container(contents: impl SceneList) -> impl Scene { @@ -444,45 +552,43 @@ //! } //! } //! -//! let items = bsn_list![#A, #B, #C]; +//! let items = bsn_list![#A, #B, #C]; // or bsn! if container takes a `impl Scene` //! commands.spawn_scene(container(items)); //! ``` //! -//! ### Conditional components +//! ### Conditional values //! -//! There is no `if`/`match` syntax inside the [`bsn!`] grammar, but you can embed +//! There is no `if`/`match` syntax inside the [`bsn!`] grammar (yet!), but you can embed //! conditionals via `{...}` blocks or handle them outside the macro: //! //! ```ignore //! fn unit(is_boss: bool) -> impl Scene { //! let hp = if is_boss { 500 } else { 100 }; -//! bsn! { Health { current: {hp}, max: {hp} } } +//! bsn! { Health { current: hp, max: hp } } //! } //! ``` -//! -//! ### Expressions as scenes -//! -//! A `{...}` block can also represent a variable or -//! expression that implements [`Scene`]. -//! This allows you to pass in scenes to helper functions, -//! allowing you to provide APIs based around partially complete scenes: -//! +//! One way to achieve conditional scenes is using a [`Box`] to store different scenes in one variable. //! ```ignore -//! fn unit_with_armor(unit_base: impl Scene) -> impl Scene { +//! fn unit(is_boss: bool, level: u32) -> impl Scene { +//! let scene: Box = if is_boss { +//! Box::new(bsn! { +//! Boss +//! Followers [ // the boss is followed by some grunts +//! :unit(false, level - 1) #Grunt1, +//! :unit(false, level - 2) #Grunt2 +//! ] +//! }) +//! } else { +//! Box::new(bsn! { Grunt }) +//! }; //! bsn! { -//! {unit_base} -//! Armor(50) +//! Level(level) +//! {scene} //! } //! } -//! -//! let my_unit = bsn! { Health { current: 100, max: 100 } }; -//! commands.spawn_scene(unit_with_armor(my_unit)); //! ``` //! -//! ## BSN syntax reference -//! -//! For a quick rundown on how to read and write BSN syntax, -//! see the docs for [`bsn!`]. +//! We plan on making "conditional scenes" easier to define in future releases. //! //! ## Scene Components //! @@ -505,15 +611,15 @@ //! bsn! { //! #Player //! Children [ -//! Sword, -//! Shield, +//! #RightHand Sword, +//! #LeftHand Shield, //! ] //! } //! } //! } //! ``` //! -//! This enables inheriting the [`SceneComponent`] as a scene, using the following syntax: +//! This enables including the [`SceneComponent`] as a scene, using the following syntax: //! //! ```no_run //! # use bevy_scene::prelude::*; @@ -532,21 +638,12 @@ //! ``` //! //! This will spawn the `Player` component _and_ the entire scene with it. This means that you write -//! systems that query for the `Player` component, they can assume the rest of the scene will be there +//! systems that query for the `Player` component, they can generally assume the rest of the scene will be there //! too! //! //! [`SceneComponent`]s can only be spawned using scene APIs like [`World::spawn_scene`]. Spawning //! them using [`World::spawn`] will log an error. //! -//! ### Inheritance Syntax vs Patch Syntax -//! -//! Notice that this uses inheritance syntax in BSN (`:`), rather than normal "component patch" syntax -//! (ex: `bsn! { Player { score: 0 } }`. Semantically these are different things: -//! - Scene inheritance syntax: constructs the full scene and inherits from it -//! - Component patch syntax: _Just_ patches the component directly and creates it if it doesn't exist. -//! This will not do any scene inheritance. You can still patch scene components this way as long -//! as the scene component is cached somewhere "earlier" in the inheritance hierarchy. -//! //! ### Custom Scene Functions //! //! When deriving [`SceneComponent`], it defaults to using `Self::scene` as the "scene function". @@ -566,6 +663,9 @@ //! //! ### `SceneComponent` Asset Paths //! +//! Note: Currently, there is no `.bsn` asset format. This exists to help you understand what is planned, and what is currenty possible +//! with third-party asset formats. +//! //! Alternatively, a scene asset path can be specified: //! //! ``` @@ -578,7 +678,6 @@ //! } //! ``` //! -//! (Note that we haven't yet landed the `.bsn` asset format or ported the glTF asset loader to BSN) //! //! ### Scene Components are Template-able //! @@ -666,8 +765,9 @@ //! ``` //! //! Notice the `@field` syntax, which specifies that a prop is being set instead of a field. -//! Props are evaluated "immediately" at the point of inheritance where the scene is constructed. -//! This means that they are not "patchable". +//! Props are evaluated "immediately" when the scene is included in another scene. +//! This means that they are not "patchable", as at that point they have already been evaluated, +//! and they _produce_ "patchable" outputs. //! //! You can set _both_ props and normal fields at the same time: //! ```no_run @@ -716,7 +816,7 @@ //! } //! ``` //! However you _can_ patch the scene component in the scene if you would like. This comes in handy -//! if you would like props to contribute to the scene component's value: +//! if you would like props to contribute to the scene component's fields: //! //! ``` //! # use bevy_scene::prelude::*; @@ -763,24 +863,30 @@ //! //! ## .bsn Asset Format //! -//! In future releases, Bevy intends to offer a `.bsn` asset format. +//! Bevy does not currently have support for `.bsn` files, +//! but intends to offer a `.bsn` asset format in future releases. +//! //! This would allow you to define your scenes on disk, //! creating/modifying them in various authoring tools and using asset hot-reloading. //! //! This format is intended to have broad syntactic compatibility with the `bsn!` macro, //! making it easy to port your content between both the macro and the asset form. //! -//! Bevy does not currently have support for `.bsn` files: -//! for now, you should use existing non-Bevy asset formats like glTF, +//! When planning to use the asset format later, be aware that `.bsn` asset files, +//! unlike `bsn!` macro calls, will not support expressions or other dynamic features directly. +//! +//! For now, you should use existing non-Bevy asset formats like glTF, //! search for ecosystem implementations or stick to `bsn!` macro calls. //! -//! When planning, be aware that `.bsn` asset files, unlike `bsn!` macro calls, -//! will not support expressions or other dynamic features directly. +//! Note that the architecture to support an asset format already exists, +//! allowing community implementations/experimentation until an official version exists. An example of how to go about this +//! can be found in the [scene benchmarks]() //! //! [`Template`]: bevy_ecs::template::Template //! [`FromTemplate`]: bevy_ecs::template::FromTemplate //! [`Asset`]: bevy_asset::Asset //! [`Entity`]: bevy_ecs::entity::Entity +//! [`EntityTemplate`]: bevy_ecs::template::EntityTemplate //! [`RelationshipTarget`]: bevy_ecs::relationship::RelationshipTarget //! [`EntityEvent`]: bevy_ecs::event::EntityEvent //! [`Name`]: bevy_ecs::name::Name @@ -813,7 +919,6 @@ mod scene_patch; mod spawn; mod spawn_system; -pub use bevy_scene_macros::*; pub use resolved_scene::*; pub use scene::*; pub use scene_component::*; @@ -826,6 +931,139 @@ use bevy_app::{App, Plugin, SceneSpawnerSystems, SpawnScene}; use bevy_asset::AssetApp; use bevy_ecs::prelude::*; +/// Creates a `Scene` using BSN (Bevy Scene Notation) syntax. +/// +/// These docs primarily contain syntax +/// See [`bevy_scene`](crate) module-level docs for in-depth details about usage and interactions. +/// +/// +/// ## Syntax Reference +/// +/// The syntax consists of scene entries which are listed below, which all act on the scene in some way. +/// Often, this is by inserting/patching a component or its values or including other scenes. +/// Scene entries can have prefix characters which specify/disambiguate the following entry. +/// +/// ```text +/// bsn! { +/// +/// : +/// # +/// @ +/// ~ +/// } +/// ``` +/// +/// ### Scene entries +/// | Examples | Explanation | +/// | ------------------------------------------ | -------------------------------------------------------------------------------------------------------------- | +/// | `CompA` | A unit or default component. Fields, if any exist, will be default | +/// | `CompA(val)`
`CompA(val, val)` | Tuple Component with some fields specified. Unspecified fields will be default, see [patching](self#patching) | +/// | `CompA { name: val }` | Component with some fields specified. Unspecified fields will be default, see [patching](self#patching) | +/// | `mymodule::CompA { name: val }` | Same as above, but referring to the component by module path | +/// | `MyEnum::Variant` | Enum Component `MyEnum` with the `Variant` variant | +/// | `template_value(component)` | Insert the component value from a variable `component` | +/// | `template_value(CompA::from_str("foo"))` | Insert the component value by immediately calling the constructor | +/// | `template(|context| { ... })` | Register a function/closure returning a Template (eg. Component). Its passed [`context`] allowing World access | +/// | `~MyType`
`~MyType {name: var}` | Type implementing [`Template`], the prefix is used to distinguish it from Components which use [`FromTemplate`]| +/// | **Including Scenes** | | +/// | `scene()`
`scene(val)` | Include the result of a `impl `[`Scene`] function | +/// | `{ expr }` | Include the result of `expr`, which should be a [`Scene`] | +/// | `@MySceneComp` | Include a [`SceneComponent`]. Fields, if any exist, will be default | +/// | `@MySceneComp { @prop: val }` | Include a [`SceneComponent`] with a `prop` field, passed to this components scene function | +/// | `@MySceneComp { name: val }` | Include a [`SceneComponent`] with a normal field, works the same as it does for normal components | +/// | `@MySceneComp { @prop: val1, name: val2 }` | Include a [`SceneComponent`] with both a `prop` and a field | +/// | `:"scene.bsn"` |
Asset format no yet implemented!
Include a cached scene asset file | +/// | `:scene()`
`:@MySceneComp` |
Caching for scene includes not yet implemented!
Include a cached scene function | +/// | **Named entity references** | | +/// | `#MyName` | Becomes `Name("MyName")` when used as a `part` of a scene | +/// | `CompA(#MyName)`
`scene(#MyName)` | Referring to the entity which was named `MyName` in this scope, results in an [`EntityTemplate`] being passed | +/// | `Name("Foo")` | Manually sets the Name component, can be put after a `#MyName` to use a custom name while allowing references | +/// | **Observers** | | +/// | `on(\|ev: On\| { … })` | Attaches an entity [`observer`] for the [`EntityEvent`] `Ev` to this entity. In this example, using a closure | +/// | `on(my_observer)` | Attaches an entity [`observer`] for the [`EntityEvent`] `Ev` to this entity. In this example, using a function | +/// | **Relationships** | | +/// | `Children []` | Spawns each entry as a child of this entity, see **Scene Lists** below for details | +/// | `ChildOf(entity)` | Makes **this** entity a child of `entity`, accepts a [`Entity`] or a `#Name` reference ([`EntityTemplate`]) | +/// | `MyRel []` | Like `Children`, but uses any `RelationshipTarget` component | +/// +/// [`context`]: bevy_ecs::template::TemplateContext +/// [`EntityTemplate`]: bevy_ecs::template::EntityTemplate +/// ### Scene Lists +/// +/// In `bsn_list!` and Relationships a list of scenes delimited by `[]` is allowed. +/// Unlike parts of a scene, which are whitespace-separated, the scenes in a scene list are comma-separated. +/// +/// Note: examples are part of a relationship like `Children []` or a list macro like `bsn_list![]` +/// +/// | Example | Meaning | +/// | ---------------------------- | ------------------------------------------------------------------------------------------------------------------ | +/// | `[ #Child1 CompA, #Child2 ]` | Spawns 2 children, one with `(Name("Child1"), CompA::default())` and the other with `Name("Child2")` | +/// | `[ (#Child1 CompA), (#Child2) ]` | Same as above, with explicit parentheses | +/// | `[ #First, { expr }, #Last ]` | Spawns an entity with name `First`, then every entity from the `SceneList` returned by expr, then one named `Last` | +/// | `[ #First, ({ expr }), #Last ]` | Same as above, but the `expr` should result in a `Scene` and will only spawn one entity using it | +/// +/// ### Values +/// +/// Values in BSN (as in `val`,`val1` etc used above) are generally any literal Rust values, plus a few bsn-specific quirks. +/// +/// +/// | Example | Meaning | Explanation | +/// | -------------------------------- | ------------- | +/// | `1` | unsigned int | positive number, common types: [`usize`], [`u8`], [`u32`], [`u64`] | +/// | `1` or `-1` | signed int | positive or negative number, common types: `i32`, `i64` | +/// | `1.1` or `-0.1` or `1.` or `-2.` | float | Floating point number, common types: `f32`, `f64` | +/// | `true` or `false` | bool | Boolean, type: [`bool`] | +/// | `"somename"` | string | Text, types: `String` or `&'static str` | +/// | `"mypicture.png"` | asset path | Asset, when used in a field which expects a [`Handle`] to the matching `Asset` type | +/// | `some(1)` | function call | , only works if the function returns the required type | +/// | `GREEN` | constant | fixed value, must be in scope | +/// | `std::f32::consts::PI` | constant | fixed value, uses full path so doesn't need to be in scope | +/// | **Expression syntax** | | | +/// | `{ 1 + 2 }` | expression | Any rust expression works in `{}`, in this case addition of 2 integers | +/// | `{ vec![true, false] }` | vector | An expression returning a [`Vec`], a collection of multiple items of one specific type. | +/// | `{ bsn!{ Text("foo") Style } }` | scene | Sometimes, you may need to pass a small `Scene` as a value to something else | +/// +/// ### Other Rust syntax +/// +/// If you're new to Rust, you might struggle with some of its syntax when you see it in or around BSN. +/// Here are some syntax snippets which haven't been shown so far: +/// +/// | Syntax | Meaning | Explanation | +/// | ----------------- | ------------- | --------------------------------------------------------------- | +/// | `\|param\| { … }` | Closure | A closure; effectively an unnamed function | +/// | `Vec` | Generic type | A type with a generic type parameter `T` | +/// | `//` | Comment | Line comment; all text after `//` on the same line is ignored | +/// | `/* */` | Block comment | Standard Rust block comment; all text inside `/* */` is ignored | +/// | `bsn! { … }` | Macro call | Calls a macro on the value inside of the braces | +/// +/// ### Syntax example +// Note: the actual syntax example comes from the original bevy_scene_macros::bsn docs. +// rustdoc appends these #[doc(inline)] docs before those +// rust-analyzer ignores these docs and only shows the original ones, see https://github.com/rust-lang/rust-analyzer/issues/14079 +// despite those being #[doc(hidden)] +// +pub struct Foo; // comment this in to get rust-analyzer highlighting in the above docs, probably same issue as above +#[doc(inline)] +pub use bevy_scene_macros::bsn; + +/// Creates a `SceneList` using BSN (Bevy Scene Notation) syntax. +/// +/// This is useful when you want multiple root entities in your scene +/// that do not share a common parent, or if you want to create multiple scenes at once. +/// +/// Like in [`bsn!`], commas separate entities, +/// while whitespace separates components on the same entity. +/// +/// All root entries in a [`bsn_list!`] share a single name scope, so sibling root entities +/// can cross-reference each other by `#Name`. +/// This is not possible with separate [`bsn!`] calls, and is a key motivation for using [`bsn_list!`]. +/// +/// See [`bsn!`] for more details on syntax. +/// See the `bevy_scene` crate docs for a high-level overview of the key concepts. +#[doc(inline)] +pub use bevy_scene_macros::bsn_list; +pub use bevy_scene_macros::SceneComponent; + /// Adds support for spawning Bevy Scenes. See [`Scene`], [`SceneList`], [`ScenePatch`], and the [`bsn!`] macro for more information. #[derive(Default)] pub struct ScenePlugin; @@ -853,10 +1091,12 @@ mod tests { use crate::{prelude::*, ScenePatch}; use alloc::sync::Arc; use bevy_app::{App, TaskPoolPlugin}; + use bevy_asset::io::embedded::GetAssetServer; use bevy_asset::io::memory::{Dir, MemoryAssetReader}; use bevy_asset::io::{AssetSourceBuilder, AssetSourceId}; use bevy_asset::{Asset, AssetApp, AssetLoader, AssetPlugin, AssetServer, Assets, Handle}; use bevy_ecs::lifecycle::HookContext; + use bevy_ecs::name::Name; use bevy_ecs::prelude::*; use bevy_ecs::relationship::Relationship; use bevy_ecs::system::{system_value, SystemHandle}; @@ -923,6 +1163,38 @@ mod tests { assert_eq!(name.as_str(), "Y"); } + #[test] + fn cached_patching_order() { + let mut app = test_app(); + let world = app.world_mut(); + + #[derive(Component, FromTemplate)] + struct Position { + x: f32, + y: f32, + z: f32, + } + + fn a() -> impl Scene { + bsn! { + Position { x: 2. } + } + } + + fn b() -> impl Scene { + bsn! { + Position { x: 1., y: 1., z: 1. } + a() + } + } + + let root = world.spawn_scene(b()).unwrap(); + let position = root.get::().unwrap(); + assert_eq!(position.x, 2.); + assert_eq!(position.y, 2.); + assert_eq!(position.z, 2.); + } + #[test] fn loaded_asset_cached_patching() { #[derive(Component, FromTemplate)] @@ -1239,6 +1511,30 @@ mod tests { .0 ); } + #[test] + fn bsn_reverse_reference() { + let mut app = test_app(); + let world = app.world_mut(); + + fn a() -> impl Scene { + bsn! { + Reference(#Last) + Children [ + #First, + #Second, + #Last + ] + } + } + let id = world.spawn_scene(a()).unwrap().id(); + let ref_id = world.entity(id).get::().unwrap(); + + let children = world.entity(id).get::().unwrap(); + assert_eq!(children[2], ref_id.0); + + let name = world.entity(ref_id.0).get::().unwrap(); + assert_eq!(name.as_str(), "Last"); + } #[test] fn bsn_list_name_references() { @@ -1332,6 +1628,229 @@ mod tests { let exploded = world.resource::(); assert_eq!(exploded.0, Some(id)); } + #[test] + fn primitive_literals() { + #![allow(dead_code, reason = "test")] + // test that bsn compiles and doesn't fail to spawn a scene with all sorts of literal values + macro_rules! types_fields { + (def $name:ident, $($typ:ident),*) => { + #[derive(Component, FromTemplate)] + struct $name { + $( + $typ: $typ, + )* + } + }; + ($world:ident, $name:ident($a:expr, $b:expr, $c:expr, $d:expr, $e:expr), $($typ:ident),*) => { + types_fields!($world, $name($a, $b, $c, $d), $($typ),*); + types_fields!(val $world, $e, $name, $($typ),*); + }; + ($world:ident, $name:ident($a:expr, $b:expr, $c:expr, $d:expr), $($typ:ident),*) => { + types_fields!($world, $name($a, $b, $c), $($typ),*); + types_fields!(val $world, $d, $name, $($typ),*); + }; + ($world:ident, $name:ident($a:expr, $b:expr, $c:expr), $($typ:ident),*) => { + types_fields!($world, $name($a, $b), $($typ),*); + types_fields!(val $world, $c, $name, $($typ),*); + + }; + ($world:ident, $name:ident($a:expr, $b:expr), $($typ:ident),*) => { + types_fields!($world, $name($a), $($typ),*); + types_fields!(val $world, $b, $name, $($typ),*); + }; + ($world:ident, $name:ident($a:expr), $($typ:ident),*) => { + types_fields!(def $name, $($typ),*); + types_fields!(val $world, $a, $name, $($typ),*); + }; + (val $world:ident, $a:expr, $name:ident, $($typ:ident),*) => { + let v = bsn!{ $name { + $( + $typ: $a, + )* + }}; + $world.spawn_scene(v).unwrap(); + }; + } + let mut app = test_app(); + let world = app.world_mut(); + types_fields!(world, Unsigned(0, 1), usize, u8, u16, u32, u64, u128); + types_fields!(world, Signed(-1, 0, 1), isize, i8, i16, i32, i64, i128); + types_fields!( + world, + Float(-1.0, 0.0, 1.0, core::f32::consts::PI, -1.), + f32, + f64 + ); + types_fields!(world, Bool(true, false), bool); + #[derive(Component, FromTemplate)] + struct Random { + str: &'static str, + string: String, + vec: Vec, + array: [u8; 4], + } + let scene = bsn! { + Random{ + str: "test", + string: "test", + vec: {vec![0, 1]}, + array: {[0, 1, 2, 3]} + } + }; + world.spawn_scene(scene).unwrap(); + } + #[test] + fn children_list_expr() { + fn container(items: impl SceneList) -> impl Scene { + bsn! { + #Root + Children [ + #First, + {items}, + #Last + ] + } + } + let mut app = test_app(); + let world = app.world_mut(); + let items = bsn_list![ + #Second, + #Third + ]; + let id = world.spawn_scene(container(items)).unwrap().id(); + let children = world.entity(id).get::().unwrap(); + let names: Vec<_> = children + .iter() + .map(|id| world.entity(id).get::().unwrap().as_str()) + .collect(); + assert_eq!(&names, &["First", "Second", "Third", "Last"]); + } + #[test] + fn children_single_expr() { + fn container(item: impl Scene) -> impl Scene { + bsn! { + #Root + Children [ + #First, + ({item}), + #Last + ] + } + } + let mut app = test_app(); + let world = app.world_mut(); + let items = bsn![ + #Second + ]; + let id = world.spawn_scene(container(items)).unwrap().id(); + let children = world.entity(id).get::().unwrap(); + let names: Vec<_> = children + .iter() + .map(|id| world.entity(id).get::().unwrap().as_str()) + .collect(); + assert_eq!(&names, &["First", "Second", "Last"]); + } + #[test] + fn conditional_scene() { + #[derive(Component, Clone, Default)] + struct Grunt; + #[derive(Component, Clone, Default)] + struct Boss; + #[derive(Component, Clone, Default, PartialEq, Eq)] + struct Level(u32); + + fn unit(is_boss: bool, level: u32) -> impl Scene { + let scene: Box = if is_boss { + Box::new(bsn! { + Boss + Children [ unit(false, level - 1) #Grunt1, unit(false, level - 1) #Grunt2] + }) + } else { + Box::new(bsn! { Grunt }) + }; + bsn! { + Level(level) + {scene} + } + } + let mut app = test_app(); + let world = app.world_mut(); + + let id = world.spawn_scene(unit(true, 10)).unwrap().id(); + let children = world.entity(id).get::().unwrap(); + let names: Vec<_> = children + .iter() + .map(|id| world.entity(id).get::().unwrap().as_str()) + .collect(); + assert_eq!(&names, &["Grunt1", "Grunt2"]); + let names: Vec<_> = children + .iter() + .map(|id| world.entity(id).get::().unwrap().0) + .collect(); + assert_eq!(&names, &[9, 9]); + } + #[test] + fn partial_tuple_struct() { + // Tests that only part of a tuple struct can be patched, + // since its different to named fields + let mut app = test_app(); + let world = app.world_mut(); + #[derive(Component, Default, Clone)] + struct TupleStruct(f32, u32); + + fn a() -> impl Scene { + bsn! { + TupleStruct(0.1) + } + } + let id = world.spawn_scene(a()).unwrap().id(); + let root = world.entity(id); + + let foo = root.get::().unwrap(); + assert_eq!(foo.0, 0.1); + assert_eq!(foo.1, 0); + } + + #[test] + fn scene_expression_passing_pointless() { + // This test exists mostly to ensure that the practice of not passing `impl Scene` into a scene + // is the same as the preferred option, using patching. + #[derive(Component, Default, Clone)] + struct Health { + current: u8, + max: u8, + } + #[derive(Component, Default, Clone)] + struct Armor(u8); + + fn unit_with_armor(unit_base: impl Scene) -> impl Scene { + bsn! { + {unit_base} + Armor(50) + } + } + fn armor() -> impl Scene { + bsn! { + Armor(50) + } + } + let mut app = test_app(); + let world = app.world_mut(); + let inner = bsn! { Health { current: 100, max: 100 } }; + let ida = world.spawn_scene(unit_with_armor(inner)).unwrap().id(); + + // inheritance is the same! + let entity_b = bsn! { + armor() + Health { current: 100, max: 100 } + }; + let idb = world.spawn_scene(entity_b).unwrap().id(); + + assert_eq!( + world.entity(ida).archetype().components(), + world.entity(idb).archetype().components() + ); + } #[test] fn enum_patching() { @@ -1668,6 +2187,19 @@ mod tests { world.trigger(Heal(id)); assert_eq!(world.resource::().0, 1); world.resource_mut::().0 = 0; + fn on_heal(_: On, mut healed: ResMut) { + healed.0 += 2; + } + fn function_scene() -> impl Scene { + bsn! { + on(on_heal) + } + } + + let id = world.spawn_scene(function_scene()).unwrap().id(); + world.trigger(Heal(id)); + assert_eq!(world.resource::().0, 2); + world.resource_mut::().0 = 0; fn move_scene(bonus: u32) -> impl Scene { bsn! { @@ -1854,6 +2386,19 @@ mod tests { .unwrap(); assert_eq!(entity.get::().unwrap().value, 10); assert_eq!(entity.get::().unwrap().len(), 2); + + fn const_val() -> impl Scene { + bsn! { + @Widget { + @children: N + } + } + } + #[derive(SceneComponent, Clone, Default)] + #[scene(const_val::<5>)] + struct SpecificWidget; + let entity = world.spawn_scene(bsn! { @SpecificWidget }).unwrap(); + assert_eq!(entity.get::().unwrap().len(), 5); } #[test] @@ -2151,6 +2696,103 @@ mod tests { func(); } + #[test] + fn macro_doc_test() { + #![allow(unused, reason = "test")] + #![allow(dead_code, reason = "test")] + + fn some_scene() -> impl Scene {} + #[derive(Component, Default, Clone)] + struct ComponentA; + #[derive(Component, Default, Clone)] + struct ComponentB(f32, u8); + #[derive(Component, Default, Clone)] + struct Node { + height: f32, + width: f32, + } + fn px(v: f32) -> f32 { + v + } + #[derive(EntityEvent)] + struct MyEntityEvent { + entity: Entity, + value: f32, + } + fn other_scene() -> impl Scene {} + #[derive(Component, FromTemplate)] + struct Link(Entity); + #[derive(SceneComponent, FromTemplate)] + #[scene(scenecomponentscene(Props))] + struct MySceneComponent { + normal_field: u8, + } + #[derive(Default)] + struct Props { + some_prop: u8, + } + fn scenecomponentscene(props: Props) -> impl Scene {} + let some_var: f32 = 0.; + #[derive(SceneComponent, FromTemplate)] + #[scene(scenecomponentscene2(Props2))] + struct Container; + struct Props2 { + items: Box, + } + impl Default for Props2 { + fn default() -> Self { + Self { + items: Box::new(bsn_list!()), + } + } + } + fn scenecomponentscene2(props: Props2) -> impl Scene {} + #[derive(Component, Default, Clone)] + struct SomeComponent; + // Copy of the macro from bevy_scene/macros/src/lib.rs + // why? because it should be tested + // why not doctests? because the macro can't depend on this crate + // why not include! it here and include_str! it in the docs? because rust-analyzer ignores #[doc = include_str!()] and this is mostly a showcase for rust-analyzer + let scene = bsn! { + some_scene() // include a scene function + #SomeName // entity name, will insert Name("SomeName") + ComponentA // component without a value will use default + ComponentB(0.0) // passing a value, other fields will use default + Node { + height: px(0.1) // same with named fields, unmentioned ones stay default + } + on(|evt: On, mut query: Query<&mut ComponentB>| { // add an observer + let mut b = query.get_mut(evt.entity).unwrap(); + b.0 += evt.value; + }) + Children [ // spawning multiple related entities using a RelationshipTarget component + #Child1 ComponentA // whitespace doesn't have to be newlines + , // entities are comma-separated + (other_scene() #Child3), // parentheses around a single entity are optional + Link(#SomeName), // passing a entity reference to a component as `Entity`, component has to implement FromTemplate + @MySceneComponent { // components which derive SceneComponent have scenes and can be inherited from + @some_prop: 3, // props, look like fields prefixed with @ but end up passed to the components scene as arguments + normal_field: 5 // while normal fields are the actual fields of the component + }, + Node { + width: some_var // you can directly use variables without {} + } + ComponentB({some_var + 3.}) // values can be expressions, when wrapped in {} + @Container { + @items: { + bsn_list![ // sometimes you may need to nest macro calls + #item1 SomeComponent, // note: the name #item1 here is in its own scope + some_scene() #item2 + ] + } + } + ] + }; + // just checking it spawns correctly + let mut app = test_app(); + let world = app.world_mut(); + let entity = world.spawn_scene(scene).unwrap().id(); + } #[test] fn scene_with_oneshot_system() { diff --git a/docs-rs/trait-tags.html b/docs-rs/trait-tags.html index 07d09ae2e57f7..c271b057d957c 100644 --- a/docs-rs/trait-tags.html +++ b/docs-rs/trait-tags.html @@ -18,7 +18,13 @@ 'SystemSet', 'SystemParam', 'Relationship', - 'RelationshipTarget' + 'RelationshipTarget', + 'Scene', + 'SceneList', + 'Template', + 'FromTemplate', + 'SceneComponent' + ]; // Find all traits that are implemented by the current type. @@ -29,6 +35,16 @@ implementedBevyTraits.delete("Component"); } + // SceneComponent already implies component + if (implementedBevyTraits.has("SceneComponent")) { + implementedBevyTraits.delete("Component"); + } + + // FromTemplate already implies Template + if (implementedBevyTraits.has("FromTemplate")) { + implementedBevyTraits.delete("Template"); + } + // Check if an implemented trait has a `type Mutability = Immutable` associated type. // This is used to determine if a `Component` is immutable or not. // TODO: Ideally we should just check the associated types of the `Component` trait, @@ -186,4 +202,23 @@ .relationshiptarget-tag { --tag-color: oklch(50% 27% 150); } + + .relationship-tag, + .relationshiptarget-tag { + --tag-color: oklch(50% 27% 150); + } + + .scene-tag, + .scenelist-tag { + --tag-color: oklch(50% 30% 300); + } + + .template-tag, + .fromtemplate-tag { + --tag-color: oklch(50% 25% 58); + } + + .scenecomponent-tag { + --tag-color: oklch(50% 27% 20); + } \ No newline at end of file