#Next Generation Scenes

1 messages · Page 5 of 1

magic belfry
#

Same, everything looks great
-# Except "BSN (pronounced B-Scene, short for Bevy SceNe)", that is so wrong and I shall not be convinced otherwise bavy BSN is pronounced "bee, es, en", and stands for Bevy Scene Notation. BSN is just the scene format, the rest of the scene system isn't inherently BSN imo

rare whale
#

Is what you are asking for:

  • Callbacks that are systems, but not registered (like run_cached)
  • Callbacks that are not systems at all
cobalt stone
rare whale
white lichen
#

im really curious about how .bsn assets will work with observers and expression blocks in general

#

are they gonna just not allow them?

#

if so, is .bsn a strict subset of bsn! or will it have things that bsn! doesnt

rare whale
#

In my earlier reactive experiments, where I used regular closures everywhere, I ran into a lot of pain points with captures - you'd have to do stupid things like declaring temporary variables and initialize them with clones just so that the closure could capture them without borrowing conflicts.

Later, when I moved to using systems, I realize that dependency injection is in many cases substitutable for captures: that is, you could often get the same information that you would have captured, but injected instead, without the borrowing problems. You'd still need to capture a few things, but these are mostly just ids which are copy/clone and have no problems with borrowing.

magic belfry
#

but could maybe be supported long-term to some extent

cobalt stone
#

And a system takes care of running them

magic belfry
#

I'm guessing .bsn assets can be hot-reloaded but bsn! can't yet, though hot patching will probably be possible with subsecond later on

native mesa
autumn bane
native mesa
#

Ok, so we should probably rebase the editor repo onto cart's branch.

quartz sleet
wind dock
#

Putting Rust code in a bsn asset file seems very challenging.

The other approach is to provide Bevy with a set of “hooks” when loading a scene asset that the system can automatically insert. So like “attach this observer to an entity named start button when it’s loaded.”

native mesa
#

My hope is that asset files will be able to reference functions that are registered with reflect.

#

Like JS callbacks, where you pass a function name to your html button.

#

But… we should probably try to keep the discussion scoped to what we have now

#

And leave asset format stuff for later.

cursive trellis
#

tbh I don't really see the point of using custom format when you can just use a .rs file with autocomplete type checking auto formatting inline expressions....

native mesa
wind dock
#

There are pros and cons to references in bsn itself, and hooking into assets dynamically. Referencing callbacks in bsn is simple but with the hooks approach you could load the same asset file twice but provide a different set of callbacks if you need to

#

It’s slightly out of scope right now but asset files are right around the corner

wooden vine
cursive trellis
#

alright I understand now

dapper sky
native mesa
#

sequel to what?

dapper sky
thick slate
#

Also uh using .rs for like save files is both cursed and likely to trip malware flags

native mesa
#

This is a "public experimentation phase" where we can collectively evaluate my proposed approach and flexibly pivot / iterate / tweak as necessary.
Speaking of the editor, and given that we clearly need to try this out to properly evaluate it, it seems like the next step would be trying to replace bevy_proto_bsn with this in the editor prototypes repo?

#

How do we want to handle that?

dapper sky
amber pebble
#

I'm a bit confused on when expression syntax is needed.
Why does the first code use it and the others work with just the plain variables?

magic belfry
#

Variables work "inline", but I think if you had something like position + offset or a function call or some other expression with actual logic, then that'd require the expression syntax

cobalt stone
jagged pilot
#

I think using {} as the delimiter for expressions is confusing. In rust (and many other languages) it would imply a struct or object instantiation, to me anyway. I would much prefer a more common syntax like {{ }} or ${}.

#

It was really the only thing just scanning over the examples that wasn’t entirely clear the first time I looked at it in de PR description

magic belfry
raw mesa
cursive trellis
quartz sleet
lime veldt
#
😂
    Transform { transition: Vec { x: 10. } }
👌
#

(I also think {} is good)

native mesa
jagged pilot
#

Ah damn haha. {} totally didn’t resonate with me as a block. You are all right… hmm. Still in the context of a template somehow it felt weird looking at the examples…

quartz sleet
#

I didn't think the tokenizer had that information, both are identifiers until the parser right?

#

Which gets skipped for macros since they get raw token streams

native mesa
#

oh that's right

#

hmm

magic belfry
#

these are generally linted anyway so imo it's fine

quartz sleet
#

So then _UnusedType and _unused_var aren't going to be valid? Like, obviously you shouldn't be doing that because they only start with _ if they're unused, but in my code it happens with global renames here and there. Just seems odd to me that it would break in this case

cursive trellis
#

this can be supported by just skipping non-alpha characters when checking the case I believe

magic belfry
#

I would treat those as valid and check the capitalization after the _ but idk

#

yeah

amber pebble
#

Forgive my dumb questions, but I have another one.
I don't fully understand yet what inheritance means in this context.

fn button() -> impl Scene {
    bsn! {
        Button [
            Text("Button")
        ]
    }
}

fn red_button() -> impl Scene {
    bsn! {
        :button
        BackgroundColor(RED)
    }
}

What is the difference between using :button here opposed to something like {button()}?

golden notch
#

i’d like if all the requirements for macro syntax were outlined for the inevitable bikeshed so we could all be on the same page

magic belfry
#

Does BSN allow this?

Button [
    Text("Button")
]
BackgroundColor(RED)

i.e. adding more components after the child relationship, without inheritance stuff

#

I would assume yes, but that it's stylistically not recommended, and there'd probably be some lint for it

autumn bane
#

Or it could just be automatically cleaned up by a formatter.

quartz sleet
# magic belfry Does BSN allow this? ```rust Button [ Text("Button") ] BackgroundColor(RED) ...

This actually demonstrates my issue with the children shorthand pretty well. I know this is likely to be a controversial opinion, so I won't really argue it too hard, but this looks to me like Button is a relationship. In my own assets, I'd probably use the full Children [ ... ] syntax for children because:

  • it makes it clear what relationship is being defined
  • Children isn't special-cased in the ECS, it's just another relationship component, so treating it special in bsn is imo inconsistent
    ...but I'll admit this is solidly in bikeshedding territory, so might not be (yet) worth the time it took me to type this 🙂
#

As a sidenote, how do we differentiate between a relationship and a single component followed by children? Is it name-based again? Because that would mean parsing relies on semantic information, which is something I'd have thought we want to avoid like the plague

craggy ermine
wooden vine
#

Having a different shorthand like @[] (so @ is effectively a sugar for "Children" in this context) seems better to me.

#

Sorry for the bikeshed, but I personally don't see any other issues with the proposal than certain syntactical edge-cases.

fallow cloak
quartz sleet
fallow cloak
#

See here: #1264881140007702558 message

quartz sleet
fallow cloak
#

there’s a small pile of bikeshed options, def throw yours on as well :)

wooden vine
quartz sleet
keen patio
# native mesa How do we want to handle that?

That would be easy since bevy_proto_bsn is not currently used by any crates, just yeet it! Jokes aside, it can still be useful if someone wants to try their hand at prototyping bsn asset editing until that is available upstream.
The only BSN-like stuff used for the actual editor UI is i-cant-believe-its-not-bsn. I guess that could be removed if we can hack together a replacement for the incrementalization

rare whale
#

I patched in the BSN PR, getting lots of lints

quartz sleet
#

Is it technically correct syntax to do: Children [A B, C D E, F, G H I]?

magic lava
#

Yep

cursive trellis
#

does BSN support unlimited number of children or is it capped at 12 like currently ?

split harness
topaz sigil
#

Not at my computer to check, does the relationship-concatenate behaviour in inheritance work as expected for one-to-one relations? I would expect it to only spawn the last-specified entity for easy override behaviour, but I also see how silently not spawning an entity could be a footgun

cerulean mango
#

If we’re bikeshedding shorthands for Children [ ] then for me at least > [] seems intuitive. It kind of looks like a fold, just like you’d see in a hierarchy UI view

silk lava
#

No shorthand for Children plus

#

Is inheritance a shorthand of expression syntax, seems odd that can do both

thick slate
#

Okay, I've finally had a chance to sit down and read the BSN proposal in earnest

#

I'm really impressed by and excited for this @split harness! It solves a lot of active issues nicely, and looks like a great base to build on.

I'm particularly keen on the "split out bite-sized chunks of this and polish them one PR at a time" as an approach to actually shipping this; looking forward to writing some docs!

karmic rock
#

wow, you read fast nvm I thought you said, finally have :p

thick slate
flint dragon
#

I think the inheritance syntax ":base_button" is a bit weird . I would propose "..base_button" which is IMO more rusty and more expressive.

golden notch
rare whale
#

Semantically, it's actually closer to the JavaScript/TypeScript spread operator, since it comes at the beginning of a block, not the end, but I don't see that as a deal breaker.

split harness
#

I also think it looks weird when used at the top, and when used for multiple inheritance

#

: is also less noisy

#

Things like ..base_button [ Node ] feel off to me

flint dragon
#

Im not sure if its noisy, but ".." is really not great semantically because in Rust it is placed at the end. However as @rare whale noted the spread operator in js land is pretty similar semantically and that is "...", maybe we could use that?

#

I think 90% of UI people know JS and would instantly recognize it

frosty shell
#

... seems like it would be more confusing than just using ..

split harness
#

: is pretty universally used as inheritance syntax

golden notch
karmic rock
#

(I also use :)

split harness
#

C++ inheritance, Java inheritance, C# inheritance

buoyant venture
#

I never connectd that symbol with inheritance, but now that you say it I can see it

flint dragon
#

I just feel that this inheritance is not really that inheritence 😅

split harness
#

Flecs is notably more like those examples, as it uses X: A {} syntax

frosty shell
#

(not java inheritance, you may be thinking of kotlin)

silk lava
flint dragon
split harness
#

~15 years

frosty shell
#

for good reason 😉

golden notch
#

hmm yeah, i guess :button to me feels different than something like class Foo : public Bar and didn't read that way to me but i guess i see it now

split harness
snow wedge
#

:button feels pretty intuitive to me (no experience with .. in js land)

split harness
#

([ Node ]) :button doesn't read nearly as well as :button [ Node ]

flint dragon
split harness
#

Yeah the big problem here is that this is new semantic territory

#

We can't be exactly like prior art because prior art isn't DOM-style ECS with multi-inheritance

wooden vine
#

Question, sorry if I missed this in all the info:
Does the "Inherited Children have lists concatenated" apply to all one-to-many relations?

split harness
silk lava
#

Hmm wait,
:button for overwrite
...button for appending
The former can have related overwritten, the latter appends them 🤔

golden notch
#

i guess that's why i liked the .., the js object spread operator feels more semantically similar than oo multi-inheritance

karmic rock
white lichen
#

what if use button

split harness
#

It would be useful to compare the tersest Flecs equivalent to:

:button [
  Node
]
karmic rock
karmic rock
split harness
karmic rock
split harness
#

It is a child of :button

karmic rock
karmic rock
split harness
#

Sorry for all of the assumed Bevy context 🙂

split harness
karmic rock
split harness
#

Yup thats my point. I think this is clearer / more natural when it comes to defining UI hierarchies (and scenes more generally)

:button [
  Node
]
#

Not to say that you made the wrong tradeoffs. I quite like where you landed / I think it reads well, especially for the bigger / more functional scenes

#

But for the "ad-hoc" spinning up of hierarchies I like the BSN syntax more

karmic rock
#

Hm not sure if I agree it's more natural, that wasn't my initial guess :p If you compare it to HTML it's much clearer what's a child and what not:

<div>
  <node>
  </node>
</div>
#

In flecs script each :{ } is a value scope, each { } is a hierarchy scope

split harness
#

But I suppose thats a matter of who you are / where you're coming from

silk lava
split harness
#

Yup. I think in both cases, thats a worthwhile tradeoff.

karmic rock
split harness
#

Its context you learn once and then you save a lot of noise + typing

split harness
#

