mirror of
https://github.com/bevyengine/bevy.git
synced 2026-07-02 00:33:03 -04:00
create-pull-request/patch
6 Commits
| Author | SHA1 | Message | Date | |
|---|---|---|---|---|
|
|
5897ed605b |
BSN: changing prefixes for Template and SceneComponent to allow removing confusing "inheritance" concept (#24367)
# Objective Changes discussed with cart: - `:` will not be called "inheritance" anymore, but instead "cacheable" (name not final) - the Template patching prefix changes `@` -> `~` - the SceneComponent prefix changes `:` -> `@` - the `:` prefix is allowed in front of `@SceneComponent` as well, marking it as being cached >> me: do we forbid caching of functions which take an argument? > cart: We should forbid it until we support it, and I don't plan on supporting this in the short term, as it adds new controversial design aspects to the system (building a mapping from parameters to cached scenes, ensuring the parameters are all hashable, etc). Link to the discord thread, specifically my [recap message](https://discord.com/channels/691052431525675048/1264881140007702558/1506422311056576702). The exact reasoning is in the linked discord discussion, but the tldr is: "Inheritance" as a term for the ":" syntax didn't really fit what bsn was doing, since scenes without `:` produce the same result. All `:` is, is a way to say "cache this pls" with the design of bsn imposing the limitation that only the first scene can be cached That was all fine, except for one case: Disambiguating SceneComponents from normal components. So if we redefine `:` to just mean "caching" we needed to untangle it from SceneComponents. That means disambiguating SceneComponents needed a different syntax, and the decision ended up being to take the @ prefix from the *much* less common `@Template` usecase, since `SceneComponent {@prop: val}` already used @ as well and introduce a new prefix to replace `@Template` instead, replacing it with `~Template`. ## Solution Implement these changes in macros. Notably, this *still* doesn't implement/enable caching right now. Most docs are updated, except for the big blocks in `bevy_scene/src/lib.rs` and `bevy_scene/macros/src/lib.rs` since i've been working on reworking those on another branch and didn't want to duplicate whats already been a lot of work and likely to be a bunch more ## Testing - [x] `bevy_scene` tests passing - [x] `cargo check --workspace` and `cargo check --workspace --examples` passing Since this PR does not yet enable caching, I think passing the current tests should be enough. --------- Co-authored-by: Carter Anderson <mcanders1@gmail.com> Co-authored-by: François Mockers <francois.mockers@vleue.com> |
||
|
|
aadbf46184 |
Scene Components (#24008)
# Objective
It is essentially a Bevy rite of passage to define a `Player` component
and then ask "ok when I spawn `Player`, how do I spawn all of the other
entities / components / scenes it needs to function with it?".
In Bevy, components are the "unit of behavior". The presence of a
component indicates that the entity should behave a certain way. Scenes
are _inextricable_ from behaviors, yet currently there is no way to tie
Components (bevy's unit of behavior) to scenes. _This is a fundamental
gap in the Bevy data model_. It is a problem that developers expect
their engine to solve for them.
It is high time that we had an official solution to this problem. BSN
was built to be that solution, and it is now time to do the last-mile
work to facilitate the `Component <-> Scene` relationship
## Solution
It is now possible to define "scene components", where `SceneComponent:
Component`:
```rust
#[derive(SceneComponent, Default, Clone)]
struct Player {
score: usize
}
impl Player {
fn scene() -> impl Scene {
bsn! {
Transform { translation: Vec3 { x: 10. } }
Children [
LeftHand,
RightHand,
]
}
}
}
```
This enables inheriting from the Player component as a scene:
```rust
world.spawn_scene(bsn! {
:Player { score: 0 }
})
```
After the inherited `Player` scene component is spawned, the _entire_
scene is available. This means that you can `Query<&Player>` in your ECS
systems, and _trust_ that the entire scene is also present.
### 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 inherited somewhere "earlier" in the inheritance hierarchy.
Attempting to spawn a scene component on its own _without_ the scene
(ex: `commands.spawn(Player::default())`) will log an error, as Scene
Components should never exist without their scene.
### Custom Scene Functions
When deriving `SceneComponent`, it defaults to using `Self::scene` as
the "scene function". Scene functions can also be manually specified:
```rust
#[derive(SceneComponent, Default, Clone)]
#[scene(player)]
struct Player {
score: usize
}
fn player() -> impl Scene {
bsn! { /* scene here */}
}
```
### Inherit BSN Asset Paths
Alternatively, a scene asset path can be specified:
```rust
#[derive(SceneComponent, Default, Clone)]
#[scene("player.bsn")]
struct Player {
score: usize
}
```
Once we port the glTF loader to BSN, this will also be supported:
```rust
#[derive(SceneComponent, Default, Clone)]
#[scene("player.gltf")]
struct Player {
score: usize
}
```
### Scene Props
Sometimes it is desirable to "parameterize" a scene: pass in values to
the scene which determine what the scene outputs are.
The answer to this in BSN is "scene props":
```rust
/// A UI widget that repeats "hello" text a given number of times.
#[derive(SceneComponent, Default, Clone)]
#[scene(HelloRepeaterProps)]
struct HelloRepeater;
#[derive(Default)]
struct HelloRepeaterProps {
repeat: usize,
}
impl HelloRepeater {
fn scene(props: HelloRepeaterProps) -> impl Scene {
let hellos = (0..props.repeat)
.map(|_| bsn!{ Text("hello") })
.collect::<Vec<_>>();
bsn! {
Node
Children [
{hellos}
]
}
}
}
world.spawn_scene(bsn! {
:HelloRepeater {
@repeat: 5
}
});
```
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".
You can set _both_ props and normal fields at the same time:
```rust
#[derive(SceneComponent, Default, Clone)]
#[scene(WidgetProps)]
struct Widget {
value: usize
}
#[derive(Default)]
struct WidgetProps {
border: bool,
}
world.spawn_scene(bsn! {
:Widget {
@border: true,
value: 10,
}
});
```
Scene props can be used with templates:
```rust
#[derive(SceneComponent, Default, Clone)]
#[scene(PlayerProps)]
struct Player;
#[derive(Default)]
struct PlayerProps {
head: AssetPath<'static>,
body: AssetPath<'static>,
}
impl Player {
fn scene(props: PlayerProps) -> impl Scene {
bsn! {
Transform
Visibility
Children [
(Head, Sprite { image: {props.head} }),
(Body, Sprite { image: {props.body} }),
]
}
}
}
world.spawn_scene(bsn! {
:Player {
@head: "big_head.png"
@body: "small_body.png"
}
});
```
### Scene Components are Template-able
```rust
#[derive(SceneComponent, FromTemplate)]
struct Player {
image: Handle<Image>,
}
impl Player {
fn scene() -> impl Scene {
bsn! { /* scene here */}
}
}
world.spawn_scene(bsn! {
:Player { image: "player.png" }
});
```
### The Scene Component is Always Added
Specifying the scene component manually in the scene function is not
necessary. It will be added automatically:
```rust
#[derive(SceneComponent, Default, Clone)]
struct Player {
score: usize
}
impl Player {
fn scene() -> impl Scene {
bsn! {
// No need to specify a Player component here.
// It is implied!
}
}
}
```
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:
```rust
impl Player {
fn scene(props: PlayerProps) -> impl Scene {
bsn! {
Player {
size_in_meters: {props.size_in_millimeters / 1000. }
}
}
}
}
```
### SceneComponent Trait
This is what SceneComponent looks like under the hood:
```rust
pub trait SceneConstructor: FromTemplate<Template: Default>
where
<Self::Template as Template>::Output: Component,
{
type Props: Default;
fn scene(props: Self::Props) -> impl Scene;
}
```
In general, developers should prefer `#[derive(SceneComponent)`, which
derives this trait automatically, ensures the Component is never spawned
without its scene, and automatically initializes the `Component` even if
it isn't specified explicitly in the user-defined scene function.
### Reusable Component Derive Logic
This PR moves the component derive logic into a new
`bevy_ecs_macro_logic` crate and refactors it to be reusable and
extendable.
This enables the `SceneComponent` derive to reuse and configure the
`Component` derive. I also took the liberty of porting the redundant
`Resource` derive logic to this system, as it follows the same pattern.
## PR Todo
- [ ] Port this PR description to docs on `Scene`.
## Whats Next
- Scene Component scene caching: It might make sense to cache
unparameterized scenes (like we do for scene assets) to cut down on
spawn costs. We could even have an opt-in parameterized scene cache (ex:
hash the props).
- Reactivity: It is important to note that props _are not reactive_.
They do not exist post-spawn (aka "at runtime") and they cannot be
changed. However they could easily be _part_ of a reactivity story:
- Props as components: insert props as components and re-run the scene
constructor when they are mutated
- Signal props: Reactive "signal" types could be passed down the init
hierarchy via props.
- Simpler "prop patching" codegen: currently for simplicity props reuse
the template-patching logic, but this introduces a few constraints and
complexities that are unnecessary. We should consider writing
specialized logic for them.
|
||
|
|
aa5322ed0f |
BSN: scene.spawn() system ergonomics (#23868)
# Objective
Spawning a scene on startup is a common pattern. Lets make it easier to
do so!
## Solution
- Add `SpawnSystem` and `SpawnListSystem` traits that are implemented
for functions that return scenes / scene lists, and return a system that
spawns the scene / handles errors.
### Before
```rust
fn main() {
App::new()
.add_plugins(DefaultPlugins)
.add_systems(Startup, setup)
.run();
}
fn setup(world: &mut World) -> Result {
world.spawn_scene_list(bsn_list![Camera2d, ui()])?;
Ok(())
}
```
### After
```rust
fn main() {
App::new()
.add_plugins(DefaultPlugins)
.add_systems(Startup, scene.spawn())
.run();
}
fn scene() -> impl SceneList {
bsn_list![Camera2d, ui()]
}
```
This cuts out some boilerplate. It also further encourages people to
define standalone "scene functions" rather than embedding them in code,
which is generally a good pattern.
|
||
|
|
6b0fb37e2c |
Rename bevy_scene2 to bevy_scene (#23668)
# Objective Part 3 of #23619 ## Solution Renames `bevy_scene2` to `bevy_scene` and adds it to the prelude. |
||
|
|
d06aa47744 |
Port Bevy Feathers to BSN (#23536)
# Objective Now that BSN is on main, we can port Feathers over! Related: #23030 #23413 ## Solution Port Feathers, leaving the bundle functions around (renamed to `x_bundle` and deprecated). This is largely 1:1, other than removing HandleOrPath (which is filling the same role as HandleTemplate). I've also ported the `feathers` and `virtual_keyboard` examples to BSN. --------- Co-authored-by: Alice Cecile <alice.i.cecile@gmail.com> |
||
|
|
727a350fc6 |
Sort the UI examples into sub-dirs (#22727)
# Objective - Fixes #22644 ## Solution - Create new subdirectories for categorization, and update the paths in `Cargo.toml` and `README`. ## Testing - `cargo check --example *` |