Book a Demo!
CoCalc Logo Icon
StoreFeaturesDocsShareSupportNewsAboutPoliciesSign UpSign In
bevyengine
GitHub Repository: bevyengine/bevy
Path: blob/main/crates/bevy_scene/macros/src/lib.rs
30636 views
1
mod bsn;
2
mod scene_component;
3
4
use proc_macro::TokenStream;
5
use syn::{parse_macro_input, DeriveInput};
6
7
/// Creates a `Scene` using BSN (Bevy Scene Notation) syntax.
8
///
9
/// BSN is a concise DSL for defining Bevy scenes as hierarchical collections
10
/// of entities and components.
11
/// While BSN's syntax largely follows Rust, it has quite a few features.
12
/// Don't feel like you need to master all of it before you begin.
13
/// Start simple and check back on the documentation as you run into problems.
14
///
15
/// Trying to decipher a strange combination of glyphs? Jump to the **Syntax Reference** section below.
16
///
17
/// ## Basic usage
18
///
19
/// Let's begin by spawning a single named entity with a couple of components:
20
///
21
/// ```rust, ignore
22
/// #[derive(Component, Default, Clone)]
23
/// struct Score(u32);
24
///
25
/// #[derive(Component, FromTemplate)]
26
/// struct Health { current: u32, max: u32 }
27
///
28
/// // Spawns a single entity named "Player" with a Score and Health component.
29
/// world.spawn_scene(bsn! {
30
/// #Player
31
/// Score(0)
32
/// Health { current: 100, max: 100 }
33
/// });
34
/// ```
35
///
36
/// Each `bsn!` block describes a single root entity.
37
///
38
/// ## Children and relationships
39
///
40
/// You can add child entities with the `Children` component.
41
/// To begin a new child entity, separate it from the previous one with a comma.
42
/// Separate components on the same entity with whitespace.
43
///
44
/// ```rust, ignore
45
/// // ONE child entity with both the Head and Body components:
46
/// bsn! {
47
/// #Player
48
/// Life
49
/// Children [Head Body]
50
/// }
51
///
52
/// // TWO child entities — one with Head, one with Body:
53
/// bsn! {
54
/// #Player
55
/// Life
56
/// Children [Head, Body]
57
/// }
58
/// ```
59
///
60
/// **Warning:** Separating items with whitespace places them on the **same** entity;
61
/// separating them with commas creates **separate** entities.
62
/// This is a common source of problems for new BSN users,
63
/// so be sure to check your commas if an entity seems to be missing!
64
///
65
/// Child entities can themselves have components and children, so scenes nest arbitrarily deep:
66
///
67
/// ```rust, ignore
68
/// bsn! {
69
/// #Town
70
/// Children [
71
/// // Parentheses and indentation help clarify the structure of nested scenes,
72
/// // but are optional — this scene would be the same without them.
73
/// (
74
/// #Tavern
75
/// Children [
76
/// #Innkeeper,
77
/// #Barkeep,
78
/// ]
79
/// ),
80
/// (
81
/// #Blacksmith
82
/// Children [
83
/// #Anvil,
84
/// ]
85
/// ),
86
/// ]
87
/// }
88
/// ```
89
///
90
/// Swap `Children` for any `RelationshipTarget` component to define custom relationships.
91
///
92
/// To attach entities to an *existing* entity not created by this scene, use the `ChildOf` component directly.
93
/// The following example uses [`bsn_list!`] to spawn two entities both attached to a pre-existing root:
94
///
95
/// ```rust, ignore
96
/// let root: Entity = /* some pre-existing entity */;
97
/// bsn_list! {
98
/// ( #Child1 ChildOf(root) ),
99
/// ( #Child2 ChildOf(#Child1) ),
100
/// }
101
/// ```
102
///
103
/// `ChildOf` accepts either a plain `Entity` value or a named `#Name` reference from the same scope.
104
///
105
/// ## Named entity references
106
///
107
/// The `#Name` syntax does two things at once: it adds a `Name("Name")` component to the entity
108
/// and registers it so that other entities in the same scene scope can refer to it by name.
109
/// To use a named entity as a value — for example, as a component field that holds an `Entity` id —
110
/// write `#Name` in the value position:
111
///
112
/// ```rust, ignore
113
/// #[derive(Component, FromTemplate)]
114
/// struct Link(Entity);
115
///
116
/// // Spawn a parent named "Hub" with two children that each hold a back-reference to it.
117
/// bsn! {
118
/// #Hub
119
/// Children [
120
/// Link(#Hub),
121
/// Link(#Hub),
122
/// ]
123
/// }
124
/// ```
125
///
126
/// References can point in any direction within the scene: a parent can reference a descendant,
127
/// a child can reference a parent, and siblings within the same `Children [...]` block can
128
/// reference each other.
129
/// All names in a single `bsn!` call share one scope; names from composed or cached scenes
130
/// (`my_scene()`, `:my_scene`) live in their own separate scopes and are not visible here.
131
/// If two scopes both define the same name (e.g. both use `#Player`), each `#Player` resolves
132
/// to its own entity — there is no conflict or shadowing.
133
///
134
/// For dynamic names computed at runtime, use `#{expr}`:
135
///
136
/// ```rust, ignore
137
/// fn reference_named_entity(name: &str) -> impl Scene {
138
/// bsn! { #{name} }
139
/// }
140
/// ```
141
///
142
/// In [`bsn_list!`], all root entries share one scope, so sibling root entities can
143
/// cross-reference each other — see the `bevy_scene` crate docs for more details.
144
///
145
/// ## Defaults and patching
146
///
147
/// BSN supports *patching*: writing `Health { current: 100 }` creates a patch that sets only
148
/// `current`. Unmentioned fields keep their values from earlier patches or the type's defaults,
149
/// and multiple patches to the same component merge rather than overwrite. This works for both
150
/// `Clone + Default` and `FromTemplate` types.
151
///
152
/// The difference between the two is about what values a field can hold at spawn time:
153
///
154
/// - **`Clone + Default` types** (e.g. `#[derive(Component, Default, Clone)]`): the simple
155
/// case. This just works in BSN with no extra derives. All field values must be plain Rust values — the
156
/// template cannot fill them in correctly based on world or context state.
157
///
158
/// - **`FromTemplate` types** (e.g. `#[derive(Component, FromTemplate)]`): needed when a field
159
/// requires spawn-time context. Use this when a field's type itself implements `FromTemplate`
160
/// — for example, `Handle<T>` fields that resolve asset path strings, or `Entity` fields that
161
/// reference named entities in the scene.
162
///
163
/// Because each approach generates a different `Template` implementation, `Clone + Default` and
164
/// `FromTemplate` cannot both be derived on the same type. This would create incoherent trait implementations!
165
/// Use `Clone + Default` by default, and switch to `FromTemplate` only when you need the extra flexibility it provides.
166
///
167
/// ## Expressions and dynamic values
168
///
169
/// BSN supports embedding arbitrary Rust expressions anywhere a value is expected,
170
/// using `{...}` (curly braces):
171
///
172
/// ```rust, ignore
173
/// fn enemy(name: &str, hp: u32) -> impl Scene {
174
/// let sprite_path = name.to_string() + ".png";
175
/// bsn! {
176
/// #{name}
177
/// Health { current: {hp}, max: {hp} }
178
/// Sprite { image: {sprite_path} }
179
/// }
180
/// }
181
/// ```
182
///
183
/// A `{...}` block can also hold an expression that implements `Scene` or, inside a
184
/// `Children [...]` block, one that implements `SceneList`:
185
///
186
/// ```rust, ignore
187
/// fn unit_with_armor(unit_base: impl Scene) -> impl Scene {
188
/// bsn! {
189
/// {unit_base}
190
/// Armor(50)
191
/// }
192
/// }
193
/// ```
194
///
195
/// `{...}` blocks can also be `unsafe` or `const` (but not both at once without nesting):
196
///
197
/// ```rust, ignore
198
/// fn friendly(people: &[&'static str]) -> impl Scene {
199
/// bsn! {
200
/// Friend {
201
/// name: const {"John"}
202
/// /// SAFETY: Jesus take the wheel
203
/// father: unsafe {people.get_unchecked(0)}
204
/// }
205
/// }
206
/// }
207
/// ```
208
///
209
/// **Note:** `.bsn` asset files will not support arbitrary Rust expressions,
210
/// as we do not intend to require Bevy games to ship a Rust compiler.
211
///
212
/// ## Automatic type conversion
213
///
214
/// BSN performs some automatic type conversion for you,
215
/// reducing boilerplate when creating scenes.
216
///
217
/// The `bsn!` macro appends `.into()` to expressions (`{...}`), bare identifiers, closures, and string
218
/// literals when they appear as field values. This means any field assignment that would be
219
/// valid via Rust's standard [`From`]/[`Into`] traits works transparently in BSN — no explicit
220
/// cast needed:
221
///
222
/// ```rust, ignore
223
/// #[derive(Component, Default, Clone)]
224
/// struct Label(String);
225
///
226
/// let greeting: &'static str = "Hello";
227
/// bsn! {
228
/// // &str → String via Into, no .to_string() required
229
/// Label(greeting)
230
/// }
231
/// ```
232
///
233
/// A related, more advanced trick is what makes string literals work as asset paths for `Handle<T>` fields.
234
/// See the docs on `HandleTemplate` for more information!
235
///
236
/// Note: non-string literals are used as-is. Only string literals get `.into()` appended;
237
/// all other literals (integers, floats, booleans) are emitted directly,
238
/// so `Health { current: 100 }` assigns `100` without any conversion.
239
/// If the types don't match (e.g. you forgot to append a decimal point to a float),
240
/// you'll get a normal Rust type error.
241
///
242
/// ## Asset loading
243
///
244
/// When a component field is a `Handle<T>`, BSN accepts a string literal in its place.
245
/// The string is resolved to an asset handle at spawn time via the asset server, reusing
246
/// an existing handle if the asset is already loaded:
247
///
248
/// ```rust, ignore
249
/// commands.spawn_scene(bsn! {
250
/// Sprite { image: "player.png" }
251
/// });
252
/// ```
253
///
254
/// This works for your own `FromTemplate`-derived components too — any `Handle<T>` field
255
/// automatically accepts asset path strings:
256
///
257
/// ```rust, ignore
258
/// #[derive(Component, FromTemplate)]
259
/// struct Icon {
260
/// image: Handle<Image>,
261
/// tint: Color,
262
/// }
263
///
264
/// commands.spawn_scene(bsn! {
265
/// Icon { image: "icon.png", tint: Color::WHITE }
266
/// });
267
/// ```
268
///
269
/// ## Observers
270
///
271
/// Use `on` inside `bsn!` to attach an entity observer — a closure that fires when a given
272
/// `EntityEvent` targets the entity. The first parameter's type determines which event is
273
/// observed. Multiple observers can be stacked on the same entity, and the closure has full
274
/// access to the ECS via system parameters:
275
///
276
/// ```rust, ignore
277
/// #[derive(EntityEvent)]
278
/// struct Damage(u32);
279
///
280
/// fn player() -> impl Scene {
281
/// bsn! {
282
/// Health { max: 100, current: 100 }
283
/// on(|ev: On<Damage>, mut query: Query<&mut Health>| {
284
/// let mut health = query.get_mut(ev.target()).unwrap();
285
/// health.current = health.current.saturating_sub(ev.0);
286
/// })
287
/// }
288
/// }
289
///
290
/// // `move` closures work too, capturing variables from the enclosing scope:
291
/// fn enemy(bonus_damage: u32) -> impl Scene {
292
/// bsn! {
293
/// on(move |ev: On<Damage>, mut query: Query<&mut Health>| {
294
/// let mut health = query.get_mut(ev.target()).unwrap();
295
/// health.current = health.current.saturating_sub(ev.0 + bonus_damage);
296
/// })
297
/// }
298
/// }
299
/// ```
300
///
301
/// ## Scene composition and inheritance
302
///
303
/// There are two ways to build on an existing scene: **inline composition** and **inheritance**.
304
///
305
/// **Inline composition** calls a scene function directly inside `bsn!`. The parent's unresolved
306
/// templates are merged with the child's and everything resolves together in one pass:
307
///
308
/// ```rust, ignore
309
/// fn base_enemy() -> impl Scene {
310
/// bsn! {
311
/// Health { current: 100, max: 100 }
312
/// Power(10)
313
/// }
314
/// }
315
///
316
/// // Compose base_enemy() and patch just the fields that differ.
317
/// fn boss() -> impl Scene {
318
/// bsn! {
319
/// base_enemy()
320
/// Health { max: 500 }
321
/// Power(50)
322
/// }
323
/// }
324
/// ```
325
///
326
/// **Inheritance** uses the `:` prefix. The parent is *pre-resolved* first — its templates are
327
/// fully flattened into a `ResolvedScene` — and the child's patches are applied on top.
328
/// When the scene is parameterless, this will "cache" the scene and share it across all inheriting scenes.
329
/// For larger scenes that are cached and spawned many times, this can be much faster than re-computing
330
/// the scene each time.
331
///
332
/// ```rust, ignore
333
/// fn boss() -> impl Scene {
334
/// bsn! {
335
/// :base_enemy
336
/// Health { max: 500 }
337
/// Power(50)
338
/// }
339
/// }
340
///
341
/// // Asset inheritance (.bsn format not yet released, sorry!):
342
/// bsn! {
343
/// :"enemy.bsn"
344
/// Health { max: 500 }
345
/// }
346
/// ```
347
///
348
/// | | Function inheritance `:my_scene` | Asset inheritance `:"my_scene.bsn"` | Inline composition `my_scene()` |
349
/// |---------------------------|-------------------------------------|-------------------------------------|----------------------------------|
350
/// | Accepts parameters | Yes | No | Yes |
351
/// | Asset-based | No | Yes | No |
352
/// | Cached resolution | Parameterless scenes only | Yes | No |
353
///
354
/// Prefer scene inheritance over inline composition in general: the expensive scene resolution is cached, saving work during reuse.
355
/// Inline composition should be reserved for parameterized scenes that vary based on a given input,
356
/// small scenes that are shared across contexts (like styles),
357
/// or one-off scenes that do not require reuse.
358
///
359
/// /// ## Formatting BSN
360
///
361
/// Whitespace, parentheses, and comments have no effect on the generated scene —
362
/// they exist purely to help you organize and read your code.
363
///
364
/// **Whitespace** (spaces, newlines, tabs) separates items on the *same* entity.
365
/// Use it freely for alignment and to make groupings of both components and entities clearer.
366
///
367
/// **Parentheses** group a set of items into one logical unit inside a `[...]` list.
368
/// Trailing commas are the delimiter used to end one entity and start another,
369
/// but these can be hard to spot in deeply nested or one-line scenes.
370
/// Parentheses (often with associated indentation) make the boundary explicit:
371
///
372
/// ```rust, ignore
373
/// bsn! {
374
/// Children [
375
/// // Hard to see where one entity ends and the next begins:
376
/// ComponentA
377
/// ComponentB,
378
/// ComponentA
379
/// ComponentC,
380
///
381
/// // Much clearer:
382
/// (
383
/// ComponentA
384
/// ComponentB
385
/// ),
386
/// (
387
/// ComponentA
388
/// ComponentC
389
/// ),
390
/// ]
391
/// }
392
/// ```
393
///
394
/// ```rust, ignore
395
/// // Without parentheses, one-line definitions of children are prone to subtle mistakes:
396
/// bsn! { Children [ComponentA ComponentB, ComponentC ComponentD] }
397
///
398
/// // With parentheses, the structure is clear:
399
/// bsn! { Children [ (ComponentA ComponentB), (ComponentC ComponentD) ] }
400
/// ```
401
///
402
/// **Comments** (`//` line comments and `/* */` block comments) work exactly as in
403
/// normal Rust and are stripped by the macro before parsing.
404
///
405
/// ## Syntax Reference
406
///
407
/// ### Components on the same entity vs. separate entities
408
///
409
/// | Syntax | Meaning | Explanation |
410
/// |--------|---------|-------------|
411
/// | `CompA CompB CompC` | Same entity | **Whitespace** between items — all go on the **same** entity |
412
/// | `A, B, C` | Separate entities | **Commas** inside `[…]` — each becomes its **own** entity |
413
/// | `(CompA CompB)` | Entity group | Parentheses group components for readability; equivalent to whitespace alone |
414
///
415
/// ### Naming entities
416
///
417
/// | Syntax | Meaning | Explanation |
418
/// |--------|---------|-------------|
419
/// | `#Name` | Named entity | Adds a `Name("Name")` component and registers the entity for cross-referencing within this scope |
420
/// | `#{ expr }` | Dynamic name | Names an entity using the result of a Rust expression |
421
///
422
/// ### Relationships
423
///
424
/// | Syntax | Meaning | Explanation |
425
/// |--------|---------|-------------|
426
/// | `Children [s1, s2]` | Add children | Spawns each entry as a child **of this** entity |
427
/// | `ChildOf(entity)` | Set parent | Makes **this** entity a child of `entity`; accepts a plain `Entity` or a `#Name` reference |
428
/// | `MyRel [s1, s2]` | Custom relationship | Like `Children`, but uses any `RelationshipTarget` component |
429
///
430
/// ### Dynamic values
431
///
432
/// | Syntax | Meaning | Explanation |
433
/// |--------|---------|-------------|
434
/// | `{ expr }` | Rust expression | Evaluated at spawn time; may be a component, `impl Scene`, or (inside `[…]`) `impl SceneList` |
435
/// | `field: { expr }` | Expression in field | Embeds a Rust expression as the value of a named field |
436
///
437
/// ### Composition and inheritance
438
///
439
/// | Syntax | Meaning | Explanation |
440
/// |--------|---------|-------------|
441
/// | `my_scene()` | Inline composition | Merges `my_scene`'s unresolved templates into this entity |
442
/// | `:my_scene` | Inheritance | Pre-resolves `my_scene` first, then patches on top |
443
/// | `:"path.bsn"` | Asset inheritance | Inherits from a `.bsn` asset file; requires `queue_spawn_scene` |
444
///
445
/// ### Observers
446
///
447
/// | Syntax | Meaning | Explanation |
448
/// |--------|---------|-------------|
449
/// | `on(\|ev: On<Ev>\| { … })` | Observer | Attaches an entity observer that fires when `Ev` targets this entity |
450
///
451
/// ### Other Rust syntax
452
///
453
/// If you're new to Rust, you might struggle with some of its syntax when you see it in BSN.
454
/// Here are the most important syntax patterns to be aware of:
455
///
456
/// | Syntax | Meaning | Explanation |
457
/// |--------|---------|-------------|
458
/// | `MyComponent` | Unit or defaulted struct | A struct with no fields, or in BSN only, with all fields at their defaults |
459
/// | `MyComponent { field: val, field2: val2 }` | Struct with named fields | Sets named fields; unmentioned fields keep their defaults or values from prior patches |
460
/// | `MyComponent(val1, val2)` | Tuple struct | Constructs a tuple-struct component |
461
/// | `MyEnum::Variant` | Enum variant | A value of the `MyEnum` type with the `Variant` variant |
462
/// | `module::MyComponent` | Path | A module path to a struct type |
463
/// | `GREEN`` | Constant | A constant value |
464
/// | `f32::PI` | Associated constant | A constant (here, `PI`) associated with a type (here, `f32`) |
465
/// | `\|param\| { … }` | Closure | A closure; effectively an unnamed function |
466
/// | `Vec<T>` | Generic type | A type with a generic type parameter `T` |
467
/// | `spawn_enemy("orc", 10, true)` | Function call | Calls a function with the given arguments by position |
468
/// | `spawn_player()` | Argumentless function call | Calls a function that takes no arguments |
469
/// | `//` | Comment | Line comment; all text after `//` on the same line is ignored |
470
/// | `/* */` | Block comment | Standard Rust block comment; all text inside `/* */` is ignored |
471
/// | `bsn! { … }` | Macro call | Calls a macro on the value inside of the braces |
472
///
473
/// ## Further reading
474
///
475
/// See [`bsn_list!`] if you want to create multiple scenes at once,
476
/// or want to have multiple root entities.
477
///
478
/// See the `bevy_scene` crate docs for a high-level overview of the key concepts.
479
#[proc_macro]
480
pub fn bsn(input: TokenStream) -> TokenStream {
481
crate::bsn::bsn(input)
482
}
483
484
/// Creates a `SceneList` using BSN (Bevy Scene Notation) syntax.
485
///
486
/// This is useful when you want multiple root entities in your scene
487
/// that do not share a common parent, or if you want to create multiple scenes at once.
488
///
489
/// Like in [`bsn!`], commas separate entities,
490
/// while whitespace separates components on the same entity.
491
///
492
/// All root entries in a [`bsn_list!`] share a single name scope, so sibling root entities
493
/// can cross-reference each other by `#Name`.
494
/// This is not possible with separate [`bsn!`] calls, and is a key motivation for using [`bsn_list!`].
495
///
496
/// See [`bsn!`] for more details on syntax.
497
/// See the `bevy_scene` crate docs for a high-level overview of the key concepts.
498
#[proc_macro]
499
pub fn bsn_list(input: TokenStream) -> TokenStream {
500
crate::bsn::bsn_list(input)
501
}
502
503
#[proc_macro_derive(
504
SceneComponent,
505
attributes(component, require, relationship, relationship_target, entities, scene)
506
)]
507
pub fn derive_scene_component(input: TokenStream) -> TokenStream {
508
let mut ast = parse_macro_input!(input as DeriveInput);
509
TokenStream::from(scene_component::derive_scene_component(&mut ast))
510
}
511
512