(same for Bevy's [])

frosty shell
#

how do you specify other relation types in the flecs way?

karmic rock
#

fwiw

// anonymous entity
{ }

// anonymous entity but you need a token
_ : foo { }

// named entity
ent { }

// anonymous child
ent { 
  { }
}

// named child
ent {
  child { }
}
karmic rock
#

or if the relationship is not a hierarchy, you can just do

bob {
  (Likes, alice)
  (Likes, pizza)
}
split harness
#

Notably, Flecs has no notion of RelationshipTarget / everything is driven by the Relationship name. Bevy vs Flecs diverge in that we define RelationshipTarget [ (E1), (E2) ] and Flecs defines relationship {E1} relationship {E2}

#

Its also worth calling out that BSN currently has no way to express this flecs-style in terms of the Relationship name

#

Which I personally have no qualms with, because I think it fits the Bevy relationships mental model better

karmic rock
#

To always have Relationship/RelationshipTarget?

snow wedge
split harness
#

Yeah it always has those two. Notably when spawning, the RelationshipTarget [] occupies the same space the RelationshipComponent would occupy anyway. It is defined in "component position" on the parent entity / looks like a component / is that component

split harness
karmic rock
#

I could live with one of them being implicit, but both requires me to do mental gymnastics

snow wedge
#

I suggested more explicitly delimiting which things are entities and which things are components here: https://github.com/bevyengine/bevy/pull/20158#discussion_r2211286817

    bsn! {
        Player,
        Sprite { image: "player.png" },
        Health(10),
        [
            #NamedEntity(
                Hat,
                Sprite { image: "cute_hat.png" },
                Transform { translation: Vec3 { y: 3.0 } } ),
            ),
            // still an entity - but without a name
            #(:sword, Transform { translation: Vec3 { x: 10. } } ),
        ]
    }
silk lava
split harness
silk lava
karmic rock
split harness
#

Which you could do. But functionally something is going to "own" that entity from a hierarchical spawning standpoint

silk lava
karmic rock
snow wedge
karmic rock
split harness
karmic rock
#

ic ic

split harness
karmic rock
#

I would've preferred it if the ( were mandatory so there's a visual indication that a child has started, but taste can't be argued ig

silk lava
#

Sadly no optional top-level () wrapping 🥹

karmic rock
split harness
#

#MyName is currently just shorthand for Name("MyName")

#

But my plan is to make it slightly more magical to allow using it in arbitrary places in a scene

#

(ex: field assignments)

karmic rock
#

So a named entity with a named child is this?

#ParentName [
  #ChildName
]
karmic rock
#

How is that not confusing 😅 if the name sits in the same place as the component

#

How do I add components to that child?

snow wedge
#

I like that entity name is always in the same place in the flecs version

split harness
karmic rock
#

(btw, funny that you use # for names, I use # to indicate that a name string contains an explicit entity id, e.g. #500)

karmic rock
split harness
karmic rock
#

Gotcha 👍 ok that makes more sense

#

Yeah names are a first class concept in flecs script, I think that's probably the number 1 reason why they turned out so differently

snow wedge
split harness
karmic rock
#

@split harness tho now that I think about it more, if #MyName is short for Name("MyName"), how do I refer to other entities by name in bsn?

split harness
#

But it will still perform the function of filling in the Name component at that position, so this still feels good to me

karmic rock
split harness
karmic rock
#

kk yeah that's a very different direction than the one I'm taking. Every name identifier in flecs script resolves to an entity (including the components, relationships, relationship targets), so it's really ingrained in the language. You can also do things like

for x in 0..10 {
  for y in 0..10 {
    "e_{x}_{y}" { // create e_0_0, e_0_1, e_0_2, ... e_9_9
      Position: {x, y}
    }
  }
}
#

and then resolve those names later on in the script

split harness
#

Referencing an #entity that is never instantiated would probably be an error though.

snow wedge
#

Having names as a first-class concept seems like the way around that

karmic rock
split harness
#

Functionally / grammatically, things in "component position" vs "field position" are parsed differently

karmic rock
#

In bsn you'd do this, but that wouldn't work:

// I don't want to name my entity SpaceShip, I want to add the tag SpaceShip by name
#my_spaceship #SpaceShip
split harness
karmic rock
#

Right, makes sense from the direction you're taking, but IMO a bit less clean since now you have two different kinds of name identifiers

#

Heavily into bikeshedding territory, at the end none of this really matters

#

as long as it works

split harness
#

Yeah I haven't explored that space yet, but it feels resolvable one way or the other

#

Maybe something like this

scene {#Root [ Node ]}
// spawns an instance of #Root
:#Root
#

Although this does "feel" better to me somehow:

scene Root {[ Node ]}
:Root

Its worth calling out that we do want to explore struct-style scene inheritance, which kind of intersects with this

#

The name of the struct would be a "rust type name" which is semantically and functionally different than the Name component in Bevy

karmic rock
#

Yeah, in flecs those are the same thing

split harness
#

From a representation perspective (and Components as entities) that could be unified. Not the first time this has come up

#

Of course that spawned from you talking about it 🙂

karmic rock
#

I'm aware :p

split harness
#

However I think "ECS data types have Name components" does not necessitate "Things with Name components are ECS data types"

#

I'm not sure I'd go that far personally

karmic rock
#

Right, the latter doesn't make much sense

#

It would make it easier to allow things like

// intentionally explicit syntax to make it clear what's going on
Position {
  // Struct tag
  Struct

  // Member children
  x { Member: { f32 } }
  y { Member: { f32 } }
}

e {
  Position: {x: 10, y: 20}
}
#

The namespace in which the Position component is looked up is the same as the namespace for entities

#

(because it is an entity)

#

Funny side effect of this is that entity inheritance can also be used to express component inheritance, because they're the same thing:

Position2D {
  // ...
}

Position3D : Position2D {
  // ...
}
#

(though I still have to do a bit of work to make that actually do the right thing)

queen oak
#

CART I just want to say, i really think you cooked the perfect syntax and i have no notes. I feel like generally when stuff like this happens only people who disagree with choices make their voices heard so i want to say i love the syntax how it is and would prefer it not be changed at all. That being the commas how they are, using :, etc

#

it's incredibly clean

#

very good signal to noise ratio

dapper sky
#

yeah this has satisfied a lot of contradicting needs and circumstances, afaict.

#

very clear case of the time being spent squaring people's needs and potential design mistakes/dead-ends and trying to account for them.

magic lava
#

I also like it, my biggest question is about the need for { } around expressions, particularly when setting struct fields

native mesa
#

I think { } is needed around expressions so that the macro can tell where the expression starts and stops. it's not required for single-variables.

magic lava
#

I figured, but I'm wondering why it needs the brackets when : and ,/} will also indicate where it starts/stops

native mesa
#

I think it's easier to accept a block. If you wanted to just accept a range of tokens between : and , or } that's a negative lookahead. For macro_rules it would be actually impossible to implement, but for this implementation I imagine it would just add some complexity.

karmic rock
#

cart explicitly called out that BSN is still in the iteration phase, so this'd be the time to bring it up

native mesa
#

I don't mind having an explicit syntax for expressions though, personally. Both because the serialized format won't support them, and because It seems like they work just like rust blocks so you can do

field: {
  let a = foo();
  a.bar()
}

(It seemed like this was possible, I would have to double check).

dapper sky
rare whale
dapper sky
#

fully aware it's a subset and a secondary concern, but

karmic rock
forest shuttle
#

Does anyone have any links to how bsn will work wrt. to hot reloading?

For some reason I thought it would be magical like Dioxus' rsx! macro which hot reloads contents inside the macro.

  • Is that the goal (later down the line)?
  • Or will it just rely on subsecond? (Which isn't specific to bsn!)
  • Or will it only work for the subset of bsn! backed by files, which won't include Rust code "just data"
  • Something else?
thick slate
#

For asset-defined BSN, we can rely on asset hot reloading, respawning when invalidated

native mesa
forest shuttle
#

this in the past
special casing like rsx! you mean? I thought I had that from somewhere

thick slate
#

For macro / code defined stuff, we should rely on system-origin tagging, and rerun startup / on-enter systems

forest shuttle
native mesa
thick slate
# forest shuttle What's system origin tagging?

We've talked about it a bit over in #1374187654425481266, but the core idea is to take advantage of our location tracking to tag each entity with "which system generated me" while in hotpatching mode. Then, when a system is hotpatched, despawn all matching entities, then run the system again

#

Which means that hotpatching would work for any style of entity spawning, not just bsn!

forest shuttle
#

Really cool. So it sounds like the endgame allows fine-grained diffing of what was changed, so e.g. within some scope it's known that system A has a diff but not system B, so only the "system A tree" needs to go through that cycle you mentioned

#

Maybe that's already how things are from the outset when using dx idk

thick slate
split harness
# native mesa cart has prototyped this in the past, i just don't think it should be MVP

I actually haven't prototyped this in the context of the macro. The original pre-subsecond idea (which I stole from makepad) was indeed to parse the rust file (specifically the hot-reloaded macro) into BSN asset format form. The macro would record the Rust file line number for correlation purposes. Then we'd just "hot reload" the asset format form like we would with a normal asset.

However with the dawn of subsecond, I think it makes more sense to start with that

#

The benefit of subsecond is that we can make it handle arbitrary code changes

#

The benefit of the asset-format approach is that we can make some changes effectively instantaneous

#

(and it also requires less setup + infrastructure)

#

We can do both, but I think we should prioritize subsecond, as its the more complete experience

#

(and because we're investing in that dev experience in other areas of the engine)

karmic rock
# split harness ~~we can have it all~~

reason I said that is not just for hot reloading/developer UX, but because being able to inspect the AST is very useful for reactivity. It lets you trace which parts of a template are related to which inputs

split harness
karmic rock
#

same!

native mesa
split harness
queen oak
#

quick merge it in while no one's looking this is why i should never be a project lead hehe

split harness
#

To make it easier, we could also formalize a Rust expression subset (and then add an evaluator to the asset format, and then ...)

karmic rock
#

Yeah, it'll let you do things like

template Foo {
  prop a: 10
  prop b: 20

  const c: a + b

  // creates observer for SomeOtherEntity.OnSet(Position)
  const x: SomeOtherEntity[Position].x

  // static entity, never needs to be updated
  e1 { Position: {10, 20} }

  // only updated when a changes
  e2 { Position: {a, 20} )

  // only updated when b changes
  e3 { Position: {b, 20} )

  // updated when SomeOtherEntity.Position, a or b changes
  e4 { Position: {x, c} )
}
queen oak
#

Virtual Machine based on bevy reflect and bevy reflected functions with only three operations

#

push, pop, and call

#

👀 _ _

#

( this would be so bad but also so good but we also should never do it )

dapper sky
#

fun exercise that sabotages tooling t_t

queen oak
# queen oak ( this would be so bad but also so good but we also should never do it )

Damn i can't make a thread within a thread so i'll have to post this here.

i do want to note that we have quite a bit of resources at our disposal in bevy, and we could pull of quite a few shenanigans, way more than most other open source projects, BUT if we keep our shenanigans to a minimum ( such as absolutely no actual code in the scene files lmao ) then all that energy can go towards other things. There are a lot of nerd traps that bevy hasn't fallen into yet, such as adding an official scripting language. So just like, yeah, it would be epic, but also damn it would cost so much, so i am not in favor of anything like this lol. We get a lot out of how small our actual weirdness budget is.

#

Just to share my thoughts

karmic rock
#

it is doable

dapper sky
#

I do think that Dhall is really cool and have linked it earlier, is the thing

dapper sky
keen patio
# split harness I actually haven't prototyped this in the context of the macro. The original pre...

I actually prototyped this, but when subsecond was announced it just felt pointless.
Source files were loaded as assets, scanned for bsn invocations, and diffed against the original to produce scenes that were partially from the static invocation, and then patched over with the changed/reeflectable stuff from the reload. This meant that you could change a value in an invocation heavily reliant on rust code as long as that particular change was reflectable. The algorithm got pretty complex in the end so I was kinda happy to let go.

keen patio
#

I think the trickiest part was matching up children/relations to allow moving them around anywhere in the hierarchy even if they weren’t reflectable

somber rivet
#

"L" as in language and "L" in the gen alpha sense

rare whale
#

For incremental updates, the approach that I have used is not to try and solve the general problem of diffing hierarchies, but to recognize that in practice there are only about a half-dozen control-flow primitives and they all have relatively straightforward diffs. So as long as you have a record of which control-flow primitive was used at a given point in the hierarchy, it's relatively simple to decide what needs to be changed. The most complex scenario is the foreach primitive, but this is just comparing two linear sequences, and the algorithms for this are well understood.

rare whale
patent hinge
#

cross posting here since y'all talk about reactivity #ui-dev message

#

aww i thought that would show an embed 😭

rare whale
#

So my original plan was to follow up with work on menus and dialogs. The logic is that menus and dialogs, being composite widgets, would be considerably easier to implement using BSN. However, menus and dialogs don't add anything new to BSN, they don't validate anything that hasn't already been validated; they are merely using techniques that we've already demonstrated with pane and subpane.

Instead, I changed my mind and decided to follow my "anxiety minimization framework", which is to say, work on the thing that I think has the highest risk and feeling of uncertainty, the most likely to discover weaknesses in the BSN design.

In my thorium_ui library, there's a bunch of components that have BundleEffects, so I decided to try and see if I could re-implement these as Templates instead. I haven't quite got it there yet, and I can see now that there's a number of things I don't understand.

#

Part of what's tripping me up is the way things are named: normally when we name some software concept, we're building a metaphor based on the behavior of some real-world phenomena or thing. However, I kind of feel like some of the things in BSN are taken from industry jargon, but only mildly resemble the things they are named after, and so like the "game of telephone" they are several steps removed from the real-world meaning of the term. And my assumptions about the connection between the real-world thing and its namesake are causing me to make assumptions that aren't true.

#

Of the various things in BSN, Template is probably the closest to its real-world definition:

  • A pattern used to construct a thing, to guide the tools used to shape the thing, and which is isomorphic to the thing being built.
  • A template is not a tool (what shapes the work product) and its not a jig or clamp (what holds the work in place while it is being shaped).
    However, I'm a bit confused as to whether bsn! can rightfully be called a "BSN template" since it's actually a collection of objects which make reference to Template instances.
#

TemplatePatch is another one that I have issue with: I read that as "a patch applied to a template", but in woodworking, one does not normally modify a template - the template is a constant during production, and is only modified when changing the design. I feel like what is being modified here is not the Template but the thing that the template produces - the work product, which might be called the "template instance".

#

What we're calling "scene" seems like it's really a "scene element builder" or "scene assembler"

somber rivet
#

Is a template patch a diff?

rare whale
somber rivet
#

That makes sense

#

Patch seems pretty straightforward in that case, or at least I struggle to find something that better matches the concept

rare whale
#

It's like if a professional boxer also happened to know how to fly a plane, would you call them a "fighter pilot"? No, because that would be confusing, even though they are both a fighter and a pilot.

somber rivet
#

Sure... I'm trying to model this in my head with bsn but the simple stuff I have knowledge of seems to work fine. For instance, I have a button template and a red button template, and red button just patches button. That seems to work fine in my mental model. Does the issue emerge in more complex cases or is the issue already there in the button and I'm not seeing it? Your fighter/pilot example does seem to be problematic, but a button/red button seems fine

#

Honestly this is already making me angry (joking). Just got done listening to Casey Muratori tell me why inheritance is bad for 2 and a half hours and this seems to belong to the same family of conceptual headaches that had me move away from OOP

rare whale
#

Well, if this is annoying I can stop discussing it. I tend to focus on precise naming of things.

somber rivet
#

No it's not annoying, I am curious what your thoughts are

rare whale
#

All right

somber rivet
#

I was just joking, and noting that this is the same sort of naming problem and "what is what, what goes where" problem that you can avoid a lot of when you leave OOP land

rare whale
#

Part of the difficult is that, in programming, we tend to use the same word for both the type and the instance of a type. Most of the time it doesn't matter: you can use the word "Red Button" to mean the type whose methods produce the red button, or you can use it to mean the actual instance of the red button.

#

However, when we talk about templates we are specifically talking about a process of creation.

#

In the physical world, a template is used to create a thing, but the thing created is not a template.

#

But the Template Rust type refers to both the template and the thing the template creates. It makes sense to patch the thing that the template creates, that's what the process of creation entails. But the template is constant, it isn't changed by the process of creation. But we use the same word for both.

#

(And BTW, OOP has far worse conceptual difficulties than this. One of my favorites is the Platypus Effect - a type which doesn't easily fit into neat hierarchical categories.)

somber rivet
#

Riiight okay that makes more sense. It seems like it's a conceptual leap that bsn is asking you to take. red_button() is the template, but in your code it behaves like the button. Now that you've pointed it out to me I can't unsee it lol

rare whale
#

I managed to get a small reactive demo working in BSN:

commands.spawn_scene(bsn!(
    Node {
        left: ui::Val::Px(0.),
        top: ui::Val::Px(0.),
        right: ui::Val::Px(0.),
    }
    // When the condition is true, insert the component; when it's
    // false remove it.
    insert_when(
        |state: Res<State<GameState>>| *state.get() == GameState::Play,
        || BackgroundColor(css::GREEN_YELLOW.into()),
    )
))
#

@thick slate You were asking as to whether we still needed BundleEffects - this code was effectively ported from BundleEffect to Template/Scene.

thick slate
#

I suspect that there's a good chance that we need BundeEffect for performance? But yeah, it's conceptually suspicious to me

rare whale
#

Now, this demo is not very sophisticated:

  • The mechanism for detecting changes is somewhat brute-force.
  • Rather than doing compile-time analysis of which properties are dynamically and incrementally updated, it just creates some small metadata at runtime, using relations, which tells it what to change. Similar to the kinds of hidden satellite entities used by observers.
rare whale
magic lava
#

Ack, just bonked my head against a wall for a while but turns out something is up with spawning bsn scenes
Somewhere in my code this is happening:

commands
    .spawn_scene(bsn! {
        Node {
            width: Val::Px(100.),
            height: Val::Px(100.),
        }
        BackgroundColor(tailwind::AMBER_400)
    })
    // Adding ChildOf stops the node from showing up
    .insert(ChildOf(*root))
    // Inserting Node the old fashioned way *somehow* fixes it
    .insert(Node::default());

I tried inspecting resulting entity (using log_components and queries) and I could not tell what is different about it when it has a parent.

#

I don't have a minimal repro at the moment

#

Also, doing (partial) ports of existing code is rather painful at the moment. Hierarchies in particular.

rigid adder
#

ports of existing code is rather painful
One of the reasons I hoped to see bsn in 0.17 🙂

keen patio
# magic lava Ack, just bonked my head against a wall for a while but turns out something is u...

Looks like Commands::spawn_scene is always deferred. It only inserts a ScenePatchInstance on the entity. The actual "spawning" (template build/insertion) happens in the spawn_queued system (currently once per frame in Update) when all dependencies are loaded.

I have a feeling this will confuse a lot of people. Maybe there should be both a sync and an async command for spawning scenes? 🤔 Which leaves the question what the sync version should do if there are unresolved deps

Not sure if (or why) this is the problem in your case, but it might be related

magic lava
#

It might be a bug in bevy_ui. I should try spawning an empty child entity, and then inserting Node after a frame or 2

copper dragon
magic lava
#

To be fair spawning scenes has generally been deferred

copper dragon
split harness
# rare whale So my original plan was to follow up with work on menus and dialogs. The logic i...

However, I'm a bit confused as to whether bsn! can rightfully be called a "BSN template"
I wouldn't call it that / I think that confuses things. bsn! is a Scene.

TemplatePatch is another one that I have issue with: I read that as "a patch applied to a template"
Thats what it is 🙂

but in woodworking, one does not normally modify a template - the template is a constant during production
We are modifying the Template here, but I think the woodworking template metaphor still works here pretty well. TemplatePatches are forming the template we will use to create our final outputs, via layers of template patches. Ex: we are "layering template stencils on top of each other to create the final template". for example, we use a pencil to trace a star template on top of a circle template to create a "spike ball" template.

#

The "stencils" are the TemplatePatches, "penciling in" is when we resolve those to the final "spike ball" Template, which we then use to trace out the spike ball on the wood.

magic lava
#

@split harness Could the Default + Clone requirement be lifted from constructors and associated constants? Presumably by wrapping them in a function/closure.
It has been pretty frustrating to deal with existing components that work just fine, but must be (non-trivially) reworked with GetTemplate if I want to use them within bsn!. Especially when they still need to be usable outside of bsn!.
In that same vein, I'd like to be able to pass functions that produce components(bundles?) into bsn!.
My workaround now is to create one-off Template types that insert the value I need. Got frustrated trying to wrangle traits to make something re-usable so I have a little macro for it.
nvm, I missed the template function, which pretty much solves all that

keen patio
magic lava
#

Ah, I totally missed that 🥲

#

Thank you!

magic lava
#

That does make things a lot nicer 😌

keen patio
magic lava
#

Fields being placed directly into a closure is rather annoying, this means we have to move calls .clone() or similar out of the bsn! macro

#

And passing in a variable called value breaks the macro :0

magic lava
#

Some convenience for boxing scenes would be nice :)

copper dragon
split harness
#

Hey yall! Quick reminder that I’ll be on vacation for a week / I’ll be largely disconnected starting tomorrow. Feel free to keep discussing and when I get back we can iterate some more

rare whale
#

@split harness Can you show me how to use an ImageNode in a BSN block? Assume that I have an &str for the asset path.

magic lava
#

You can work around it for now

template(move |entity| {
    let handle = entity.resource::<AssetServer>().load(string);
    Ok(ImageNode::new(handle))
})
#

Looks terrible though

#

It would look like this:

#
ImageNode {
    image: string
}
rare whale
#

@split harness Another use case for "intercepted children": menu buttons:

pub fn menu_button(props: MenuButtonProps) -> impl Scene {
    bsn! {
        :button(ButtonProps {
            variant: ButtonVariant::Normal,
            corners: props.corners,
            on_activate: CallbackTemplate::Ignore,
        })
        [
            {props.label},
            :flex_spacer,
            :icon(icons::CHEVRON_DOWN)
        ]
    }
}

The problem here is that we want the label to come before the downward chevron on the right. We could hack around this by reversing the order, and setting the flex_direction to ColumnReverse, but if you have more than one child that gets confusing.

keen patio
#

Hot reloads anyone!? 🌶️

I implemented a reconciliation (incremental update) algorithm for ResolvedScene based on @native mesa's awesome work in ICBINBSN, with a couple upgrades:

  • The Name component (or Template rather) is used to key entities, otherwise it falls back to an auto incrementing id like usual. Works nicely with the #-syntax! Though it will be even nicer with #{<expr>}
  • The component diff handles removing components that were previously explicit but now required (to make sure they reset when recycling entities)
  • Supports all relationships ala bsn!

This could potentially be used for coarse/medium grained reactivity implementations.
But it's also great for non-destructive hot reloads. Here's a demo using subsecond, reconciling the scene on every frame:

(sorry about the watermark... too lazy to figure out the right ffmpeg args)

Edit: Now available at https://github.com/cart/bevy/pull/36

native mesa
#

wow

#

i've done web-dev with bundlers that were slower than that.

white lichen
#

this is so good

native mesa
#

it never occurred to me that you could use the same basic incrementalism for both both logic/interactivity AND for reloading the logic.

#

the way the checkboxes stay checked during styling is chefkiss

thick slate
#

@thick heath look 😄

dapper sky
thick slate
#

Yeah, I'm very "this is Rust"??

native mesa
#

I am going to have to gush about this a bit; the way you can maintain the full state of the application through a code-swap is something special. Keeping that last button unchecked while moving it around in a function is not something it's easy to do even on the web.

wind dock
#

It speaks to the power of the bsn design that people can experiment with reactive stuff on top of it like that

dapper sky
#

seeing the long-standing tradeoffs of rustdev get melted away by community effort is just really exciting

dapper sky
native mesa
#

Obviously the big limit to this is going to be the names, right @keen patio ? If you tried to move state across the hierarchy, I wouldn't expect it work (my prototype used namespacing, idk if you found a way to fix that). Any other edge-cases?

native mesa
#

hot take i really like the :thingy syntax.

#

it reminds me of elixir

#

and the more i see it in use, the more i like it.

thick slate
#

Yeah I'm pretty okay with it 🙂

#

There's a learning curve, but that's fine

keen patio
keen patio
native mesa
#

I think that's a pretty reasonable limitation. Reordering is much more common than arbitrary hierarchy restructuring.

#

So like ... this is probably what the editor scene viewer should run on right? We could rebuild & re-render the entire scene, but if we do things incrementally then it's easier to persist things like selection state across a reload.

white lichen
#

it reminds me of css selectors

#

div + :hovered type shit

#

doesnt really map conceptually though

karmic rock
native mesa
#

This diff system (correct me if I am wrong @keen patio ) runs on the final output of that asset system, ResolvedScene. You just just take the current ecs state and "reconcile" it with the ResolvedScene data.

#

I was thinking of the editor writing bsn asset files, then loading them, resolving them, and reconciling them.

karmic rock
dapper sky
#

ah, are you calling .bsn files "code" here?

karmic rock
#

I called it bsn code yeh

native mesa
#

that's my understanding of the plan at least.

#

bsn is what the editor edits.

keen patio
#

Yeah that's how I imagine it as well. An inspector however (I think its very important that we separate the two) would still edit components in the live ECS through reflection

karmic rock
#

Right, so if you have a complex scene with say 10K LOC and you drag an entity across the scene. You're going to patch & reload that scene?

#

* for each frame that you're dragging

#

That sounds like a bad idea

cobalt stone
#

I had this demoed a while ago with bevy_dioxus 😅

karmic rock
native mesa
karmic rock
#

Sure, it wasn't the loading/saving to a file that I was worried about :p

keen patio
karmic rock
keen patio
#

The inspector is a tool to inspect a live ECS (for debugging etc)
The editor is a tool to edit scene (aka .bsn) files

karmic rock
#

In a way the scene view is worse since the frequency of updates is higher than an inspector

native mesa
#

Not in my mind. One of those is editing a document, one of those is editing runtime values. Bsn is not a save-file format, at least not currently. You can't take arbitrary ecs state and serialize it back to bsn (currently).

karmic rock
#

How I would have expected this to work is that both go directly through the ECS (with or without reflection) and when you save the scene it's saved to bsn

native mesa
#

How would that work with things like scene imports, or patching?

karmic rock
#

I don't see why that wouldn't work

#

Import or patching are infrequent things, so then just serialize the scene to bsn, patch, done

native mesa
#

say you've loaded some data into the ecs from several files, one of which imports and applies patches from others. how do you know how to modify those files?

#

if a component on a given entity has fields compiled from multiple different files (and some default fields), and then you overwrite one of the defaults with a specific value, which file does it go in?

karmic rock
#

Dunno, that sounds like a self imposed problem. Doesn't that go for any update you make to the scene? If a component is set in A.bsn and one field is patched in B.bsn and now I modify that field, which bsn asset do I update?

keen patio
#

Some of the values might not even be in any of the files. They could just be part of T::Default. We would have to track each field on every component to know if it was explicitly set, so we know if it should be included in the saved file

native mesa
#

you have to expose the fact that values come from bsn file to the user, to differentiate things, and let them pick what goes where. I suppose you could add a layer to the ecs tracking which fields to serialize where, but at that point you're basically doing the same thing as modifying bsn documents in memory.

karmic rock
#

Ok, bit confused now. So you're saying you cannot save a scene to bsn with the current approach?

native mesa
#

I think we should probably move this to #editor-dev

karmic rock
#

If you load a scene from several bsn files but you cannot track which one to update after you make modifications, then what's the point of having the scene saved in bsn? 🤔 probably exposing my ignorance of bsn here, just trying to understand the workflow

native mesa
#

I don't want to distract from @keen patio 's work.

keen patio
wind dock
#

Since state is retained does that mean it triggers change events only when values change? Like it’s only going to trigger updated events for a component if that specific component changed?

keen patio
#

There really isn't much magic to the state retention here. No change tracking or event triggering.

One just has to think about what is state and what is output. The scene being reconciled would be the output. Any state components has to be added in other ways, and may never be part of the reconciled scene itself, as they would be overwritten with their default values on each reconciliation.

The reason the Checked state is retained is because the Checked component is not added by the reconciliation. It is added as a result of clicking the checkbox. The reconciliation only removes components that it added.

:checkbox in its current state actually includes a state component, namely Hovered. And the only reason you are seeing a hover effect in the demo is because the scene is reconciled in the First schedule. The Hovered component is re-inserted on every frame with its default value (not ideal). update_is_hovered runs in PreUpdate.

@rare whale how would you feel about only including output components in the feathers scenes? State components like Hovered could be implicitly included using #[require()].

I personally believe that it is important to separate state and outputs regardless of what the final reactivity solution will look like. Mixing them together sounds like a recipe for 🍝

karmic rock
#

(which would be entirely reasonable)

keen patio
rare whale
#

What happens with on() ?

keen patio
rare whale
#

My guess is with current-style observers you'll get duplicate registrations; with future relation-based observers it will work like other relations. Maybe.

keen patio
#

Looks like it outputs a template that runs entity.observe(...) on build. So that would run on each reconciliation

rare whale
#

I think the answer here is that Template needs an unbuild method for these special cases. This is very similar to what @timid leaf did with his thing.

keen patio
#

Yeah I guess that could be one option

#

I went for a different solution in proto BSN where observers were added with a generic Component instead, adding/removing the observer using hooks. That way it could handle reconciliation

#

(also inspired by ICBINBSN)

#

Maybe OnTemplate could check if there already is an observer attached of that exact type (the static function type, not the event), and if so skip adding it? 🤔

rare whale
#

There's no right answer:

  • Keeping the same observers means keeping any mutable state
  • Blowing away the observers and replacing them means updating captured values from the environment.
    Of these two, I think the second is more important
keen patio
#

Right, yeah option two makes the most sense to me

#

Anyway, it's getting late over here (1am in 🇸🇪). I'll try to get something up as soon as possible so yall can play with it too!

rare whale
#

Anyway, I'm having weird problems with spawning BSN as children to pre-existing entities. That is, when I spawn a bunch of nested scenes, no problem - but if I try and add a child scene later, using commands.spawn_scene() and then add_child, it doesn't show up - but looking at the code, it should work.

queen oak
karmic rock
#

but there's still a 3 second delay for a UI that only has 5 buttons

#

I think we'll have to wait for a bit to see how this scales to apps like an editor

queen oak
#

Hahahahah, true, true

#

More work must be done!

#

But im fully convinced that its within reach for rust to be incredible for ui dev, it just will take more focused effort by amazing developers

karmic rock
#

Hm, this is not so much about Rust I think, since there are already frameworks that have demonstrated it's useful for UI

#

This is more about BSN / hot reloading I think

#

Like we already have dioxus which people seem to like

timid leaf
cerulean mango
#

Re: reload speed: I’m using proto bsn’s asset format and reloads using the asset are way faster than going through code. Obviously you only have a subset of the features you’d have in code, but reloads are almost instant. The hierarchy I have isn’t huge, maybe 1k lines of ”code”, but still..

lusty epoch
# native mesa if a component on a given entity has fields compiled from multiple different fil...

in Figma's component-instance system, there are virtual entities which doesn't exists in the "source of truth" data (in our case bsn files), that are created because you are instancing a template that is a tree. If you modify any property of these objects, it's data is saved as "overrides" entry on the immediate non-virtual ancestor. (the instance root)

I feel bsn is an authoring format (allow easy edit in plain text form, multiple ways to do the same thing), but an editor works better with a serialization format. Not sure how hard it is to make the format that support both.

magic lava
cerulean mango
#

Using proto BSN I noticed that if Node is added to a parent in a deferred way (don’t know the right terminology), the children will not show. I have to manually add Node so things don’t break in that one frame when Node is missing.

keen patio
#

Yeah it seems like it would be a bevy_ui issue, which just becomes easy to hit since spawn_scene is deferred. You end up with an invalid (has non-Node parent) Node until the scene is resolved and spawned. And it seems like the UI systems can’t handle the parent adding Node, making the child valid

(Have not reproduced and confirmed on main though)

rare whale
raw mesa
#

Why does spawn_scene need to be deferred? What does it do that requires deferring it?

keen patio
#

To spawn a scene synchronously right now, you could do something like this:

/// Synchronously spawns a [`Scene`] with pre-loaded/no asset dependencies.
fn spawn_scene_sync<S: Scene>(entity: &mut EntityWorldMut, scene: S) -> Result {
    // Create a new ResolvedScene and patch it with `scene`
    let mut resolved_scene = ResolvedScene::default();
    world.resource_scope(|world, assets: Mut<AssetServer>| {
        scene.patch(
            &assets,
            world.resource::<Assets<ScenePatch>>(),
            &mut resolved_scene,
        );
    });

    // - Builds the templates
    // - Inserts the resulting bundle on the entity
    // - Spawns related entities
    resolved_scene.spawn(entity)?;
}
copper dragon
rare whale
#

There are really two parts to this problem, the first being the deferred spawning. In order to preserve the order of insertion of children, we need an entity id to produced synchronously, but that id has no content until the assets are loaded. But the other half of the problem is that once the assets are loaded, the ui system is (I'm guessing) not updating or propagating what it needs to - it's not noticing that the empty child is now full of stuff.

somber rivet
#

Could a mvp of bsn just require that bsn files be loaded on startup? It sounds like the current form supports some kind of asset streaming which is an awesome use case, but is an advanced one most users probably wouldn't need?

rare whale
#

This is an important feature

somber rivet
#

I think a blocking version or maybe the option to preload assets in a startup system would be good then.

#

I guess both since the preloading would speed up the blocking version

#

fwiw I think these are common distinctions when loading assets

#

You can get the asset now (blocking) or later (not blocking). Technically it complicates things but not more than any other version of this type of thing.

copper dragon
raw mesa
#

Also, (I am probably wrong about this) I thought the template for handles just store the asset path, so they wouldn't be storing a handle, so they wouldn't be loaded with the asset? (Unless we have special code that looks for those templates and creates those handles so we load them ahead of time)

rare whale
keen patio
keen patio
karmic rock
thick heath
keen patio
split harness
# raw mesa Why do we need all those other assets to spawn the bsn? I would assume that the ...

Found a short moment to read some messages (I’m not back from vacation yet)

It’s worth noting that waiting to spawn a scene until arbitrary asset references have been loaded isn’t fully wired up yet. Currently it only does scenes. Template::register_data exists for this use case though. My current plan is to add a high level spawn_after_deps_loaded API, and maybe also adapting HandleTemplate to support configuring this per template (ex in SpriteTemplate)

Not quite sure what the final shape of this should be, so feel free to develop opinions and experiment

raw mesa
#

My main opinion here is that by default BSN should not be deferred. The primary API should be sync. Deferred should ideally just be a convenience component to do the spawning for you once assets have loaded (basically like SceneRoot today).

Good to know templates already have a way to register this data early! It is certainly useful to start loading these assets ahead of time. I'm just not really convinced that you need to block on them. Most of our systems already expect assets to possibly not be loaded, so I'd prefer we lean into that.

split harness
karmic rock
#

What about scenes where you stream in content based on what's around you?

copper dragon
split harness
#

Back when I blurred the lines more between templates and scenes

copper dragon
#

this feels like the current pattern we use for the defered/nondefered info level

split harness
#

Although in practice anything “real” is likely to be doing scene inheritance. Which requires dynamism

copper dragon
golden notch
split harness
#

Very tbd. Feels like a “higher level” thing. Curious if you think it would require fundamental changed to the design

karmic rock
split harness
#

Which a scene could configure

golden notch
split harness
#

We have some LOD stuff already

golden notch
#

obv loading the mesh fully for physics etc still needs to be supported

#

so i don’t think it’s a problem for scenes. if the mesh isn’t requested for cpu memory it can just load instantly with its description/layout

#

and defer streaming to gpu until that’s actually needed

#

have been talking about this a lot with @white lichen

karmic rock
# split harness That’s mesh / asset config

Right, but (and maybe I'm misunderstanding) that could cause meshes to be (re)loaded at any point during the scene right? So "all or nothing” seems like something that's difficult to satisfy

white lichen
#

we probably want an asset process step for physics meshes eventually

#

straight up throwing a mesh at physics doesnt make sense: you need different things. you dont need tangents or uvs or colors etc, you need geometric normals not interpolated normals, you need acceleration structures like a bvh over a trimesh, or you need convexity, etc

golden notch
white lichen
#

yup

golden notch
#

in general i just want us to move away from the idea that because an asset exists in the world that it has any relation to being in vram or not

#

separate question, you may want to reference an asset before it’s materialized fully. the asset lifecycle != gpu lifecycle. we just have to make it easy for beginners so that it does the current behavior by default while leaving room for advanced users to optimize

split harness
split harness
#

Ex “fully lazy”, “wait for the lowest lod”, etc

karmic rock
#

Makes sense 👍

#

I was envisioning something more like a CSS media query where you could add/omit/change things to a scene based on LOD/other parameters

#

And just like a browser it'd load assets when they are first required

rare whale
#

@split harness @raw mesa I don't think making scene loading sync is the answer, or at least let's try solving the problem some other way before we throw out the baby with the bathwater. It's sill early yet.

#

I can tell you that better async support would have been a godsend on Sims 2. We spent sooooo much developer time trying to keep the player entertained while loading the neighborhood (which took like 15-30 seconds).

#

Really, the problems that we are seeing aren't really the fault of BSN, it's more that BSN is exposing weaknesses already present in Bevy.

#

We do, however, need to expand the limited BSN API, something which cart and I have already discussed.

raw mesa
# rare whale <@153249376947535872> <@160128095502860290> I don't think making scene loading s...

There's a little weirdness here in terms of what we mean by sync vs async. I want the core BSN API to be "sync" in the sense that I can create a BSN and spawn it directly into the world without waiting for a system to see it or something. I don't want the API to require to wait for every asset the BSN references. I consider that async/deferred since (AFAIK) the scene literally won't spawn until the referenced assets are loaded meaning if you spawn it into the world, you won't actually be able to interact with the scene yet.

#

I am all for having utilities to wait on the referenced assets before spawning the BSN, but that could be done with a SceneRoot component that also has a flag "wait for referenced assets".

rare whale
#

(Maybe even re-use the same event)

raw mesa
#

Something like that, yeah

#

Essentially I would like it if spawn_scene was just a very convenient Command (i.e., instantaneous), and then all the asset stuff is handled in like a spawn_scene_from_handle or something (which would be deferred)

rare whale
#

I mean ultimately the end goal is "don't let the player see bad stuff"

raw mesa
#

I don't agree? As in, I think the goal of BSN itself should be to make it easy for devs to spawn a hierarchy (obviously an oversimplification). All this stuff about waiting for assets to load seems like something that should be handled "externally". That allows powerusers to decide exactly what they're willing to block on, and what they're willing to let be deferred.

#

I know load screens suck, but it also sucks for things to take longer to load in the first time compared to other times. Giving users more control over when they want to spawn scenes is my priority (e.g., we have to block on the floor mesh, so make sure it's loaded before we spawn our BSN, but the trashcan mesh we can just ignore and spawn the BSN before it's ready).

mellow rover
#

How about a WaitForX Relationship to put the control inside the scene? Like WaitForAllAssetsLoaded [handle_audio, handle_background_image,..] or even WaitForEvent

keen patio
keen patio
heady latch
keen patio
heady latch
#

Can’t put your PR into the milestone because of that haha

keen patio
heady latch
keen patio
#

I guess there could be issues like "State-preserving scene hot reloads" or "Reactivity". Reconciliation would be one step towards those, but there may be other ways too. I don't think it makes sense with an issue on the bevy repo for it just yet, since BSN may also change and we might find other ways to solve those issues.

rare whale
#

@split harness I did an experiment whereby I changed BSN to load scenes in PreUpdate rather than Update, and the FOUC problems went way. However, what I really want to do is instead add an explicit ordering to run it before PropagateSet::<TextFont> - but this can't be done without adding dependencies. Either that, or have the BSN stuff run in its own SystemSet and then put PropagateSet after it.

thick slate
rare whale
rigid adder
#

Why resolve_scene_patches and spawn_queued happens in Update instead of PreUpdate? 🤔

split harness
split harness
keen patio
queen oak
#

OWO

queen oak
#

i've experimented a good amount with this kind of thing, if we ever wanna open the door to specifically talking about tighter bevy async integration i have thoughts, lots of thoughts 👀

keen patio
#

Posted a couple more PRs to carts branch:

GitHub

bevyengine#20158
The #-syntax currently allows specifying names using Idents, which is nice.
This PR extends it to also support arbitrary expressions enclosed in braces:
let n = 5;
bsn! {
#{fo...

GitHub

bevyengine#20158
Got annoyed by the lack of formatting, so I started commenting out things to see what caused it.
Looks like rustfmt doesn&#39;t do well with long turbofish paths in quote! macr...

patent hinge
#

finally seriously released my reactivity solution 🙂 #crates message

keen patio
magic lava
#

I'm somehow still getting child nodes that don't show up 😭

magic lava
#

@keen patio I'm still getting missing child nodes with this simple repro. When you have time could you take another look at this issue?

commands.spawn_scene(bsn! {
    Node
    template(|entity| {
            let id = entity.id();
            entity.world_scope(|world| {
                // This shows up
                world.spawn(Text::new("Argh")).insert(ChildOf(id));
                // This does not
                world.spawn_scene(bsn!{ Text::new("Bruh") }).insert(ChildOf(id));
            });
        Ok(())
    })
});
#

I have the fixes from you and ickshonpe as I'm on the latest commit on next-gen-scenes

keen patio
#

Interesting. Is it only when using Text or do you get the same issue if replacing it with Nodes instead?

magic lava
magic lava
#

Okay, it's not specific to UI nodes at all.

#

3d meshes don't appear either

#

Bah, I didn't try to check it earlier but the scene doesn't get spawned properly. @keen patio, It's got nothing to do with UI this time

magic lava
#

Somehow the query inside spawn_queued never considers the scene instance component as 'added' on this entity...

#

Wait. As I type that I just realized..

#

It's because this entity is spawned inside that system

#

aaaaa

lime veldt
#

these subtle details are what can make this "refreshingly simple" engine... defreshing

magic lava
magic lava
rare whale
#

The popup menu is initially invisible, because it needs to measure the size of the popup before it can determine the correct coordinates. One frame later, the code changes the visibility of the popup to Visible, but when desktop_app is in effect, that never happens. I tried sending a RequestRedaw event when I spawn the menu scene, but this doesn't seem to have any effect.

#

If you wiggle the mouse after clicking the menu button, the menu shows up.

#

Strangely, my other menu example, which doesn't use BSN, doesn't seem to have this issue.

#

Also FYI @crude pawn , your Virtual Keyboard widget is broken atm in the BSN branch because VirtualKey isn't Clone. Wasn't sure what the right fix is.

split harness
rare whale
split harness
#

Perhaps it should be structural though 🙂

rare whale
split harness
rare whale
#

The difference is when it happens

#

At the next sync point if we're doing commands I would guess

split harness
rare whale
raw mesa
rare whale
# raw mesa Does an asset load request a redraw? Probably not, but it probably should?

For all I know, it already does. The problem in my case is that there are 3 separate delays:

  • The observer calls commands.spawn_scene, which runs at the sync point.
  • The scene gets spawned after asset handles have been resolved, which creates the popup.
  • The popup waits one frame so it can measure the size.
    So what we want is for step 2 to request a redraw so that step 3 can run.
raw mesa
rare whale
rare whale
#

@split harness @raw mesa So, that didn't work. I tried adding an Add observer for Popover, and it never runs (until I wiggle the mouse). So the redraw request needs to be earlier in the food chain.

#

It's hard to reason about this stuff because I don't exactly know what existing Bevy systems request redraws.

raw mesa
#

If any system in bevy requests a redraw, it should run - the check for the request redraw happens in the winit runner stuff IIRC, which is after the entire app runs a frame

raw mesa
#

So there are potentially three places you might need to request a draw:

  1. you spawn_scene after the BSN spawning system runs - place a request redraw here to let the BSN spawning system run.
  2. the asset gets loaded - this would ensure the asset gets picked up, and BSN spawning system uses that asset. If you're not using an asset this isn't relevant. (does the BSN spawning system run after AssetServer::handle_internal_asset_events?). This is also not something we can do from user space.
  3. For your particular case, after BSN spawning runs, using the observer
#

In the worst case, you may need all three of these

somber rivet
#

I'm not sure I totally understand what "on" is doing here:

fn player() -> impl Scene {
    (
        Sprite::patch(|sprite| {
            sprite.image = "player.png".into();
        ),
        on(|jump: On<Jump>| {
            info!("player jumped!");
        })
    )
}
#

I would appreciate an explainer

#

Like I see it's using a closure. Could it also just use a regular observer I define in code? Like could I do

fn player() -> impl Scene {
    (
        Sprite::patch(|sprite| {
            sprite.image = "player.png".into();
        ),
        on(jump)
    )
}

fn jump(
    trigger: Trigger<Jump>,
    ...
) { ... }
somber rivet
#

Oh sick, thank you

#

So basically yes I will be able to do that?

#

on(jump)

#

That also frees up a nice keyword. I wanted to name something "Trigger" in my project as it made the most sense to me but I don't like to conflict with bevy

copper dragon
#

can bsn asset files do anything with components that have generics?
or is that not allowed in spec?

split harness
copper dragon
#

tho maybe we could ignore that for intial versions?

split harness
split harness
#

(until something changes)

copper dragon
silver bramble
#

The Sprite template is currently not implemented as far as I could see?

Is there any example implementation of a component that needs a Handle<X> where the template is used to retrieve the handle?

rare whale
#

@split harness @thick slate Something I noticed today, I can use NotShadowCaster in a BSN template, but not NotShadowReceiver. Complains about it not implementing Clone.

thick slate
split harness
#

I would also avoid calling it a "bsn template", as a Scene is not a Template

rare whale
#

@split harness Another issue I have run into - which I'm not sure is a bsn issue or not - is that I can no longer have a mesh with multiple materials. It used to be that if I inserted more than one material into a mesh entity, it would render each one, as long as they were different types. This is something that I previously used extensively to do outlines using vertex extrusion. However, now it appears that only the last material inserted gets used.

split harness
#

I have vague recollections of this changing from some of the recent renderer changes / optimizations

rare whale
split harness
#

Note that the renderer folks have plans to make materials more "dynamic", and multi-pass is something that should be on their radar there

#

Worth bringing it up in #rendering-dev

rare whale
#

@split harness What I am futzing around with is "gizmoids" which are dynamic meshes generated via a reactive formula:

commands.spawn_scene(bsn! {
    :gizmoid(|_cx: &Cx, builder: &mut ShapeBuilder| {
        builder
            .with_stroke_width(0.5)
            .stroke_rect(Rect::new(-5.0, -5.0, 5.0, 5.0));
    })
    Transform::from_rotation(Quat::from_axis_angle(Vec3::Y, 1.0))
});
#

(This particular example isn't reactive yet, as the cx parameter is unused)

rare whale
# split harness Yup its definitely something we want to continue supporting / multi-pass materia...

Unfortunately, it's currently difficult in BSN to share a mesh handle between entities, unless that mesh comes from a loaded asset - that is, you can make two entities with identical asset paths, but if you are creating a mesh from scratch at runtime like I am, things get complicated. You can make a template() that generates a new mesh handle, but then you have to come up with a way to pass a reference to the handle to the other entity.

#

Creating custom material instances also requires template():

template(|context| {
    Ok(MeshMaterial3d::<UnderlayMaterial>(
        context
            .resource_mut::<Assets<UnderlayMaterial>>()
            .add(UnderlayMaterial { color: RED.with_alpha(0.5).into() }),
    ))
})
split harness
#

I especially think that in combination with "assets as entities" BSN Set-ish functionality will feel pretty good for this (and be reasonably straightforward under the hood)

#

Ex: if a Mesh is "just' an entity with a Mesh component, and a Handle is "just" a "strong reference counted entity handle", we might not even need a special "asset" keyword in BSN Sets

#

But yeah thats "phase 2" stuff. Not planning on building it until the core / minimal functionality has landed

rare whale
#

@split harness Have a look at my latest bsn tomfoolery: #rendering-dev message

split harness
rare whale
# split harness Delightful. Seems to be coming along very nicely 🙂

Just added: now supports line primitives:

:gizmoid(|cx: &Cx, builder: &mut Line3dBuilder| {
    let time = cx.resource::<Time>().elapsed_secs().rem_euclid(TAU);
    let size = time.sin() * 2.0 + 4.0;
    builder.draw_cuboid(Vec3::ZERO, Cuboid::new(size, size, size));
})

The gizmoid() function is generic over the type of builder passed in, so long as it can produce a mesh. If we pass in a Line3dBuilder instead of a ShapeBuilder, we can draw line primitives instead of triangles.
@golden notch @thick slate

#

On my todo list: support for additive blending modes, which is very useful for overlays because lines are often too dim to make out clearly.

#

I don't think I'm up to doing all the fancy screen-space magic that regular gizmos use to draw thick lines 😦

#

There are several advantages that gizmoids have over regular gizmos, all of which have to do with the fact that they are regular retained meshes:

  • You can choose what material you like to use
  • The mesh can be pickable, or have other user components inserted
  • The mesh only gets recalculated when the input params change, other time it's static
#

Although in practice, for picking, I would most likely use an invisible proxy - possibly using an avian collider - rather than a ray cast.

heady latch
#

^ this is something my toolings (like the navmesh editor) would love to use

#

right now I have some weird runtime mesh generation stuff that pretends it's a gizmo so it behaves the same as the line gizmos

#

but it's really ugly

rare whale
#

For picking, I'd want to give the user some slop, particular if the gizmo shape is skinny and hard to pick.

rare whale
thick slate
#

Not that this isn't interesting, but this feels like the wrong channel for protracted discussion?

rare whale
thick slate
#

Probably #rendering-dev 🙂

copper dragon
urban galleon
#

With BSN will modders be able to edit your UI?

dapper sky
#

it'll depend on how much of the UI is tied up in the codebase vs in assets. like with the bulk of XML bevy ui crates there's been.

#

if most ui is hardcoded, assetless bsn! declarations then that'll be difficult/impossible to mod. if styling is entirely tied up in bsn files then it'll be relatively easy (depending on how accessible the files are to a user)

urban galleon
#

Thank you

heady latch
#

I would like to try out the BSN branch in my 0.17 dependencyless project to give feedback, but by now it’s of course outdated.
I assume the bsn branch will be updated to main when the RC is released?

autumn bane
#

btw is there any point making a PR on cart's BSN branch for no_std compliance right now? I happened to start using that branch for a project recently, and the no_std part fails due to a couple stds that snuck in.

Obviously low priority at the moment, so I can just make an issue or PR later.

thick slate
urban galleon
#

No idea

heady latch
#

The macro is just one part of the whole BSN story. It should also be possible author assets in BSN format (AFAIK) on disk

urban galleon
#

Great if it didn’t have user mod-ability I would just vendor current ui framework (if it stopped being supported)

crisp garden
#

BSN assets are arguably the most relevant part of BSN for me. I desperately need to move content out of code, and the bsn macro won't help with that

autumn bane
heady latch
dapper sky
autumn bane
#

I'm hopeful that a minimal asset implementation will be pretty straightforward! Obviously it gets a little... complicated the more syntax you want to support (like arbitrary constructors or associated contants).

rare whale
cerulean mango
#

We’re using bevy_proto_bsn from the editor prototypes repo in the hopes that final bsn will be quite close in its syntax

#

I have really simple inheritance patched on top of it, along with some of our own tooling

#

Overall I think that having the macro is nice, but once you go asset-side you don’t really want to go back. Instant hot-reloads without any compilation are just a game changer for so many things.

crude pawn
#

What do people think about fonts as entities

#

the current font implementation only cleans up unused fontfaces but not the textures for sized fonts

#

so if you do something like:

fn embiggen_font_system(mut query: Query<&mut TextFont>) {
    for mut font in query.iter_mut() {
        font.font_size *= 1.01;
    }
}

it generates a new texture atlas for every sized font every frame without deleting the old ones until you run out of memory

crude pawn
#

Afaik subassets would be too limited, something like:

let font_face: Handle<FontFace> = asset_server.load("fonts/FiraSans-Bold.ttf");

Then later:

let font_with_size_10: Handle<Font> = asset_server.load("fonts/FiraSans-Bold.ttf#10");

would require that the FontFace asset loader already created the size 10 Font asset?
And if you hadn't created a size 16 font in the asset loader, this will fail:

let font_with_size_16: Handle<Font> = asset_server.load("fonts/FiraSans-Bold.ttf#16");

and there is no mechanism for it to create a new sized 16 font subasset as needed later on.

#

I don't know for sure though, maybe it's not as limited as I think.

#

But even if it isn't it that limited, responsive font sizes seem really difficult to deal with like:

let responsive_font_1: Handle<Font> = asset_server.load("fonts/FiraSans-Bold.ttf#10vh");
let responsive_font_2: Handle<Font> = asset_server.load("fonts/FiraSans-Bold.ttf#20%");
white lichen
#

that sounds like just a subcase of that

crude pawn
#

ah right oh I had heard about that, forgotten it though

#

yeah fonts is the most urgent because an app just crashing because you cycle the font size for bouncy text or something is just unforgivable

#

I'm going to hack together a basic implementation to see where the problems are

stoic hazel
#

is bevy_scene2 intended to replace bevy_scene? and will gltf in the future produce impl Scenes?

crude pawn
rare whale
# crude pawn https://github.com/bevyengine/bevy/pull/20966

Unfortunately, I think that font handles are unergonomic; it's cumbersome to have to acquire a handle in order to be able to style a span of text. I would rather see an approach involving some form of ID that allows for tokens to be defined as static consts: that is, I can define MAIN_FONT somewhere in my code and then assign that to text spans wherever I like; somewhere in my app initialization I would register that name and assign a font asset to it.

#

Ideally, MAIN_FONT wouldn't simply be a reference to a single font asset, but rather to an entire font family. This means that if I style a span of text with MAIN_FONT + Bold, it would automatically select the correct asset.

#

Right now, if a user wants to style a text span as bold, they have to know the exact spelling of the asset, and there's no consistent universal standard as to how font variants are named.

crude pawn
#

that could be implemented on top of this approach

#

with this now if you have an entity representing your main font, if you change its font face or size that changes it all for the text in the app

#

we could have some way of associating ids like MAIN_FONT with the font entities somehow

rare whale
# crude pawn that could be implemented on top of this approach

Maybe. You'd have to be smarter about font lifetimes. In my vision, you don't deal with fonts directly, there's a layer of indirection, so the framework has a lot more freedom in how it manages font lifetimes under the hood; however, it also means that it doesn't know precisely when a font is no longer needed, so deciding when to uncache a font atlas entails a heuristic of some kind.

#

I'm thinking that MAIN_FONT would either point to a FiraSans.family.ron asset, or you would manually register the different variants/styles at startup.

crude pawn
#

yes that's the motivation behind making them entities mainly, we can just query for a list of all Font components and them compare them against the list of font atlases and delete any fontatlases that don't have a matching font component

rare whale
#

Some of these ideas were floated in the old lorem ipsum working group

rare whale
#

Anyway, we should probably move this discussion

rare whale
# native mesa Is this an interning lib?

No. It's similar to CowArc, except:

  • Has an option for storing small strings inline
  • Doesn't have an option for borrowing
    The lack of borrowing actually makes it simpler since you don't have to deal with lifetimes.
split harness
#

And World -> BSN is not a particularly high priority for me at the moment

stoic hazel
#

okay. I'm asking because I was looking at this and wondering if it makes sense to do that if BSN lands in 18 and the fix to that issue would land in 18 + I read a comment in the message rename PR that mentioned something like "this scene system being on the way out"

split harness
#

I think its worth designing the api in the context of the BSN data model

#

Rather than the current scene system data model

stoic hazel
#

If I'm understanding the next-gen-scene branch correctly then bsn scenes feature nested scenes much more heavily (one scene per entity?) than current scenes like gltf (which are a world of entities), so pushing some relation from the bottom of the tree to the top-most entity sounds difficult.
I think what would be really powerful, both for making these AnimationPlayer relationships but also for patterns I've seen in the ui examples, would be a way to say "these N scenes/bundles/entities are related with this relationship" and then inserting them at any position in the canonical child-parent hierarchy. If something like that exists or it's been talked about I haven't found it.

split harness
stoic hazel
#
    #[test]
    fn related_spawn() {
        let mut world = World::new();

        #[derive(Component, Default, Debug)]
        #[relationship_target(relationship = LikedBy)]
        struct Likes(Vec<Entity>);

        #[derive(Component, PartialEq, Eq, Debug)]
        #[relationship(relationship_target = Likes)]
        struct LikedBy(pub Entity);

        #[derive(Component)]
        struct A;
        #[derive(Component)]
        struct B;

        let (source, target) = related::relate_bundles::<LikedBy, _, _>();
        let parent = world
            .spawn((
                Name::new("parent"),
                children![(Name::new("alice"), A, source), (Name::new("bob"), B, target),],
            ))
            .id();

        let &[alice, bob] = &world.entity(parent).get::<Children>().unwrap()[..2] else {
            todo!()
        };

        assert_eq!(world.entity(alice).get::<LikedBy>(), Some(&LikedBy(bob)));
    }

or something else that makes it ergonomic to insert these kinds of relationships. I guess the assert is more important than the specific way I got there

silk lava
# stoic hazel ```rust #[test] fn related_spawn() { let mut world = World::new(...

With BSN Entity Names

let parent = world.spawn_scene(
    bsn!(
      #parent,
      Children[
          (#alice, LikedBy[#bob]),
          (#bob, LikedBy[#alice]),
      ],
    )
).id();

Although I guess the Relation syntax would need to support a mix of both Entity Names/ID's for existing Entities and Scenes/Bundles for spawning new Entities. 🤔
or just use them with normal Component syntax, although then you lose the nicety of knowing what is an Relation at-a-glance.

stoic hazel
#

oh this is how entitypaths/ids are meant to work? could you do something like

bsn!(
  #my_entity
  [(:scene_relates_to(#my_entity)]
)

or

let my_entity = magic();
bsn!(
  {my_entity}
  [(:scene_relates_to({my_entity})]
)
rare whale
#

@split harness Here's a partly working example of how we could get rid of all that crap in the ui_widgets demo:

fn button(on_activate: CallbackTemplate<In<Activate>>) -> impl Scene {
    bsn! {
        Node {
            width: Val::Px(150.0),
            height: Val::Px(65.0),
            border: UiRect::all(Val::Px(5.0)),
            justify_content: JustifyContent::Center,
            align_items: AlignItems::Center,
        }
        CoreButton {
            on_activate: {on_activate.clone()},
        }
        Hovered::default()
        TabIndex(0)
        BorderColor::all(Color::BLACK)
        BorderRadius::MAX
        insert_dyn(|cx: &Cx| {
            let entity = cx.owner();
            match (
                entity.contains::<InteractionDisabled>(),
                entity.contains::<Pressed>(),
                entity.get::<Hovered>().map(
                    |hovered| hovered.0).unwrap_or(false),
            ) {
                (true, _, _) => NORMAL_BUTTON,
                (false, true, _) => PRESSED_BUTTON,
                (false, false, true) => HOVERED_BUTTON,
                _ => NORMAL_BUTTON
            }
        }, BackgroundColor)
        [(
            Text::new("Button")
            TextColor(Color::srgb(0.9, 0.9, 0.9))
            TextShadow::default()
        )]
    }
}

I say "partly working" because it doesn't react to component removals.

#

Within the insert_dyn block, we are grabbing cx.owner(), which is of type TrackedEntityRef.

#

This is like an EntityRef which also records access to components (currently only supports two methods).

#

So the key problem, which we have wrestled with before, is detecting removals. That is, I can easily detect whether a component has been removed, but not whether it has been newly removed.

#

The code for detecting component changes currently looks like this:

self.component_deps.iter().any(|(e, c)| {
    world.get_entity(*e).is_ok_and(|e| {
        e.get_change_ticks_by_id(*c)
            .map(|ct| ct.is_changed(self.tick, tick))
            .unwrap_or(false)
    })
})
#

If I change the .unwrap_or() call to true, then the reaction never converges, because it continues to react to old removals.

#

Could this be done with observers or hooks instead? Honestly, I can't think of a way to do it, at least not in a way that is any kind of efficient.

#

There's a fundamental tension that we haven't resolved: observers like marker components but hate mutation; change ticks like mutation but hate markers. You're damned if you do and damned if you don't.

silk lava
#

Observers could be made to work with mutable mutation, wouldn't be that hard honestly. I've mentioned before, but you just make a new QueryData wrapper or change Mut<T> to emit events as part of its command forwarding to the Query in apply/queue. Although I know cart wanted opt-in change detection first.

rare whale
silver bramble
split harness
split harness
# silk lava Observers could be made to work with mutable mutation, wouldn't be that hard hon...

Yeah definitely doable, but tracking those mutations in Mut introduces overhead and threading concerns if enabled by default. And using a different opt-in query param makes it error prone. We could add that behavior statically as part of the component derive (making the overhead + other tradeoffs opt-in), but that also requires the author to anticipate those downstream use cases, and they must be willing to "pay the price" across all use cases

rare whale
split harness
#

Not to mention that the "may have false positives" aspect of it would probably be lost on some people / thats hard to document

rare whale
split harness
golden notch
silk lava
silk lava
# split harness Yeah definitely _doable_, but tracking those mutations in Mut introduces overhea...

True, I see it like:
User wants Detection: React<C> query param wrapper
Author wants Detection: Component derive built-in.
I dont know what other path you could take, gotta be provided by the User or Author(or both?).

Thinking about it more though, I don't think Query commands are considered by the auto-sync-point in Schedules, so system_b.after(system_a) wouldn't see the results of any Query commands. 🤔
If it did, almost all systems would be exclusive 😢

copper dragon
#

what was the reason for not having comma seperation in the bsn macro?

#

intuitively id expect to have comma seperation
even copilot wont shutup about expecting a comma lol

silver bramble
#

Is ordering of the observer execution (similar to system ordering) possible?

I encountered a few instances where that would have been very useful.

Instead I had to opt into one observer which then triggers other observers in the right order.

copper dragon
wheat remnant
#

They don't even make it into the system graph, they run in place when the commands that trigger them run (or immediately if you trigger with &mut World), since they never get collected there's currently no oppoturnity where you could reasonably order them.

silk lava
#

With the Event/Trigger rework you can have youre own ordering, although you only have access to the ObserverRunner currently, if the Observer Entity were stored alongside you could potentially lookup the Observer Entity for metadata on ordering/sorting.

quartz sleet
# copper dragon what was the reason for not having comma seperation in the bsn macro?

It's still in the bikeshedding phase iiuc. Cart's pretty strongly in favor of "less punctuation = less clutter" while the other camp is "make it look more like Rust". Last I checked, Cart's official position is "I could be persuaded but it's one of the least important design decisions at the moment". My read is that the discussion will eventually need to be had, but it shouldn't be frontloaded. It's mostly just an opinion thing that might ultimately boil down to a poll, no reason to waste developer hours debating it when there are so many other more productive discussions to be had in the space.

Don't want to put words in Cart's mouth, this is just my understanding.

#

Although the Copilot thing is a point in favor of commas that I hadn't seen brought up ||unless you want to encourage people not to use LLMs with Bevy||

rare whale
quartz sleet
#

Agreed. The joke was probably in poor taste in retrospect.

rare whale
#

I'm just a cynic

slender lion
#

designing apis with a preference for llms instead of humans doesn't sound like a great road to go down 🙃

quartz sleet
#

The question is less about ensuring LLMs work properly, it's about ensuring the out-of-box experience is good, and for better or for worse, the out-of-box experience for some devs includes LLMs.

rare whale
heady latch
keen patio
keen patio
#

As for bsn!, only adding commas won't make it fully rustfmt-compliant.
After playing around with it a bit:

  • The # prefix for names is not valid syntax - only works if it can be parsed as an attribute, options that do work:
    • Replace # with a token that can precede an expression syntactically.
    • Use a different way to name entities, where the name is not part of the list of entries, for example using an assignment expr: MyEntityName = (ComponentA, ComponentB, [ <children> ])
    • Skip shorthand and use Name("...") instead
  • @-prefix for templates is not valid for the same reason as the name prefix - @ can't precede an expression syntactically
  • :-prefix for inheritance suffers from the same issue

All of the above could be solved using tokens that are allowed to precede expressions, for example: !, -, *, &. But those come with confusion/ambiguities as they are also used within the expressions.

Designing BSN around what is valid rust syntax may be a footgun for this reason. Instead we could add a formatter to bevy-cli, like Dixous does with dx fmt 🕵️

copper dragon
slender lion
heady latch
#

It’s just something to keep in mind imo. The relevant data point is "statistically, in similar contexts, a comma is usually included". Let’s not forget that behind all the smoke and mirrors and big talks by CEOs LLMs are just statistical models.
Obviously no one wants to base the design on LLM output, I don’t think that’s seriously on the table 🙂
But I also don’t think we should ignore statistical insights just because they happen to come from an LLM. Just treat it as a tidbit of information about how past languages were designed that we can deviate from if we want.

slender lion
karmic rock
#

Can’t leave JSON out which is much more widespread

autumn bane
#

okay but JSON commas suck haha

#

(for humans anyway, not parsers)

#

Of course there is value to precedent! Although I'm not ready to call BSN's commas a bad idea yet. I value the concise authoring, which I expect to do a lot of.

copper dragon
#

Comma separation just makes sense to me, like if we take bsn, subtract rustfmt's attempts to space things, we end up with stuff like

bsn! {
    Node {} BackgroundColor()Hovered::default()TabIndex()
}```
Atleast having commas you get 
```rs 
bsn! {
    Node {},BackgroundColor(),Hovered::default(),TabIndex(),
}```
karmic rock
#

For an apples to apples comparison, also remove the spaces from the commas example

#

Though I don't think picking the worst possible usage of a syntax should be the guiding example, nobody is going to do that

copper dragon
karmic rock
#

What I did is make commas optional unless it's a plain list of values, so

Position: {10 20} // NOK
Position: {10, 20} // OK
Position: {x: 10 y: 20} // OK
Position: {x: 10, y: 20} // OK
#

which means you can do this, which feels natural

Position: {
  x: 10
  y: 20
}
lime veldt
#

personally I prefer "one correct way" so it is not a point of confusion for new users looking at their code, example code and code from people that try to help (and might suspect a missing comma is an issue themselves as well)

karmic rock
#

"one correct way" and "succinct" are often at odds, going extreme in either direction IMO ends up with less than ideal syntax unless it's really simple (like JSON)

lime veldt
#

certainly the amount of spaces should not matter for example

rare whale
#

Right now the BSN syntax has more serious problems: it has an ambiguous grammar. The parser cannot reliably distinguish between:

  • Marker [children]
  • Relation [related]
silk lava
autumn bane
#

I feel that's far too verbose.

fallow cloak
#

and implicit Children is nice in a lot of cases

silk lava
autumn bane
#

Will you?

silk lava
fallow cloak
#

maybe in some cases you'd use mostly custom relations, but UI for example?

#

or plain 3d scenes?

autumn bane
#

At least in my own UI code, where I fairly often use custom relations, the vast majority of relation bundles are just children.

#

In other words, heavily hierarchical structures like UI also tend to be the most verbose hand-authored things. I would really appreciate some concise syntax personally!

silk lava
#

Concise to you, is implicit and magical to others, though.

autumn bane
#

I think there's a balance to be struck, but I don't feel that some concise syntax for Children is too far. It's pretty fundamental.

silk lava
#

Thought experiment: how do you remove Children from Bevy and replace it with you're own Parent/Child Relation? Its relevant because this implicit-ness further intrenches the Children type to not be able to replaced.

autumn bane
#

I don't see how that's particularly valuable, personally. You'd just fall back to the standard relationship syntax in that case.

#

Being everything to everyone is not always ideal since you can't take shortcuts.

silk lava
#

Hmm fair enough, but I am still on the explicit syntax side, for what its worth. 👍

frosty shell
#

I don't mind Children having special notation, I just don't like that it's ambiguous

autumn bane
#

right that's definitely not ideal imo

#

Even just a single sigil would help; in the scenario where you find out that Children actually isn't what you want, you could pretty easily find/replace, similar to the general syntax. The current implicit approach denies this.

fallow cloak
#

could also require some extra syntax between relation name/items: MyRelation: [], MyRelation=[]

#

cart said he didn't like that so much, just mentioning alternatives

karmic rock
#

The way I went about it is

parent { // implicit hierarchy
  child_a {}
}

child_b {
  (ChildOf, parent) // explicit hierarchy
}

// 'implicit' hierarchy for other relationships
(IsA, Thing) {
  (IsA, Organism) {
    (IsA, Plant) {
      Tree {}
    }
    (IsA, Animal) {
      Human {}
    }
  }
}
autumn bane
#

that seems especially nice because the explicit syntax closely mirrors what a relation is in flecs and also how it's declared in normal code (as far as i understand). idk if we can do that though blobthink

karmic rock
frosty shell
#

::<ChildOf>(parent) in bsn, clearly

karmic rock
#

it's concise, consistent, clear and unambiguous

karmic rock
rare whale
#

I suppose that requiring mandatory commas between components would resolve the ambiguity:

  • Marker, [children]
  • Relation [related]
autumn bane
#

oh i suppose so

silk lava
#
bsn_list! {
    (#parent),
    (#child, ChildOf(#parent)),
} 

I think fragmented-relations would look like normal Relations, even in BSN 🧠

karmic rock
#

What is the #?

silk lava
#

Name shorthand, but I think it could also be used as an Entity Variable.

karmic rock
#

Oh lol, that's funny. In my case # indicates an entity id, like #500

silk lava
#

Why not just use raw numbers, just like a unit-prefix I guess?

karmic rock
#

Because entities can have numbers as names

silk lava
#

Why does that prevent you from just using raw numbers 500 as an EntityID? #500 for an entity with Name("500") and raw numbers for providing an EntityID (ChildOf, 500)

karmic rock
#
500 { // entity with name "500"
}
silk lava
#

Ah right, you dont use # for names lol

karmic rock
#
#500 { // entity with id 500
}
silk lava
#

Hmm interesting, I think I prefer #name syntax, and leaving 500 as an ID/number.

karmic rock
#

The reason is also API consistency. Identifiers are looked up with the lookup function, so:

world.lookup("foo"); // find entity with name foo
world.lookup("500"); // find entity with name 500
world.lookup("#500"); // return entity with id 500
#

I used to treat numbers as entity ids, but that's annoying since a number can take lots of different shapes

karmic rock
#

It's like having to prefix every identifier in rust with #

karmic rock
copper dragon
karmic rock
silk lava
queen oak
#

I think its a cost worth paying to teach and have a special syntax for parent children

karmic rock
silk lava
karmic rock
karmic rock
autumn bane
karmic rock
#

It's a language that's specifically designed for games/scenes, and those tend to be hierarchy heavy

silk lava
silk lava
autumn bane
#

Anyway, I don't think it's really worth worrying whether Children is important or special. It obviously is; it's been that way in Bevy for years, and in other game engines for basically as long as they've been around.

somber rivet
#

Parent/child relationships are often given special presentation. For example bevy_inspector_egui, the gameobject inspector for Unity, and the node inspector for Godot all privilege this kind of relationship.

silk lava
silk lava
karmic rock
#

(which is what generic graph frameworks do, but jikes :p)

rare whale
#

The example I like to use for alternate relations is a treasure chest in an RPG: the chest "contains" items, and is arguably a parent, but we don't want to use the regular ChildOf relation - because we don't want to render the contents of the chest in the game world. The words "parent" and "child" are an imprecise metaphor (the parent didn't literally give birth to the child), and are typically used in software to define hierarchies generically without any implied semantics. (I think the source of the metaphor is the structure of geneological ancestry trees).

In Bevy, the "child" relation does have implied semantics, although the meaning is slightly different depending on whether we're talking 3d or UI (3d children have no layout behavior for example). Picking also treats the child relation as special.

lethal juniper
#

When I'm developing, I like to group things together using parent/child relations to scope things together, like if I have a tilemap, I like to make all tiles for that tilemap as child of that tilemap, so it is easy to query and to reason about. Also this looks great on Editors,

#

All this to say that ChildOf should have a special notation, since it will be the default/most used relationship. The point is, as the other said, this default can't be ambiguous

silk lava
karmic rock
#

Then we will never agree xD

somber rivet
#

I think there's a heuristic programmers use where general, consistent solutions to problems are favored over bespoke solutions. There would be a certain conceptual elegance to treating all relationships the same. It would satisfy the afformentioned heuristic, but I think this is a time where that heuristic fails and a special case is favorable. I think this is thoroughly agree-to-disagree territory tho

copper dragon
#

We could settle this real quick

Let's just ask a bunch of non bevy related people what looks better/makes more sense/has better clarity.
I think our bevy centric knowledge is clouding the objective discussion on the syntactic clarity of bsn

somber rivet
#

Side note: I know its goofy to talk about 0.18 at this point but if 0.18 dropped next month with just the bsn! macro I'd be stoked. It seems like the bevy dev cycles are getting longer and I don't want to wait 6 months for bsn (even if it comes with 10 other cool features) if it's actually working on main this time next week. That said I don't know what the actual status on any of this is, so consider this 'just yapping'

silk lava
autumn bane
#

I think the Bevy user perspective is important! We're the ones using it after all haha

copper dragon
karmic rock
#

This is a thoroughly explored design space

split harness
split harness
copper dragon
split harness
split harness
copper dragon
split harness
silk lava
# silk lava Just curious, but in flecs how would you do this, if a different Relation needed...

So right now if you have ChildOf on an Entity you get a set of behaviors, and there is no way to pick and choose.
In ECS, you would break apart that monolithic Component/Relation into multiple, so that the existence of the data is the existence of behavior.
The more things you add to children the more monolithic it becomes, and more knobs you need to adjust to get acceptable behavior (this is OOP)

copper dragon
queen oak
split harness
#

I'm doing release prep atm

split harness
#

Ex: do you expect A or B?

// A
fn x() {
}
fn y() {
}

// B
fn x() {
},
fn y() {
}
#

In most languages, things at that level are separated with spaces, newlines, or ;

#

And ; is clearly a bad fit for this context

frosty shell
#

I think of a bsn entity spec moreso as parameters, so , works in my mind

white lichen
#

i think this is a place where BDFL power is acceptable to use btw. whatever cart says goes on these minor syntactical debates imo

#

obv that doesnt mean dont question it

#

but like im pretty happy not needing to have an opinion on this lol

frosty shell
#

as long as the grammar ambiguity w.r.t children is solved some way, I don't mind which way

silk lava
split harness
split harness
# copper dragon But it is enclosed? By `{}`

That is compatible with my statement that aren't enclosed in [] or ()
From a rust perspective, {} context and fully unenclosed context are essentially the same from a "syntax expectations" perspective

silk lava
#

Hmm what about ; then 🤔

split harness
#

My personal feeling is that it makes me incredibly sad

karmic rock
#

Name lookups for other relationships don't really make sense, you'd get multiple unique names per entity, potential name clashes when you lookup using a different relationship etc. Messy

keen patio
split harness
#

Not to mention that developers should be able to run rustfmt and bsnfmt whenever they want manually without needing to worry about order of operations

keen patio
#

Good points! 👍

quartz sleet
#

lol didn't mean to kick off a comma war. It did remind me though: can we expect ChildOf(x) to work properly in bsn? My biggest gripe with bsn is that it encourages nested hierarchies which traditionally wreak havoc with most diff tools. It'd be nice if I could phrase my bsn as:

EntityA(A, B, C)
EntityB(D, E, ChildOf(entity_a))
#

It's one of the reasons I like godot's scene format, even though it's not exactly the poster child for "human editable"

somber rivet
#

That's how the current scene format works right?

split harness
#

See the #Name proposal

#

That would only really make sense in the context of a bsn_list!, and only one that isn't passed in like this: Children [ some_bsn_list ]

#

As bsn! assumes a single root

#

And every other entity defined in bsn! is already a child of something

quartz sleet
#

I'm thinking of the asset, will a .bsn file be a bsn_list! or a bsn!?

split harness
heady latch
#

Cart, when you have time to work on the BSN branch again, would you mind maintaining a branch that uses 0.17.0 and not 0.18.0-dev? I believe quite a few people, me included, would like to beta test BSN the coming cycle, and this way we don’t have to patch all our dependencies to point to 0.18.0-dev or fork your branch ourselves 🙂

split harness
#

I will give it a try, but I also don't want to get bogged down by it. My first priority is landing it in 0.18

white lichen
#

i think just having the current bsn branch updated for the 0.17 release would be enough, doesnt need to track further developments as it gets closer to landing in .18

heady latch
split harness
rare whale
split harness
#

Can't wait to be back on the BSN bandwagon

heady latch
queen oak
#

Lmao we're gonna fork the 0.17 ecosystem

#

"Is it 0.17 with bsn or without?"

heady latch
crude pawn
#

so "align_items" -> "align_children" and "AlignItems" -> "AlignChildren"

#

It would make it slightly more awkward to translate from CSS to bevy style definitions but I'm not sure many people do that anyway

#

it feels to me like there is a lot of confusion among users because we use the flex "items" and "containers" terminology a lot

#

that might be cleared up if we uniformly replace them with "children" and "parents"

crude pawn
#

oh no, spent the last hour trying to debug the rem PR:pub fn rem<T: ValNum>(value: T) -> Val { Val::Px(value.val_num_f32()) } 😅

heady latch
#

Not saying this is a counterpoint to a rename, just mentioning I exist 😄

crude pawn
#

Yeah I guess I have done it a lot as well 😄

#

I guess I think that understanding that "items" means "children" is obvious to users well versed in CSS, but not so much to anyone else

silver bramble
#

Yeah the alignments in CSS is very confusing and gladly we have hotpatching now so I can just try the different elements.

But keeping it the same as CSS is probably the right path unless its planned to completely break loose from css in multiple instances, then changing the naming would make it obvious that its not meant to be used as a direct reference.

thick slate
split harness
#

Yeah I think there is interest in alternative layout algorithms, so my vote is we keep it as-is for now, document the correlation to children, and use "children" if we adopt an alternative algorithm

golden notch
#

see the argument here re: standards. i am no css lover by any means, but i think some people undersell the benefits of being css based? like for all the problems of css, there is practically infinite css learning material and content in a way that a custom layout system will never match. absent other alternatives, i think it’s worth leaning into rather than trying to obscure imo. if we can definitively do better than web tech in a way that fits our idioms, by all means, let’s. but being based on the most well understood and widely distributed platform ever is a strength not a weakness

thick slate
#

(and there's a fallback / alternative for the cases and users where it doesn't work well)

crisp garden
#

Being fully compliant would likely drive people insane (css has many weird behaviors for legacy reasoms after all), but deviatig arbitrarily probably would too.
That said, alternative layout options would be amazing. We have a much improved spawn API now, and BSN should improve this, with good reactivity and an asset version of BSN it might even enable good hot reloading. But even then the guesswork involved in CSS-style layouts is wildly frustrating
-# Morphorm/subform style layouts when?

rare whale
#

It's widely recognized, even among CSS partisans, that some parts of CSS are better than others. I can say this with some justification because recent feature additions to CSS have striven to overcome some of the weaknesses of earlier design decisions. For Bevy, we need to do two things:

  • Adopt the best parts of CSS
  • For those parts where we deviate from the CSS spec, make sure the dividing line is easy to explain and understand.
#

For example, one of the most confusing parts of CSS is the cascade: the rules for prioritizing property overrides based on selector specificity. I don't think that anyone is advocating adopting this part of CSS in Bevy, other than people who are doing third-party crates that support all of CSS in a compatible way.

#

You can think of CSS as having multiple parts:

  • Syntax and Structure
  • Selectors
    • Static
    • Dynamic (psuedo-elements, etc.)
  • Cascade and Specificity
  • The Box Model
  • Layout Systems (flex, grid, block...)
  • Typography and Text
  • Colors and Backgrounds
  • Visual Effects (animate, transition, borders, shadows...)
  • Responsive Design (media queries, viewport meta, flexible units)
  • CSS Functions (min, max, clamp, calc, etc.)
#

So for example, Bevy today does not support CSS syntax or selectors, but does support the box model and layout

quartz sleet
# golden notch see the argument here re: standards. i am no css lover by any means, but i think...

To be fair, there are more options than CSS or NIH. Another of bevy's strengths is our ability to choose newer standards. Webgpu is relatively new, but we're on the front of it. Getting in front of a newer layout standard is an option I would really like: it saves us inventing something, it presumably already has documentation, and it's ideally nice to use. Obviously if no such thing exists, I agree CSS has a lot over a custom solution. Personally one of my favorite things about bevy is its lack of baggage, but UI is kind of the notable exception (I realize there are many reasons for this)

#

My two cents

slender lion
thick slate
#

As a taffy maintainer, I am very open to adding more layout standards, especially established ones that are stable with a nice spec

quartz sleet
# slender lion what other options are there for newer layout standards?

Honestly, I might not really be the best one to be starting that conversation. I don't do much UI, and even less UI framework dev. That's mostly why I added the disclaimer that if no such thing exists, we can stick with what's there. I guess I just assumed there were other options: I know morphorm uses its own algorithm, there are more basic things like Android's old layout system (or its new ConstraintLayout) or Godot's containers, or more recently I've been seeing composition-style layouts like Flutter, SwiftUI, Compose, Elm, etc. I realize those aren't equivalent to CSS, they're only layout algorithms/engines. Tragic backstory to help you understand where that initial comment came from: I just recently was exposed for the first time to what it's like trying to use HTML/CSS raw without any framework on top, and I found the experience surprisingly bad. CSS is happy to conflate layout and theming into the umbrella of "styling", meaning things that should be local (like layout) get thrown into the same pot as things that are more global (like theming). Godot's UI clearly separates them: layout is done by nesting Controls, and things like text color, font size, background image, etc. are nicely encapsulated in "Themes" that propagate downward.

The tl;dr is: I may not have useful advice ||but I do have a dislike of CSS||. I assumed there were more modern algorithms/models that we could take advantage of, but thinking about it more deeply, I realize there's more to it than just "replace the layout engine". If CSS really is the state of the art, I'm sad, and I think maybe a custom solution could win, but I can't really argue against the status quo more effectively than that.

slender lion
quartz sleet
#

And thus the unfortunate conclusion: we use the layout approach that some people really hate, but has infinite learning material and resources available

#

I guess I shouldn't say unfortunate

#

It's only unfortunate for the people that really hate it

slender lion
#

there will always be that group, even if its different people. and the learning material and resources is a big win

quartz sleet
#

But all the other solutions are unfortunate for everyone because they're less discoverable/learnable

golden notch
#

i do think that there’s been a lot of learning in the ui space over the past decade and would rather write ui in 2025 than 2015 but there’s a reason people keep making endless new approaches. i think ui is just kinda hard and uncomfortable in general

quartz sleet
#

Simple solution: just get rid of users 😩

golden notch
slender lion
#

I think enabling alternative solutions to exist is good, but css is also a really good default given many considerations

golden notch
#

btw @thick slate i used taffy on my last contract for a c++ project and it worked great!

#

i convinced them not to implement an ad hoc layout engine lol

karmic rock
#

Otherwise you'll just end up with just another layout API

rare whale
#

I think that any alternate layout algorithm would have to meet a high bar with respect to documentation. The canonical "how to use flexboxes" essay is something like 6 pages of text and illustrations. You're not going to compete with this via a couple of paragraphs in a rustdoc.

quartz sleet
loud mural
#

@tawny stone is morphorm stable enough to be added to taffy?

quartz sleet
#

Not inherently opposed to flexbox

slender lion
karmic rock
thick slate
slender lion
thick slate
loud mural
#

Thanks both of you!

thick slate
slender lion
thick slate
karmic rock
thick slate
#

Which is great for things like "life bar" and "minimap"

rare whale
slender lion
quartz sleet
rare whale
#

It's not that no one has bothered to write a dead-rule detector, it's that you can't write one.

karmic rock
rare whale
thick slate
karmic rock
rare whale
#

This happens more often than you might think

karmic rock
rare whale
#

On the plus side, the set of use cases that CSS encompasses is vast: I would be very surprised if there are any alternate layout algorithms that are a superset of what CSS handles.

slender lion
#

basically "stack to the left", "stack to the right", etc