#Next Generation Scenes
1 messages · Page 6 of 1
yeah, "replaced for layout purposes" might be better phrasing
The first time I read about flexboxes in CSS, my immediate reaction was "why didn't we have this 10 years ago? Every other GUI framework has rows and columns already."
from unity:
Ah, yes, precisely
this reads a bit like "basically flexbox"? is there much of a difference
https://docs.oracle.com/javase/tutorial/uiswing/layout/visual.html - state of the art from 20 years ago
This Swing Java Tutorial describes developing graphical user interfaces (GUIs) for applications and applets using Swing components
Flexbox has confusing labels/attributes imo. I always gave to google
(I wouldn't bother with stretch / any resizing for our anchor impl)
Agreed: being able to say "top-left" is much much easier for beginners, and harder to screw up
I don't think they're the same, but could be wrong
If you look at that Java article, you'll notice that all 8 of the options are just flexboxes or grids (nested in some cases) - IOW, flexbox is just a generalization of what preceded it.
yeah, this anchor thing looks like absolute positioning afaict. The unity page requires you to have an absolutely positioned box, that then children define absolute offsets from.
pre-defined width/height, etc
I'll give you center, but top/left/bottom/right exist in absolute positioning
ah yup, that's true
i've utterly resigned myself to getting the wrong {align,justify}-{content,items} rule at least once every time i need some type of alignment
i think that's the only part of the terminology that consistently wrecks me, everything else like start/end is fine
Same, I work with it almost every week and my brain is unable to remember which is which lol
Maybe it's because I'm not a native english speaker, but justify-anything is not intuive to me at all
fwiw I don't think its a native speaker thing, the names confuse a lot of people, but I also don't really know of better ones for "main axis"/"secondary axis"
main axis or secondary axis would've been better
yeah maybe
another option would've been to add dedicated halign / valign and forego the flex-direction thing
I get why they did it, but it's confusing because it makes everything you do context dependent
I wrote a rant but suffice it to say I would love to drop the dubious naming of CSS flexbox properties even if we keep the concepts. It might have made sense at the time in the context it was made, but it is.... neither elegant nor clear.
While I enjoy knowing the right incantation to center something in bevy_ui, I don't enjoy the part where people not fresh off CSS will 100% have to look up wtf AlignItems vs JustifyContent means.
same
I really like flutter and gtk's layouts systems. They're not as powerful as css, but simpler cases are much easier.
I only speak english… maybe it’s my dyslexia?
maybe aliases, but renaming breaks things in both directions. new bevy_ui users can't use the education and tools that already exist in the world, and css users have to use a brand new slightly-different-but-the-same-maybe? naming scheme.
Maybe a better model would be to have a small number of common layout options with intuitive names, and for everything else heavily rely on calc
having "layout components" is pretty common in the web world, even for things like Flex, etc. Someone could create <Grid>, etc for preset layouts
examples for those unfamiliar (Grid, SimpleGrid, Stack, etc): https://chakra-ui.com/docs/components/grid
i've been happily using helper functions to avoid working in terms of align-items and justify-content: https://github.com/benfrankel/pyri_new_jam/blob/main/src/util/extend/node.rs
This is pretty easy to do with BSN
In fact, part of my goal for feathers - which I prototyped, but which did not make the 0.18 cut - was to start working on a set of "standard containers"
Another point I want to make about CSS, is that some of the confusion occurs when we have gaps where we are missing a feature of CSS that people would expect to be there. Working around the missing piece often requires diving deeper into the guts of layout to find an alternate solution.
More specifically, one could imagine a collection of "mixin" templates which patch the layout properties to get various standard behaviors - like "stack" to get a flex column of align-stretched items.
yeah, the point there was definitely "we don't need to rewrite the layout implementation to provide high-level apis"
I feel CSS alignment comes with all the baggage of CSS history, I think it's better for Bevy UI to be its own thing and well documented in the new book (yes take inspiration from existing systems, but don't be trapped by it)
I feel really comfortable with CSS nowadays, but it's very easy to forget how much of a struggle it was to work with initially
Interesting. How did you do the FFI? A lot of people have been asking for C/C++ bindings to Taffy, but I haven't gotten around to making them.
I've been thinking about it as a small extension to absolute positioning that allows you to choose which part of the box the absolute position applies to (although CSS has it's own anchor positioning which is way more complex)
(aside: but as it happens I'm currently implementing floats in Taffy+Parley. Turns out 73% of websites still use them)
FWIW, my latest thinking on how to integrate a "morphorm-like" (which is also GTK-like, Iced-like, etc) layout mode into Taffy, is to stick with the CSS box model (padding/margin/border) but to add a new unit that is defined as "a proportion of available space" (kinda like the fr unit in CSS grid, but which can be used for width, height, etc. The aim would be to create a mode that could potentially be upstreamed back to the web (and implemented in actual web browsers).
There are some complications around things like wrapping (flex-wrap), because if you have a container with wrapped contents then it's not obvious how you ought to define "remaining available space"
But I think these are solvable
Bevy could probably get away with just renaming the alignment properties (and seperating the Flexbox and CSS grid ones). IMO CSS properties are confusing mostly because they try to abstract over Flexbox coordinates (main and cross axes) and CSS Grid coordinates (inline and block axes). That's how you end up with "align" and "justify" that don't intuively mean anything to anyone. I think if had align_main and align_cross for flexbox and align_inline and align_block (or even align_x and align_y if we don't care about vertical writing modes) for CSS Grid, then it would be pretty intuitive.
I guess perhaps we would also need to make the "content" (rows/columns) vs "items" (children within each row/column) distinction clearer.
just hand wrote them to cover what we needed, it really wasn’t that bad, maybe 1k loc
Is there something planned for gather data from children on spawn?
The event hocks
pub fn on_add(mut world: DeferredWorld, ctx: HookContext)
seem to not have the children setup when the hook is executed.
With singleton entities as resources are we gonna have resources be setable in bsn files?
Just FYI, the latest version of morphorm now implements the auto layout strategy from figma, because I really like designing with it. The implementation is pretty much complete, but the documentation is lacking. There is a guide in the vizia book though. To be honest I don't know what this means for the future of any taffy integration.
My friend just heard BSN and decided it can be pronounced Bassoon
What are the odds that can be the official pronunciation of the format name? 
everyone already hates me for calling it "b-scene"
"bee ess ennn" doesn't roll of the tongue nearly as well imo
everyone calls it bee ess enn
You've lost this battle heh
and critically "bee ess enn" means nothing on its own
I posted some thoughts on what a binary BSN might look like in bevy discussions; I proposed bsb (Bevy Scene Binary) for the binary format, and fortunately that file extension does not appear to be used by anything.
Eh, just wait till he changes the letters for the macro bscene! 😂
heheheh just wait until i write the BSN release notes. then everyone will know the truth
obligatory: "this is an open conversation and I'm not a dictator unless I have to be"
obligatory: the board could depose me at a moment's notice
So here's a story: when I was working on Faery Tale 2, I came up with the term "knowledge packages" to represent mixin behaviors for character dialogues; but someone started calling them "knowledge packets" which was one less syllable, and nothing I could do (even with my power as creative director of the project) could turn the tide.
packets, what are they networked dialogues? 🤔
Yes, my reaction precisely
The story of King Canute and the tide is an apocryphal anecdote meant to illustrate the piety or humility of King Canute the Great (also written as Cnut), recorded in the 12th century by Henry of Huntingdon.
In the story, Canute demonstrates to his flattering courtiers that he has no control over the elements (the incoming tide), explaining that...
I can see the Faery Tale 2 design influences on some of your recent UI / isometric work (or perhaps, your influences on Faery Tale 2). The designs hold up quite nicely to this day.
My recent work is actually a formalization of something I previously did subconsciously. I now know much more art history than I did back then, and am deliberately cribbing from artists like Alphonse Mucha.
OK this is officially OT 🙂
The same friend just said "b-scene, not heard. Because that name will be seen and not heard"
"In the meantime, Bassoon is an instrumental name."
It will be done "Real Bas-Soon now"
we can't actually know this for sure (but I think you're right)
@heady sierra get in here
Cart, is it possible to trigger hooks on Components added to a Scene when building it, after the Scene is ready.
Currently, wherever you spawn the Scene Root, that structure is ignored via hooks because they are run while the Scene is still building itself, which is less then optimal for certain use-cases.
Context: #1351602963352911963 message
you might want to clarify further 😅 "when building it" could mean when constructing the Scene World or when instantiating it into the main world and mapping entities, etc
The issue in that thread is that you can't iter_ancestors through the top of the Scene's root into the entity with the SceneRoot in a Component hook or observer, because the hooks fire between when the scene starts instantiating and before its parented to the SceneRoot entity
Yeah, I dont know the terminology or lifecycle as well as I should, but the above code is ergonomic and I expected it to work, to find out that it actually doesn't seems like a bug from my perspective.
The specific issue that started the conversation is that this line of code sets the parent after the scene is fully spawned: https://github.com/bevyengine/bevy/blob/227c2222e5d5643929e4a09789491a7e4ef9952b/crates/bevy_scene/src/scene_spawner.rs#L487
The behavior difference would be something like to set the parent after the scene root (not the SceneRoot, but the root node in the scene) has been spawned instead. Currently the code scans for "an entity without a parent" to represent the scene's root node, then sets ChildOf on it, so it would require digging into the scene to find that node earlier.
But scenes are always meant to have a single root entity right? So theoretically you could spawn that entity before hand and just map it later?
I agree that the "trigger event when scene is ready" scenario needs to be covered. We've discussed this in the past, and I think this will either come down to:
- introduce a new "scene ready" event, which fires post-scene-init (ex: children are present, as are all other components)
- reframe scene init to ensure children are spawned first
Currently, entity spawning is done "top down", but "bottom up" makes way more sense for some scenarios
And notably, other engines have equivalents
- Introduces potential multi-event requirements, if what you care about is an OnAdd Event, having to care about SceneIsReady is extra overhead to using Scenes.
- This would only work for Children, so other Relations would still fail. (yet another Children specialization 😢 )
(2) is a matter of phrasing. In theory this could be (and probably should be) generalized to any "sub entities"
That could work then 👍
Although while that will allow intra-scene availability for hooks, it doesnt(?) allow inter-scene/world availability for hooks, right?
they do, and its not the SceneRoot entity 😆
given that higher level entities are generally "orchestrators" of lower-level entities, we need an "onready" event to facilitate that behavior
Right, but that Entity will always be parented/related to the SceneRoot entity.
Depends on the scenario i guess
that's only accurate if you're using the SceneRoot method of spawning, which is not a requirement
This might be "onadd" but thats an open question imo
Agree, but if it works for 2/3 then 1/3 is still not working. kek
We have a way to avoid hooks for relations right? Could extend that to all components, and then manually call the hooks afterwards, probably not the right choice though 😂
@split harness I have some BSN questions:
- Do you have an ETA for when there will be a new branch? I realize that you're probably recovering from release madness, and it will take you some time to catch up on all the feedback in the PR comments.
- Don't feel that you need to do the feathers migration yourself; I can do it if you prefer. Your PoC is already validated so you don't need to do the migration again (unless you really want to).
- There is a question about whether we should provide a non-BSN way to use feathers. For example,
bevy_immediatehas demonstrated an ability to spawn and control feathers widgets, and while the @cloud fog says that they can potentially work with BSN, I suspect it will be easier for them to work with components directly. If that's the case, then I presume that we want the "nicer name" to be the BSN-compatible one.- If BSN ends up supporting "structs as templates" I would like the "nicer name" to be the PascalCase spelling:
:Buttonrather than:button. - This means we'll need to come up with a "less nice" name for the non-BSN alternative functions.
- If BSN ends up supporting "structs as templates" I would like the "nicer name" to be the PascalCase spelling:
Do you have an ETA for when there will be a new branch?
I'd like to sort this out this week, provided nothing critical comes up to derail me
Don't feel that you need to do the feathers migration yourself; I can do it if you prefer.
Cool cool. I might farm this out, although porting is a great way to ensure everything is working / to get more familiar with the current state of things.
There is a question about whether we should provide a non-BSN way to use feathers
I'm not immediately opposed to this, although I think this risks compromising (or at least, complicating) the data model. If we have a "raw component" version of feathers (ex: defined as a Bundle), that would "hide" those components from being patched.
This means we'll need to come up with a "less nice" name for the non-BSN alternative functions.
If / when we addPascalCasesupport, it would be becauseButtonexists explicitly as a Rust type that correlates to the BSN functionality. That would free upbutton()to be used by the raw version.
that would "hide" those components from being patched.
I meant to qualify this with "if we use the raw component version inside the BSN version". We could of course fully duplicate the declaration across the two paradigms.
Yeah I was thinking of duplicating the declarations. The declarations aren't that big, and most of the code is in the dynamic style systems which wouldn't be duplicated.
This is me throwing out an idea as an uninformed outsider but
With the new upcoming nightly rust reflection syntax
Couldn’t there be some automatic duplication/generation of BSN <-> code?
I think, generally speaking, anything that's possible with the nightly apis could be done today with bevy_reflect
I might've missed something in the upstream api, but I was pretty sure its the type id/type info data and such, which we already have available.
yeah this
yep that's the one I was thinking of
Yeah, doesnt really unlock anything new I dont think, except that bevy_reflect can be less heavy by using this as a backing.
#ecs-dev will probably use this for something at some point
But yeah, I'm not super excited about it for Bevy itself
reducing macro code-gen could be a significant boost to compile times
Interestingly Bevy is mentioned as the example. Are those things meant to be solved by the MVP or future work?
If this experiment is successful, crates like bevy will be able to "just work" with arbitrary types instead of requiring authors to #[derive(Component)], #[derive(Bundle)], or #[derive(Resource)] their types just to get the bevy_reflect information built at compile-time. Crates like bevy_reflect and facet will still exist, but only as different libraries with different goals and methods for exposing reflection information.
https://rust-lang.github.io/rust-project-goals/2025h2/reflection-and-comptime.html
Future work! The hope is to gradually reduce the amount that we need to maintain by finding better solutions upstream
I like the idea but I honestly like having to have that, it makes the usage clear that "this is a component, this is a resource" etc
Yeah #[derive(Component)] seems neat to have. #[reflect(Component)] on the other hand is something I want to see gone if possible.
Yea manually flagging for reflection is the greater problem
I resisted reflection with editor for awhile cause forcing users to add reflect to everything sounded antipattern
We can remove the #[reflect(_)] requirement for some known set of traits using autoderef specialization if needed. Last time I've tried it I ran into the usual problem of it not working well with generics, but other than that there shouldn't be any other problems with that approach. Removing it for all traits would require real reflection with trait support, which we'll hopefully get someday
Going to go OT for a little bit: having worked in both the games industry, and the animated film industry (during my time at Nimble Collective), I find it interesting the way that the word "scene" means wildly different things in each of those fields despite the fact that both are based heavily on 3d computer graphics.
In the games industry, a "scene" is generally an adjective that is part of the term "scene graph", which I think everyone here understands.
In companies like Pixar and DreamWorks, however, "scene" is a unit of temporality and storyboarding: That is, films are composed of frames, which are grouped into shots, which in turn are grouped into scenes. A "shot" is a single contiguous "take" in which there are no jump cuts; A "scene" is a sequence of shots which take place in the same location or involve the same characters, and may have jump cuts between shots.
(I'm not making an argument here, just pointing out something I think is interesting)
i wonder if eventually someone will try to encode some sort of shot sequence into a bsn scene
Wouldn't the Scene->Shots->Frames be a scene graph? maybe not
It could, however, I think workers in those industries tend to think in terms of more classical film editing techniques. Shots are storyboarded and rendered, and then the rendered frames are assembled after the fact. The need to switch camera positions in mid-playback is more of a game engine cut scene kind of feature.
(There's that word "scene" again)
The more similar term is Mise-en-scène.
(Also sorry for the nitpick but in general film nomenclature a scene is a unit of story that doesn't necessarily imply the same time nor space. E.g. in Tenet when they are setting up the heist they have the same conversation across n different locations (do they suffer from short term memory loss???). The other way is usually true though that the same time and space implies the same scene.)
I've ported the BSN branch to Bevy 0.17.2 (including feathers):
https://github.com/bevyengine/bevy/pull/20158#issuecomment-3367790614
@rare whale: to keep the complexity of the port down (and ensure this branch is as "patchable" to 0.17 as possible), I did not include the "newer" feathers widgets that 0.17 didn't ship.
Also: theres currently two bugs to resolve:
- The initial state of the color swatch is not synced to the "swatch color resource" when first spawned. Things "snap" to the correct value after dragging once.
- The "alpha texture" is not rendering correctly
You may want to do a dev-announcement about this at some point
Since I know quite a few people are burning to try the bsn! macro out in their stuff
checking out the feathers example, I don't love how there's a visual mix between lines that want to end in , and lines that don't
and Rust not allowing ..default(), is not exactly helping 
How come Node has implicit ..default(), but ButtonProps seemingly does not?
Is that a technical or a stylistic limitation? Is it related to the fact that one is used as a component and one as a parameter?
hmm GitHub's syntax highlighting is not liking this else here
But I love how much more concise this all is 😄
thanks for making sure this is patchable to 0.17, excited to port the rerecast code to BSN and give practical feedback then 🙂
ah, while I'm on it, how do you envision these kinds of observers? Should they be inlined as shown here or in an on method? Obviously if they're hundreds of lines long they will be better off in an own function, but would you generally recommend something of this size to be inlined in the rest of the UI BSN?
Next question, how come here we need to say Children before the [
But not here?
Sorry if all of this was already mentioned before 😄
Just trying to understand BSN better
I'm seeing a few other problems:
- color slider gradients not updating until first drag
- color slider thumb positions not updating until first drag
- lint warnings in virtual_keyboard widget:
--> crates/bevy_feathers/src/controls/virtual_keyboard.rs:53:15
|
53 | {key_row}
| ^ ^
|
= note: `#[warn(unused_braces)]` on by default
help: remove these braces
|
53 - {key_row}
53 + key_row
|
The first two appear to be system ordering issues, trying to untangle them now
For each slider, there are two update systems:
- The color_slider has a system in
PreUpdate/PickingSystems::Lastwhich computes the gradient and position from the slider value - The feathers demo has a system in
Updatewhich sets the slider value from the current color
I've narrowed it down some more. What appears to be happening is:
- the current color is stored in a resource
- the sliders are updated when the resource changes
- the resource
.is_changed()initially triggers before the sliders have finished spawning, so they miss the update
If I disable change detection so that the sliders are updated unconditionally, it works as expected
I'm able to work around the issue with a somewhat unitutive hack:
fn update_colors(
colors: Res<DemoWidgetStates>,
mut sliders: Query<(Entity, Ref<ColorSlider>, &mut SliderBaseColor)>,
swatches: Query<(Ref<SwatchType>, &Children), With<ColorSwatch>>,
mut commands: Commands,
) {
// Check to see if sliders or swatches were added after resource has changed.
let sliders_added = sliders.iter().any(|(_, slider, _)| slider.is_added());
let swatches_added = swatches.iter().any(|(swatch, _)| swatch.is_added());
if colors.is_changed() || sliders_added || swatches_added {
This also fixes the initial state of the color swatch, but I have no idea why the alpha pattern isn't working
I could do the alpha pattern as a repeating checkerboard texture, but I'd like to understand the problem rather than masking it
The whole alpha pattern setup is a bit of a hack, a pattern that is used in several places within feathers: because we want to avoid building APIs that requires the user to pass in a reference to AssetServer, we instead insert a marker or token or something that tells us "hey, an asset handle goes here, please fill it in when you have a chance". In the case of the alpha pattern it's an observer on the marker
For some reason that observer isn't firing.
I suspect that the whole pattern could be jettisoned with bsn, but I'd still like to understand why it's not working.
How would bsn get around the problem? Exciting if it can. I assume you meant it could help with the problem of having to pass assetserver
Part of the reason why bsn is async is so that it can resolve asset references
That's a feature
IIRC, this has to do with:
Resolve MarkerComponent [CHILDREN] vs Observers [OBSERVERS] ambiguity
Which is the first item on MVP TODO. The means right now, whenever the children list is preceded by a marker component, the explicit Children is needed. In the second example there is no ambiguity, since [] is preceded by an observer.
I did a fix for the alpha pattern after a previous merge, but I think it was accidentally reverted on a subsequent one.
https://github.com/cart/bevy/pull/41/files#diff-81755fef19a17841204f5eae383a167764c84855d4c00eca4b77c34f6e77a842
Maybe I should PR that to main instead, as it doesn't rely on anything next-gen-scenes-specific
I believe the reason it isn't working is because bsn internals currently inserts the components one-by-one (inserting as a single bundle is on carts TODO)
AlphaPattern is inserted before MaterialNode::<AlphaPatternMaterial>, so when the observer fires there is no MaterialNode yet (?)
I'm assuming it's because Component [ children ] is ambiguous with Relation [ related ] and the macro would probably interperet InteractionDisabled as a relation if the Children wasn't there
but it's not needed here because the preceeding on(...) doesn't look like a type name
This is one of the things I really hoped to avoid with bsn. My statechart create is really sensitive to the order you build entities in a way that is unintuitive and hard to explain to users because of exactly this. This problem crops up in my main project as well to the point that I wish there was a way to add components to entities in a system, in multiple commands, that treated all of those inserts as a single bundle and resolved observers as such
Funny thing, I woke up this morning and had the same intuition, although your fix is much better.
Right. Although inserting bundles has never been truly atomic, it's always been atomic from the perspective of observers; and the intuition that a lot of developers are likely to have is that bsn will behave the same way, however it doesn't.
In any case, I think we need to get a ruling on whether bsn component insertion is intended to be transactional or not, this will help us decide which path to go down.
is there a reaction proposal more current than https://github.com/bevyengine/bevy/discussions/17917 ? i know we haven't settled on anything yet but curious if anything more recent has been formalized
Not from what I've seen
bringing this back up
basiclly determines if we are gonna have resources included in editor
I feel pretty strongly that users need to be able to define resources in scene files, regardless of the underlying implementation
Various properties that define a scene in the game sense make sense to serialize and import
yea but if it cant be represented by bsn then we cant have it in the editor so i had to ask if the resources-as-entities makes that possible
I would like to give extra scrutiny to statements of the form "feature X won't be possible until we implement Y as entities.", when the actual truth may be closer to "feature X will be a lot easier" rather than "impossible".
The danger is when "X as entities" becomes an unnecessary roadblock.
Agreed. Note that our current scene impl supports resource serde just fine
as i understand it, bsn only does entities/components, meaning resources as they exist now are incompatible, so either bsn's scope would have to change or resources as entities.
the serialization isnt really the issue but the textual repersentation of it and the hierarchical resolution, how would bsn handle multiple defines of the same resource? etc
why aren't asset paths working here when they work in the example?
what might I be doing wrong?
does TextFont also need to derive GetTemplate?
Yeah many of the types hasn't been migrated to Template/GetTemplate yet.
A workaround is to use the template fn which allows you to do "anonymous" templates:
bsn! {
Text("hello world")
template(|context| {
Ok(TextFont {
font: context
.resource::<AssetServer>()
.load("fonts/FiraSans-Bold.ttf"),
font_size: 33.0,
..default()
})
})
}
yeah that's what I've been using
I'm not even sure exactly how Template/GetTemplate would be implemented for TextFont , as the font handle is optional.
Doing a custom Template implementation requires removing Default (or Clone) from TextFont, but that may not be what we want since it does have a default.
I don't understand, if it has a default then isn't that what the template should use?
also ImageNode doesn't work either
first I'm going to link this: https://github.com/bevyengine/bevy/pull/21346
Secondly: the current implementation of scenes uses resources just fine
So resources-as-entities are not necessary (but they are nice)
The current scene impl doesn't have the scene patching tho right?
That's the concern I have.
Say bsn A defines a resource
Then B and C try to patch different values for that resource
How does it get resolved?
I haven't looked to deeply into the bsn implementation
and because my branch is currently messy I can't even test how it works now
GetTemplate is blanket implemented for Default + Clone types. So to add a derived/custom implementation for TextFont that makes use of HandleTemplate (allowing the nice bsn syntax), I guess one would first have to remove the Default impl for TextFont.
So sadly it's not as simple as just deriving GetTemplate for it 🙁
Maybe TextFont could get split up to something like TextFont(Handle<Font>) and TextStyle { size, etc... }, where TextStyle implements Default and TextFont requires a valid Handle (and uses default font if component not present)
but how I think it currently works is this: after you spawn a resource, a hook makes sure it's the only one - replacing the previous one if it already exists. So the last definition of a resource is the definitive one. This does not concern itself with patches at all
In an MVP, I'd be fine with just "overwrite any current instance with this one"
(I'm half hoping to get this finished before bsn is merged, so I don't have to figure that logic out)
Something like this could work until we have "resources as entities":
#[derive(Default)]
struct InsertResource<R: Resource + Clone>(R);
impl<R: Resource + Clone> GetTemplate for InsertResource<R> {
type Template = Self;
}
impl<R: Resource + Clone> Template for InsertResource<R> {
type Output = ();
fn build(&mut self, entity: &mut EntityWorldMut) -> Result<()> {
entity.world_scope(|world| {
world.insert_resource(self.0.clone());
});
Ok(())
}
}
bsn! {
InsertResource<MyResource>(MyResource { .. })
}
You may want to do a dev-announcement about this at some point
I'd like a bit more bake time (personally) before doing an adoption push like this. Lots of rough edges to sand off. I don't think its quite ready yet people "out of the loop" to start putting it to the test.
Is that a technical or a stylistic limitation?
Currently a technical one, but it is well-motivated. The current implementaton assumes they are standard rust code, which means you can do "anything" there. The reason we can omit ..default() for component patches is because we know with certainty that we are dealing with a struct or an enum that definitely implements GetTemplate (meaning we know we can rely on that to create an instance of it).
If we want to support arbitrary Rust expressions for function parameters, that means we can make no assumptions about what is present there. We could detect the cases where a struct is being instantiated, but we cannot (and should not) assume that it implement GetTemplate. Function parameters do not need to implement GetTemplate (this would be annoying, and adding it would provide no value). Likewise, we cannot assume that an initialized struct has a Default impl in normal Rust code. It might have no Default impl, but rely on specifying all of the fields.
I think the XProps scenario will be less weird ultimately, as our goal is to embrace type driven scene inheritance, so :button(ButtonProps { corners: RoundedCorners::Left, .. default() }) would become :Button { corners: RoundedCorners::Left }.
Should they be inlined as shown here or in an on method
I like inline observers when they are small, and factored-out observers when they are large.
Next question, how come here we need to say Children before the [
This is hacking around an ambiguity in the syntax. This is discussed in the PR, and it is one of the first things I'll tackle now that I'm back on the BSN train.
The lint warning is a macro code-gen issue thats carrying the {} forward unnecessary
I have a controversial
was this sentence cut short?
Agreed
Oops yep
I'll fill that out
checking out the feathers example, I don't love how there's a visual mix between lines that want to end in , and lines that don't
I have a controversial set of syntax changes that I'm working on that would clean a number of things up, including helping make this a bit more intuitive. Still a couple of deal-breaking issues I need to sort out first though
thanks for the explanations, appreciate it 🙂
so the rule is <...> indicates a child entity and you'll generally have stuff like this:
Components
Components
Components
<Child>
<Child>
<Child>
Like, it makes me feel like I'm reading a shorthand notation of
bxml! {
<Node width=px(1) />
<Inventory>
<Item />
<Item />
</Inventory>
<C>
<Node />
</C>
}
which definitely reinforces this point for me
it's worth noting that, if we wanted to be closer to how XML does things, this would be
<Components Components Components>
Child
Child
Child
</>
this is almost exactly the opposite.
yep that's an excellent point
It would be this right:
<Components Components Components>
<Child />
<Child />
<Child />
</>
And notable the </> makes no sense here
I think over-embracing XML would be a mistake
It breaks down for the multi-component case, and it introduces unnecessary syntax
yeah agreed, just wanted to mention the intuitive link it made in my head
The big fundamental difference is that children are defined inside of an element:
// alt-bsn syntax
<
Component
<Child>
>
// XML
<Component>
<Child />
</Component>
it's going to take a lot for me to get used to seeing > used as an end-bracket for a list of children. i do see why () is not ideal.
in general... i like kinda it? it's weird though. very terse.
In addition to removing the </ Component> which is nonsensical in a multi-component world
My first read of this is that I also like it. Your examples make a very compelling point.
My concern is that it looks enough like XML that you are never going to hear the end of it.
I agree that it increases our weirdness budget. But my defense there is "this is a weird new thing we're doing". If we pretend to be Rust too much, it creates weird friction (as we see in the current bsn impl) and if we pretend to be XML too much, it creates weird friction (as we see in the "actual XML" example)
yep, pretty much this for me too.
is there any way at all we could use [] instead of <>? just to increase the hamming-distance from xml?
This would reintroduce the Children [] ambiguity I believe
A while back I did propose [Relationship: Child, Child] but that didn't get much traction.
The "Rust-ey list syntax" would introduce its own weirdness
Especially given that we aren't using , separators
This would be something like: [Relationship: [Child] [Child]] in the []-entity-syntax world
I'm going to say something that might seem tangential, but I'll make the connection clear at the end. One of the limitations we struggle with currently is that our impl Bundle functions assume that a widget caller is free to add arbitrary children: for Button, any and all children are the button's label. However, for many of the more advanced widgets, like dialogs, menus, and scrollviews, the "content" or "caption" will be actually the grandchildren of the widget's root entity. The direct child is an internal entity used for layout or positioning. Other advanced widgets may have "slots" that can be filled in by sub-trees. Currently we have no syntactical support for this.
Now I realize that this is a different problem - we're talking about template parameters, not the direct construction syntax - but I wonder if solving the one might suggest a solution for solving the other.
That is, I wonder if one problem might be a superset
I have a question. Can components follow children? Is this valid
Component
<Child>
Component
If not, then we don't really need the closing brace. If we wanted to be really weird we could try a prefix
Component
Component
+ Child
+ Child
++ Child
Also, what if you inherit from a mixin that adds children?:
:mywidget_with_children
<Child>
:flex_grow(1)
Is the < > syntax going to cause problems with generics?
yeah, i am reluctantly in favor, i think. this is an odd solution but it resolves the constraints.
In theory yes, but we might want to constrain this
I think closing syntax is a good idea. Without closing syntax we're verging on "semantic newlines" territory, which usually has tradeoffs
how would nested children work with closing syntax in this example
Which example?
<> syntax?
yep
Node
<Node>
<
Node
<Node>
>
Looks very similar to what I do - if I'm understanding the syntax correctly
{ // parent
Node: {width: 100}
BackgroundColor: {GREEN}
{ // child
Node: {width: 10}
BackgroundColor: {GREEN}
}
{ // another child
(IsA, checkbox)
InteractionDisabled
Text: {"Hello There"}
}
}
but with {} instead of <>
wow im really not a fan of angular braces just standing alone on a line like that
i dont really want to influence this though i trust yall
this is a lot more intelligible to me ngl
it looks a lot like json
It certainly feels a bit unnatural. But note that this is par for the course in html-land:
<div
foo
bar
>
<div>Hello</div>
</div>
Yup very similar!
Using colons to distinguish between children and properties is smart
The use of braces avoids the uncanny-valley-xml effect
it's more the nested < <> > that feels weird to me since it isn't a thing in html land
same
Nice thing about that is that it's consistent between both component & member assignments, so : { always means assign value to whatever is left of it
I like it a lot better
It creates new challenges:
- We'd be back to "rust but not" territory (athough rust expects
;separators for{}, which is a bit different relative to,+()) - We'll need to find new "inline rust" syntax
The downsides of my approach vs. BSN are (for as far as I understand):
- you always need to add
{}when defining an entity (so notebute {}) - you need to always put
{}around components, they can't appear by themselves (so notPosition: {10, 20}but{ Position: {10, 20}})
For inline rust I would use {{ }}
This is ambiguous with an entity nested inside of another entity
Well, you could say that {{ is a token, and if you want nested entities you'd have to spell it as { {. Either that, or go with shell syntax ${} for inline rust.
{} feels very "arbitrary" to me, and because it is the "scope" syntax in rust, it is used everywhere
It certainly feels more familiar, but I think it comes at the cost of losing clarity of intent
curly braces are used in almost all c-like languages to denote “start of object / end of object”. this use more clearly follows that convention, which is why it reads more easily to me.
And it is more likely to get lost in the noise of many other {} instances all next to each other
Also this is ambiguous:
{
Node
{}
}
Is that a Node {} or a Node with an empty {} child
I believe in Sander's example it's spelled Node: {}.
Yup, the : is what disambiguates between an entity & component value
Ah yeah I was just swapping <> for {}
I don't like that we're departing from Rust syntax in the context of defining the Rust types
Ideally the messaging is "use Rust syntax when defining rust types: Enum::Variant, Struct(10.), Struct { x: 10. }"
I would really like to avoid this
Well, we're not going to get a perfect solution that satisfies both aesthetics and familiarity, there just aren't enough ASCII delimiters available. Any solution is going to be disappointing in some aspect.
to me, the difference from rust seems a lot more minor than the potential confusion with xml. how faithfully can we really hope to capture rust’s syntax anyway?
do we even support generics?
We do!
We can always turbofish the generics if we have to
oh, neat!
From my perspective, we're going to be unfamiliar no matter what we pick, as we are using unfamiliar semantics. There is no mainstream syntax that captures the semantics we require. I think "<> syntax with some similarities to xml but not quite" actually serves us reasonably well:
- It allows us to retain some of the existing associations:
<>defines an "element" (or in our case, an Entity), inside you can define multiple properties, you can nest elements under each other producing "hierarchy", etc. - It is fundamentally different enough that it forces developers to fully acknowledge that it is different (ex: no closing
</button>, accepts Rust types inside / uses Rust syntax inside for those types, children live inside<>, etc) - Because
<>is less prevalent/more scoped in the context of Rust, it reduces ambiguity / noise. Imo, Intent is much clearer, especially to newcomers, compared to a()or a{}syntax on its own
For comparison:
Node { width: 100 }
BackgroundColor(GREEN)
<
Node { width: 10}
BackgroundColor(GREEN)
>
<
IsA(#checkbox)
InteractionDisabled
Text("Hello There")
>
This feels simultaneously more "rusty" and more "nested hierarchy-ey" to me
I can't speak to the rustyness but I don't intuitively see the nested hierarchy here 🤔
Is this a parent with Node and BackgroundColor with two children?
Yup!
Why does the outer entity not need the < >?
It would be wrapped in bsn! {}. We've allowed omitting the () wrapper in that case (for the current bsn! syntax), in the interest of saving spacing / improving legibility. I've applied that here as well
A list of bsn entries would require <>:
bsn_list! {
// list item 1
<Node>
// list item 2
<
Node
<Child1>
<Child2>
>
}
Ah ok. What is the difference between () (also delimits an entity?) and <>?
() is an entity, <> is in an entity list?
I'm discussing old vs new syntax here. () does not delimit an entity in the proposed <> syntax
Ah gotcha
So <> always means entity (is optional for the root) and () is always a component value
In this case, in component position, () would always be prefixed by a type name (ex: BackgroundColor(RED))
But yeah <> always means entity
Gotcha makes sense
With the exception of generics: Ex: List<Item>
what about square brackets?
Do you foresee any use cases for where you want to use the root for other things? Like something I do is:
// import components from namespace into current namespace
using flecs.components.*
e {
Position: {10, 20} // flecs.components.transform.Position
}
(not suggesting you should do something like this- more whether you want to have root-level syntax not related to a root entity)
Theres also "trait disambiguation syntax": <Transform as Component>::on_add(), but that isn't relevant in the component-position
We will also support imports like that in the asset format. I believe that is still compatible with "optional root <>"
I like it weirdly, makes finding each new entity easier.
Is a shame though that it makes short-hand Children stick out more compared to user Relations.
Yeah this is one of the bigger losses imo
Although "optional root <>" actually isn't really an option, as bsn! { <> } is ambiguous in that case.
Is that an empty root or an empty child?
So it would need to be <> is disallowed at the root
[] ?
Or alternatively <> is disallowed
got it
One benefit of using {} to indicate an entity is that it already pairs with bsn! {}
this kinda feeds into the recent conversation of whether empty relationships should be supported or not
But I think that is pretty powerfully cancelled out by the need to abandon Rust syntax for initializing types
I could see a situation where someone wants to fill in entities post-spawn
But in most cases, that case would still benefit from <#Name> or <Marker> to identify a specific entity
Could do entities as [] and relations as <>, swap-aroo 😂
the example given in that discussion I'm referencing is the semantic distinction between "has no concept of inventory" and "has an empty inventory"
too bad rust macros don't support <> brackets, bsn!< ... > would be nice
I'm leaning toward "relationships can never be empty", in the interest of making the empty/no-relationships state query-able, and in the interest of keeping the number of configuration knobs / behaviors as small and predictable as possible
bsn! [
Node { width: px(10) }
Inventory <
[Item]
[Item]
>
[Node]
]
The inventory case can be covered by having Inventory and InventoryContents be separate components
Then you can do Query<&Inventory, Without<InventoryContents>>
I think the swapped version reads better, as <> is more "element-coded"
Ah but I missed the appeal of the bsn! [] matching [] entities
What is the syntax name for the bsn, I cant find the right one xD
That would certainly be a pro
bsn!?
I've been using rust there
However <> does make that work less well
But there were other aspects of bsn that broke naive (non-semantic-token) highlighters
Huh I thought there was another one that highlighted better
Ah, it was javascript
bsn! [
Node { width: px(10) }
Inventory <
[Item]
[Item]
>
[Node]
]
nim works for this snippet somehow (ruby and js work too) :p
There might be, but I always use rust
I believe RA will fill this in properly for us
But thats worth investigating
And its worth factoring in how rust highlighting will behave for naive highlighters (such as those on blogs / discord / github)
At this point I'd be willing to drop the shorthand syntax for Children. Yes, it's an extra word, but it doesn't increase indentation in most cases. And as I mentioned before, most of my use cases have a complex relationship with children anyway. EIBTI.
<> + rust works reasonably well on github, with a few cases where it drops down to using white instead of the struct color: https://github.com/bevyengine/bevy/discussions/21431
As in, you're proposing keeping the current bsn syntax (() entities) and dropping [] shorthand in favor of Children []?
I'm only proposing the second part, I'm not taking an opinion on the first part.
The <> proposal also involves dropping [] shorthand
Just as an FYI
Okay, yeah, I guess the implicitness of that makes me a bit uneasy
If we're sticking with the current () syntax, I'd prefer keeping [] for terseness and adopting Children: [] to disambiguate
I think having Children [] everywhere would be very noisy
The word "very" is doing a lot of work in that sentance 🙂
The implicit "followers are children" feels YAML-ish to me.
Have you tried porting the feathers.rsexample to Children [] everywhere? I have and it made me pretty sad
I am not sure we can change that at this point, I think there is more interest in it being implicit. (I prefer explicit)
We should lobby the Rust team for macro! <> syntax
🙂
(Unrelated to this discussion, but YAML is banned for internal use at AWS because a truncated YAML file is still syntactically valid.)
Dropping this here in case it provides food for thought, I noticed that in a lot of my script code entities have a component that is very clearly the entity's "kind". I added a bit of syntax to make that more readable (and is also a bit more rusty):
div(width: 200, height: 100) {
button(text: "Hello")
}
// same as
{
div: {width: 200, height: 100}
{
button: {text: "Hello"}
}
}
found it to work very well with UI code
What would adding a marker component to the top div look like?
div(width: 200, height: 100) {
MarkerTag
AnotherComponent: {10, 20}
button(text: "Hello")
}
The syntax inside the { } is still the same
How is that not ambiguous?
There's just a Component(value) shorthand for { Component: { value } }
What's the ambiguity?
Whether MarkerTag is a child entity or a component on div.
An entity always has a scope, so you'd have to write
childEntity {}
I really like this
This psuedo-replaces your names syntax right? or do names still get set, and its just the kind component 🤔
Names are still supported:
div myName(width: 200, height: 100)
// same as
myName {
div: {width: 200, height: 100}
}
I see the appeal, although I'm not in love with the arbitrary nature of deciding a "kind"
Hmm I would expect the name first 🤔 🤷♂️ noice (oh C vs Rust diff i think)
I also suspect that intermixing the two styles would affect legibility
// flecs shorthand
div(width: 200, height: 100) {
button(text: "Hello")
}
// proposed `<>` bsn syntax (1 line longer)
<
Div { width: 200, height: 100 }
<Button { text: "hello" }>
>
// same, but one-lining this case to save space (same length)
<Div { width: 200, height: 100 }
<Button { text: "hello" }>
>
Having one way to do things does simplify the mental model
oh C vs Rust diff i think
I was actually going for something that had the same ballpark verbosity as HTML
div myName(width: 200, height: 100)
<div id="myName" width="200" height="100"/>
Yeah I agree it's a tradeoff
Agreed. Feels similar to the [] vs Children [] choice
It's not entirely arbitrary. Components can provide hints to the parser by providing a "default kind" for the children in the scope, so you can do:
enum Color { // default kind = constant
Red,
Green,
Blue
}
// same as
enum Color {
constant Red
constant Green
constant Blue
}
// same as
Color {
enum
Red { constant }
Green { constant }
Blue { constant }
}
It's definitely a bit to unpack, but if you have to write that code hundreds of times over, I def prefer the first syntax 😛
I see the appeal, especially if you're trying to represent type schemas inside the ECS and use those in this context
For normal "scene" use cases, I suspect that would be less relevant
I could see it being relevant for things like
steve {
Inventory() {
Axe,
Wood
}
}
// instead of
steve {
Inventory() {
Item Axe {}
Item Wood {}
}
}
But yeah, definitely most useful for type definitions
I see this part of the language more as a guilty pleasure :p It's much less obvious than the other stuff, but too much fun to leave out
We kind of already have this in the form of the Relationship/RelationshipTarget pairing. But I do see what you mean:
#Steve
Inventory [
<Axe> // implicitly receives InventoryItem relationship
<Wood> //
]
Yeah I do like it!
More likely we would call it the InInventory(#Inventory) relationship
Or maybe ItemIn or ItemOf
converted the bsn in bevy/examples/ui/feathers.rs to the <> syntax to see how it feels on a "real world" scene
some thoughts:
- two of the issues this syntax resolves relate directly to the
Node [ ... ]shorthand (the one that doesn't use()), but i don't think that shorthand was used once in this example - if it's causing issues, maybe just forbidding the shorthand would be an alternative - my brain is not prepared to read child entities and parent components on the same indentation level
- i do think there's something lost making bsn less "rusty" - Bevy exists in an ecosystem that includes multiple very rusty syntaxes (rust, wgsl, ron) and there's a comfort there that feels bad to give up in a syntax that we expect to be a very common sight in user code
going to incept slowly inventing bevy lisp notation into carts dreams tonight
i had that thought looking at this example:
Node <Node Marker <Node>><Node Marker <Node>>
it's so close to being lisp:
(Node (Node Marker (Node)) (Node Marker (Node)))
Unfortunately "rusty" is such a subjective term, @split harness says his syntax is more rusty, sounds like you think it's less rusty - to me BSN looks kind of like JavaScript or C literals.
perhaps i misread, but it sounded like the way this syntax solves the commas issue is by explicitly making it less rusty
so that people stop expecting commas to be required
So my immediate feel is ick for the <> syntax
I feel that the Children [] ambiguity can be solved by removing [] shorthand and instead have e.g. @[] for children - we just call out in the docs that Children is aliased as @ (or whatever other single character). I think this is warranted as Children is such a fundamental relationship for Bevy
Maybe another question that's worth thinking about: how will many-to-many relationships work in bsn? Suppose I have 5 entities with #name components, how can I encode relationships between them?
I know they don't exist yet, but it's worth not backing ourselves into a corner
Personally I really like the lisp-y version!
Option 2 for custom relationship syntax also seems nice. Does it have to rely on newlines/whitespace for disambiguating though? Could it just read all entity items until it reaches the next non-parenthesized thing?
also, would it be helpful if we had a prototype bsn grammar? Might help with alternative syntax proposals, and I need the practice for my uni class lol
i think entity marker syntax should probably be [], and child/relationship lists switched to parenthesis Children ()
i’m most worried about ambiguities with generics, struct initializers and inline expressions
I do not mind the <> syntax but having no delimiters between components (not on the same line) feels strange/unnatural and would definitely take while to get used to (but that does not mean it is bad).
Seeing the example of Runi i would no longer vote for {}, instead of <>, given the otherwise syntax "ambiguity" of rust initializers (e.g. the first Node); i like how you can quickly differentiate between both on a glance. Same for the function call's with () (e.g. button(...), on(...)). So i like the "different syntax for different concepts".
I also like to point that using () to identify an entity feels more natural than <> because after years of writing queries like Query<(&ComponentA, &ComponentB)> it seems strange to use <> on bsn! and () in queries.
Seeing the example of Runi i would no longer vote for {}, instead of <>, given the otherwise syntax "ambiguity"
You could do what I did, which is requiring a:for the initializer:
Node: { display: Display::Flex, ... }
less rusty but fixes the ambiguity
I may sound like a heretic, and it's not particularly rusty, but to help distance the syntax from xml, you could flip the brackets:
Node { width: 100 }
BackgroundColor(GREEN)
>
Node { width: 10}
BackgroundColor(GREEN)
<
>
IsA(#checkbox)
InteractionDisabled
Text("Hello There")
<
It looks intuitive if formatted like this, but could break down in the later examples
>Div { width: 200, height: 100 }
>Button { text: "hello" }<
<
extreme example:
> Div { width: 200, height: 100 } > Button { text: "hello" } < <
.<
Personally I find the <> syntax basically completely unreadable; my brain has been trained to read {}, [], and () for blocks or groups of things in programming languages, and <something>content</something> in XML/HTML, but I have never seen <> used like the proposed syntax anywhere and it's kinda breaking my brain
Also this one isn't a point for or against the syntax, but it's kinda funny to me that one could technically write this (assume no formatter)
bsn! {
<Div>
Text("Hello")
<Div>
}
and it would actually produce a text node with two div children, rather than a div with a child text node
(of course this wouldn't be valid in HTML either though, since the second tag isn't closing like </div>)
It's funny, when you start designing a language it feels like you can do anything, but if you add up all the constraints and include people's sensibilities there's really only a very small number of reasonable options
what about this :P
bsn! {
Node { width: px(100) }
BackgroundColor(GREEN)
// Anonymous child
#: (
Node { width: px(10) }
BackgroundColor(RED)
)
// Named child
#checkbox: (
:checkbox
InteractionDisabled
#hello: Text("Hello there")
)
}
(I still prefer the existing syntax tho)
This is probably obvious to others, but why is it not this?
Node { width: px(100) }
BackgroundColor {GREEN}
BackgroundColor is a tuple struct like
struct BackgroundColor(Color);
you need to create tuples like that with TupleName(Value)
in normal Rust syntax
Rust by Example (RBE) is a collection of runnable examples that illustrate various Rust concepts and standard libraries.
Hm gotcha. Is that a constraint on BSN? Or could it deviate if it wanted
I imagine it could deviate, but that would be changing fundamental Rust struct initialization syntax, which I imagine Cart would be against
Being rusty is technically a design constraint of BSN, but what is and isn't rusty is a bit subjective
Which of the problems listed by Cart this one solves? The main one, with the current design is the RelationshipTarget []vs Component [] ambiguity.
Also there are others, like commas and beign "Rusty"-like
I don't mind the syntax <>, it's just it is a price to pay, the uncanny valley of XML/HTML, but if this solves our problem, I think we should go for it.
We could resolve at least the ambiguity without the <> syntax ("entity delimiters"?)
the <> is mostly for improving the mixed commas afaict
- No over-indentation
- No relationship ambiguity, since this is syntax sugar for
Children [ ... ] - Where to put commas (no commas needed since the
#works for disambiguating syntax)
The closest to the original proposal would be something like
bsn! {
Node [
// normal children
]
InventoryItems: [
// inventory items, disambiguated by the ':'
]
}
I think the lisp-y version of entity delimiters would be great for the commas situation though
(if we can avoid adding another ambiguity)
You could get cheeky and reframe the scope of an entity as a regular struct scope where each "member" is a component. Basically like in my original example:
{ // parent
Node: {width: 100}
BackgroundColor: {GREEN}
{ // child
Node: {width: 10}
BackgroundColor: {GREEN}
}
{ // another child
Text: {"Hello There"}
}
}
That'd still be valid Rust syntax I think, just with a different mapping to Rust concepts (removed the flecs specific stuff for clarity)
My syntax was inspired by the flecs syntax above, except instead of a "kind" there is just the (optional) name before the :
oh i do like this
rather JSONy, which isn't so bad for a scene format
For clarity, you could add names without using a kind:
parent { // entity with name "parent"
Node: {width: 100}
}
or with a number as name:
1000 { // entity with name "1000"
Node: {width: 100}
}
or with a hard coded id:
#1000 { // entity with id 1000
Node: {width: 100}
}
Sander, how does your syntax handle passing in subtrees as parameters? For example, say I have a dialog box component that has a header, body, and footer slot, each of which can take another template as input?
Yeah that seems basically like my version, just without the # prefix for names
Oh hmm we could even drop the : and just do this
bsn! {
Node { width: px(100) }
BackgroundColor(GREEN)
// Anonymous child
#(
Node { width: px(10) }
BackgroundColor(RED)
)
// Named child
#checkbox(
:checkbox
InteractionDisabled
#hello(Text("Hello there"))
)
}
That's not bad
how would this handle custom relationship syntax?
#checkbox(...) just looks a bit like a function call without the space like #checkbox (), but that can be bikeshed
It doesn't, it's just syntax sugar for Children [ ... ] but for a single child. So all relationships still support the current Relationship [ ... ] syntax
I don't have a slot syntax implemented yet, but I've been drafting it. Something like this, but TBD
template DiagBox {
div(class: "diag-header") {
slot("header")
}
div(class: "diag-body") {
slot("body")
}
div(class: "diag-footer") {
slot("footer")
}
}
DiagBox() {
slot "header" {
Text("Info")
}
slot "body" {
Text("ECS Slots!")
}
}
So you have a specific slot reserved word
Yup
I guess this doesn't fix the RelationshipTarget [] vs. Component [] ambiguity
I was thinking something like:
:DialogBox {
header: #[
Text("Do you want to Quit?")
],
body: #[
Text("There are unsaved changes")
],
footer: #[
(
:Button { label: #[ Text("Cancel") ] },
),
(
:Button { label: #[ Text("Quit") ] },
)
]
}
Where #[ is shorthand for {bsn_list!(...)}
it does, if you follow the same solution as Cart proposal, which was to not have a dedicated Children [] anymore, so [] is always used for RelationshipTarget, in that case, Component [] would be a compilation error
Ah right, nice
bsn! {
Node { width: px(100) }
BackgroundColor(GREEN)
Children: // imagine this was a custom relationship
(
Node { width: px(10) }
BackgroundColor(RED)
)
(
#checkbox
:checkbox
InteractionDisabled
#hello(Text("Hello there"))
)
}
bsn! {
Player
InventoryItems:
(#MySword Sword)
(#MyShield Shield)
}
the removal of implicity [] for Children solves two issues: identation and the ambiguity
ok maybe entity delimiters doesn't work as well for bigger entities 😅
the problem/issue with Cart proposal is it try to solve a lot of problems at once, so many solutions/proposals become mixed.
The thing that makes it a slot is that it's identifier : #[?
Yeah. It's just a parameter whose type is "bsn_list". The #[ just means "inline literal of type bsn_list".
You could also pass in a variable or expression of the right type
Ah I like that
with entity delimiters you wouldn't even need the #, since we wouldn't use [] for anything else then
I'm not too familiar with a bsn_list though, is that something that gets lazily initialized? You can't create the list before the slot is being "called" right?
I think so. The way I think of it is as a bound template function.
A curried template
The difference between bsn and bsn_list is that the former produces a single root entity, while the latter produces a vector of entities
That's why esolangs are so fun 😈
Alternatively, with curly brackets
bsn! {
Node { width: px(100) }
BackgroundColor(GREEN)
// Anonymous child
# {
Node { width: px(10) }
BackgroundColor(RED)
}
// Named child
#checkbox {
:checkbox
InteractionDisabled
Children [
Text("Hello there")
]
}
}
Gotcha, that's very similar to how I was thinking about it. Will it have access to its own context? For example:
template DiagBox {
div(class: "diag-body") {
slot("body")
}
}
DiagBox() {
const padding: 4 // contrived, but example of local variables
const width: 100
slot "body" {
Text("ECS Slots!", width: width - padding)
}
}
(if there is even such a thing in bsn)
tuples make more sense in some ways (bundle), but curly brackets maybe look cleaner, not sure
Don't know yet, but I'm hoping that it has normal scoping rules
Yeah the tricky part (for me at least) was that it is like passing down a template, but it needs to be executed with access to its own scope
Passing down that context is a bit annoying, because I don't want to inherit too much complexity from the AST
Sure. Also, we can't assume that it will be defined and consumed in the same place: a template might just forward the parameter to an inner template.
In this case, there are two ways to declare children, implicitly, by using #{}, and explicitly using Children [].
What happens with:
bsn! {
Node { width: px(100) }
BackgroundColor(GREEN)
// Anonymous child
# {
Node { width: px(10) }
BackgroundColor(RED)
}
// Named child
#checkbox {
:checkbox
InteractionDisabled
}
MarkerForSomething
// More child
Children [
Text("Hello there")
]
}
Would this even be legal?
Either just combine them, or return a compile error, I'm not sure why you'd mix both syntaxes in the same entity
Users, they are just, users
We'd need to combine several # {} children anyway, so I imagine it could do the same when there's additionally Children []
I think treating components as "members" of an entity results in a more elegant syntax, and it'd also let you get rid of the #
For comparison, I did the same as Runi, but for the #{} syntax and I like it better than <>. The main downside tho is the closing entity declaration is ambiguous with closing component declaration.
So:
}
}
it is not possible to know which one is which, whereas using <>is pretty clear.
Overall I'm able to easy know where an entity begins and where it ends, so I think it is fine.
this has the advantage of already making room for entity ID, if this become a thing in the future, because #name_child can be also it's Name or some arbitrary ID, defined somewhere in the BSN settings
I like that I can scan down through that example and my eyes are very drawn to the #{ characters, which makes the individual child entities easy to see, even across indentation layers
this one is my favorite so far
it's not perfect, but generally i like the idea of bsn being a "very sugary" syntax over typical bevy spawning code. the more similar it is, the easier it is to convert between the two formats, and the easier it is to learn and read bsn imo
i think <> syntax gets a little further away from that
using parentheses gets a little closer, comma delimiters would be even closer
That would be helpful! Although things are in flux enough at this point that keeping it up to date might be a challenge
Heh this has gone too far 🙂
Hilarious. Ideally we would make this case illegal (components defined after children) / the auto formatter would align their indentation.
I think this is a pretty solid proposal (both #() and #{}). Retains most of the benefits of my <> proposal, with the added value of not rubbing some people the wrong way with <>. However in every way but <>-avoidance, it feels worse to me:
-
I really like that
<>fully "bounds" the entity.#Nameis still a component that exists "on" the entity, so it makes sense for it to exist "inside" the entity bounds.#Checkboxis just (super-powered) sugar forName("Checkbox"), so hoisting it out feels wrong to me. This is also notably how people think about html ids, which are filling essentially the same role (ex:<#Name Node>vs<div id="Name" />) -
Defining names at the root would need to be "different". We cannot write
bsn! #Name (). I don't like that inconsistency. We would likely need to do the following, which reads to me like the#Rootis a peer of#Child(especially when considering that in RustFooandFoo()are both "struct syntax"):
bsn! {
#Root
#Child()
}
-
Anonymous entities (which will be very common) require one more symbol (
#{}) compared to<>. -
Because
#comes first for children, people will come to associate#syntax both as the "child entity designator" and the "entity name sugar". This overloads the semantics in a way that I don't love. -
()and{}are still very common symbols. We're losing out on having a distinct "entity bounds" symbol.
I would say that for consistency we would want this to be:
Children [
#{Text("Hello there")}
]
That perspective makes sense / I agree that it (in a cheeky way) could be considered Rusty. But I think the "Rusty-ness" we lose by not being able to initialize the types in the way we would in Rust is significantly more impactful than the "maybe rust-like if you squint" we get from { Foo: { bar: 10 } } syntax.
Right, I am not as used to the rust syntax so to me using a different delimiter for component values looks weird, but I can see how that's different if you come from rust
I do think the <>-aversion will come up regularly. Which is a shame because I think it is the "best" option
Why we wrapping values in <> 
I also think the <> aversion is "just" because it isn't seen as much. Its the kind of thing people would get used to. But I do suspect it would color first impressions
If I were to be contrarian for a moment though, I'd argue that
{
Component: {a: 10, b: 20}
BackgroundColor: {Green}
}
is more rusty than
<
Component {a: 10, b: 20}
BackgroundColor(Green)
>
if nothing else because the above could plausibly be actual rust code, whereas the bottom example could not be
So I guess it's more about how badly you want tuple type assignments to be explicit
I see your point. I still disagree the value/weight you're assigning to each side of the argument 🙂
I think a language is strongly defined by how you talk about types. Losing that coupling is massive, relative to adding new syntax/symbols to represent a brand new concept. One is a "language extension" and the other is a "language mutation"
But yeah thats a matter of opinion
language mutation
Is it though? You have to map something that doesn't exist natively in the language (an entity->component relationship) to something that exists in the language. What you map it to is kind of arbitrary. There are ECS frameworks out there that let you access components as if they're members on an entity
I'd argue the latter is more rusty, because it looks like 2 random Rust values in <> as opposed to the first one which looks wrong because it looks like a struct with wrong field naming and incorrect value syntax 🤔
You have to cut me a bit of slack for not knowing rust that well, so maybe I'm making dumb mistakes :p
Now ofc neither is really rust, since it represents this:
(
Component {a: 10, b: 20},
BackgroundColor(Green)
)
I agree that it is arbitrary, but its hard to deny that your arbitrary choices involve the added step of translating "component defined on an entity syntax" to "component defined in the rust type system"
Of course 🙂
For sure. I'm definitely not arguing that I cannot see why you'd want to keep using the Type(...) syntax. I'm just arguing that it's not 100% black and white, and that there is some wiggle room for a less controversial syntax that's still somewhat rusty
Agreed 🙂
If the only problem with the proposal is <> we could just use any other random delimiter, we could even use ^$ and make everyone think it's a regex instead 
I still think <> is the best choice, as it is already the open/close syntax for "markup language elements"
Which is what this is
bsn!{
Node{width: 100, height: 200}
BackgroundColor(CYAN)
Inventory [
(Item Name("Spear"))
(Item Name("Axe"))
]
(
Node
Text("Text Here")
)
}
Could keep the old syntax, and enforce () for entities and Children short-hand is root-level entities.
Sadly theres the ambiguity of BackgroundColor(Color) vs BackgroundColor (Color)
Cursed idea / not a serious suggestion; what if we allowed both RelationshipTarget [] and RelationshipTarget(), where the former takes a list of entities, and the former latter takes a single entity. This would at least kind of address the extra indentation problem, if nothing else 😛
bsn! {
Node { width: px(100) }
BackgroundColor(GREEN)
// One child
Children(
Node { width: px(10) }
BackgroundColor(RED)
)
}
(I don't actually like this, still kinda verbose and confusing especially if you want many "inline" children like this)
Theres a space in between?
Yes, but (1) thats a pretty easy mistake to make that would result in confusing results and (2) Rust is not a language where whitespace is semantically relevant.
Then why are we not using commas? cant make it white-spaced then say no to whitespace 😂
We could bend that rule here, but it would be a departure
Haha strong point
Yea the way Rust handles all the ambiguities that arise here is by putting (almost) commas everywhere
(Or turbofish in the case of generics)
Ah shit yeah current proposal also relies on whitespace reliance:
Node
<Node>
Although maybe not actually. I believe that currently requires turbofish: Node::<Node>
I don't know any markup/template language where <> is self-closing by itself (you need <div></div> or <img />, or where you nest <> to form hierarchies. The closeness to language like XML/HTML is more of a downside than a benefit to me, considering it does not work the same as the proposed syntax
-# (I'll just ignore that in HTML you can also do <br> without explicitly closing it)
I just aspired to defeating the turbofish in bsn
I agree that it isn't a 1:1 mapping
And that we're pulling in "undesired context" in addition to "desired context"
OH THE MISERY
-# The turbofish is not to be defeated, but to be worshipped
OH its so bad compared to the joy and beauty of the previous syntax 😭
Haha I didn't find this bit particularly joyful:
-# this has only been valid since HTML5 because they just decided to depart from XML in favor of supporting the mistakes people make
There were a lot of unreconcilable rough edges
Time for commas!
bsn! {
Node { width: px(100) },
BackgroundColor(GREEN),
(
Node { width: px(10) },
BackgroundColor(RED)
),
(
:checkbox
InteractionDisabled,
Children [
Text("Hello there")
]
),
(Node { width: px(10) })
}
At least it looks like it could almost be Rust code to me, unlike the <> syntax
[] is very clear to me as "an array of entities", and () is very clear to me as "a bundle of components", practically the same way it would be with e.g. the children! [] macro
<> says nothing except "generics" or "XML-like templating"
Cart made a good point about how #{} would make inconsistent the naming of entities with inside and outside of it's declaration. I think it is a valid point to be concerned. since it would be nice if we could ID the entity just as any component, maybe in the futre, having a special trait that could be used to unique identify that entity which any component that implements this trait, and using #my_custom_id, I don't know, just wondering the possibilities.
If we consider that we wanna #id to be inside the declaration of the entities, together with components, what are our options, aside from <>?
All #{}/#() tells me is that I'm doing proc macros, which is not a good for the average rust dev 
Yeah I dont think pre/post-fixing the entity/'s components, like how flecs does it, is a good idea personally
Could we use {{ and }} as a new kind of separator? 🤔
No double character symbols pls
we could just do syntax sugar with @ or $ (ew jquery) or something I guess
bsn! {
Node { width: px(100) }
BackgroundColor(GREEN)
// Children
@(
Node { width: px(10) }
BackgroundColor(RED)
)
@(
:checkbox
InteractionDisabled
@(Text("Hello there"))
)
}
pre/post fixing components? 🤔
Like the bundle of the entities components, the entity, weird phrasing lol
You mean this?
e { // prefix
// components here
} // postfix
e, being a pre-fix in this case
so at this point, it's a trade-off between @() or anything + braces/brackets, vs <>. I think the weirdness of <> makes it a good candidate, since it will be unique and have no conflicts with other semantics in the future (aside from generics, but turbo fish is out there)
e is an optional entity name, I could also do this
{ // prefix
// components here
} // postfix
Right, but for bsn the root entity doesnt get wrapped, so you can't pre-fix it with a name or whatever, so there is less consistency
I'm curious about what you mean here
Also, I prefer all of the data to be inside of the entity syntax
Wrap the root entity just like any other entity
Makes sense. You trade a bit more top-level boilerplate for consistency
Right, it's 2 extra characters in what is probably going to be very large documents
Theres also the "benefit" (there are pros and cons) of "list syntax" and "single-root" syntax looking the same, other than one vs many
ex: currently they are "different":
bsn! {
Node
<Node>
}
bsn_list! {
<
Node
<Node>
>
}
Does a canonical root need to exist for a BSN, inside of the BSN or could the BSN always create one implicitly for itself, meaning you could have multiple 'root' entities, but would always be spawned under the implicit BSN entity.
Could potentially merge the list vs single syntax then (although there might be problems with the traits/impls still)
Guess then you would have extra BSN entities that would be just for metadata storage 🤔
Nvm guess it doesnt really work when using them as patches on a single entity
Oh no, that "feature" from the current scene system
bsn! requires a canonical root, as it can be embedded/spawned at arbitrary points as a single entity.
Thats not something I want to give up personally. Although whether that is enforced statically (bsn! vs bsn_list! vs bsn_set!) or at runtime (everything is bsn! and we fail if there is no canonical root and one is expected), is an open question
I think statically gives us nicer guarantees (and skips some runtime checks / simplifies representations), but I see the appeal and simplicity of having a single bsn!
TIL. That's actually a pretty big difference between bsn/my goal, which is to have a format that can represent any possible entity configuration
But not unreasonable ig. HTML also has <html>
Why don't we have both?
bsn! {
Node { width: px(100) }
BackgroundColor(GREEN)
// Children
> (
Node { width: px(10) }
BackgroundColor(RED)
)
> (
:checkbox
InteractionDisabled
> (Text("Hello there"))
)
}
It looks like quote syntax on discord, or maybe a terminal prompt
Yeah now that I look at it, it actually reads pretty nice 🤔
is all this syntactical gymnastics just to avoid writing Children sometimes
Yup this is required reading 🙂
i did read this
Lol the bike icon is red on desktop and blue on mobile. Very meta
Getting back to this for a moment: I think that if the feathers example were properly written, there would be about half as many Children than there is now. The reason there are so many is because every button has a text label child. But I would rather see buttons as having a "label slot" or "caption". Sure, in the case of buttons, you can assume that the children of the button is the contents of the button. However, I don't think that is a good habit for users to get into, because for many widgets just blindly adding children will break them.
Now, even with half as many children it might be argued that this is too many instances of Children and that it deserves a dedicated shortcut.
Isn't this more of a formatting issue?
One could format it like this and skip the increased indentation:
bsn! {
Node
Children [(
Node { width: px(10) }
BackgroundColor(RED)
)(
:checkbox
InteractionDisabled
)]
}
so, as far as i understand it the () inside the Children [] currently is superfluous and was added because a comma alone is easy to miss, but now the complaint is that its too noisy.
kind of yeah, but that looks less readable to me + does not match how rustfmt would format things (if that matters)
is this not good?
Children [
Node { width: px(10) }
BackgroundColor(RED),
Node { width: px(10) }
BackgroundColor(RED)
]
like we can put the comma on its own line if we want it to be more visible
the point is it become too verbose to always write Children []
I would prefer to use <> instead of having to type dozens of Children []
i disagree with that tbh
that's fair
Children [
(
Node { width: px(10) }
BackgroundColor(RED)
)
(
Node { width: px(10) }
BackgroundColor(RED)
)
]
🤡
I would even go so far as to say that most widgets want to be able to accept interior entities as parameters, and those entities will not be direct children. A typical dialog box for example:
- Full-screen barrier element (absolute positioned)
- Dialog box frame (Flexbox / Column)
- Header slot
- Body slot
- Footer slot
- Dialog box frame (Flexbox / Column)
we can have a shorthand for Children and not completely replace relationship syntax
i dont like the idea of Children being extra extra special among relationships
Heh, first time (/j I feel you)
This is valid for UIs, which is a fair point, but for scenes in general, the hierarchy is pretty havily used
as far as i can see the two complaints are
- Children is verbose
- too much indentation/parens
and i feel that 1. is resolved by one of the various "just use some symbol like # that expands to Children" and 2. is basically self inflicted by not liking commas
And many of those will also want to take arguments/scope. Personally I think I would prefer those as closures passed in with the props.
Circling back to a discussion above, #[] would be confusing, since there is already plans for # to be used to identify the entity, like #my_entity. So it would have to be @[] or something like that
E.g:
:dialog(DialogProps {
header: || bsn! {
Text("Header")
},
body: || bsn! {
Text("Header")
},
})
that seems very asset unfriendly
bsn! {
Node { width: px(100) }
BackgroundColor(GREEN)
^ [
Node { width: px(10) }
BackgroundColor(RED),
:checkbox
InteractionDisabled
^ [
Text("Hello there")
],
Node { width: px(10) }
]
}
Cries in ISO keyboard
^^
does ISO not have caret?
following this conversation is starting to feel like reading a chatgpt log where it's running out of context
maybe you can use ^ for the opening character and v for closing
There is a duplicated Node component, or there are 3 childrens, and the comma is used to distinguish between them?
the comma distinguishes
What's wrong with commas? Bone of the biggest issues with JSON is the lack of trailing commas
I've been writing a decent amount of UI code with bundles lately, and I'd argue that indeed children! (and its fully qualified BSN equivalent Children) is very noisy and verbose. I'd encourage anyone who thinks it's not verbose to try it out and compare it to more traditional UI languages.
It does, but on some OSes it won't output anything unless you press again (giving ^^) or press space
I think this is very hard to distingish the entities by using comma, tbh. For reading a file like feathers.rs it would be very hard IMO
bsn! {
Node { width: px(100) }
BackgroundColor(GREEN)
^[ Node { width: px(10) }
BackgroundColor(RED)
, :checkbox
InteractionDisabled
^[Text("Hello there")]
, Node { width: px(10) }
]
}
I think that's valid and why Children deserves a single character sugar
(not entirely serious)
That's fair! But the asset format could potentially support "bsn closures functions" in a very limited form 🤔
Vaguely related to this ^ idea, can we
the [] shorthand, but make () represent a single entity which, when not inside a relationship like Observers [], represents a single child entity
bsn! {
Node { width: px(100) }
BackgroundColor(GREEN)
// Child entities
(
Node { width: px(10) }
BackgroundColor(RED)
)
(
:checkbox
InteractionDisabled
// Relationship syntax still works too
Children [
Text("Hello there")
]
)
Node { width: px(10) }
}
and disambiguate TupleComponent(...) from Component (...) based on the space (or can we not do that?)
This does create some weirdness since in normal spawn commands, nested tuples are effectively flattened to a single bundle, but I'm not sure if that's a problem, with BSN you don't normally need to do that anyway
Why you copy me 🤔 😂
#1264881140007702558 message
Oh right I guess that's the same
AFAIK whitespace is not a rust syntax token, so it can't be parsed? Or maybe it is possible if not using syn... 🤔
Just to try to sum things up, these are the questions/discussions to be had, which are different solutions to different problems with may or may not be related.
-
RelationshipTargetvsChildren, should it have a special short hand (like currently it has, only using[]), should it be explicty at the end (like the cart proposal at #21431) or should it have no distinction between them? -
Entity definition scope, currently it uses a "lisp-like" approach, with
(Node {} Tag Enum(A)), cart proposal is to change to<>, since when combining with (1), the current approach is ambiguous withA (Node)- Should this be a component and a Node child? or this is an Enum/newtype component ?
the main point is how to design those two points and all pros/cons they bring together
Every proposal needs to address those two points, focusing only in one will just circle back to previous discussions
compare it to more traditional UI languages
Very much agree with this. Look how clean & simple qml is in comparison
import QtQuick 2.15
import QtQuick.Controls 2.15
ApplicationWindow {
visible: true
width: 200
height: 150
Button {
text: "Click me"
anchors.centerIn: parent
onClicked: text = "Clicked!"
}
}
A new format should at least aspire to be as easy to read/write
On 1 Children are relations and IMO should be identical to how all other relations are included, I don't like the magic alias of [] which is very jarring of a syntax in a language built around explicitness.
I dont care that its a little more verbose, consistency and explicitness are far more preferable than a little extra terseness.
On 2 relations are components.
If () is a entity then everything about that entity should be inside it, and only omit the () when there's only one entity.
Wayy better imo
@split harness I wanted to ask some questions about the #Name syntax: In the BSN doc, it says that this is just a shorthand for the Name component. However, I think that may be a problem: consider a case where a scene inherits from another scene which has conflicting ideas about the name:
(
#animatable
:widget(...etc...)
}
Where widget internally assigns #othername to the same entity. This means that one or the other name will be overwritten.
I think it would be better if #name was locally scoped to the scene, and completely independent of the Name component (which would have no special syntax).
That is, the syntax #name binds the string name to the entity id, and that binding is accessible during scene construction. The binding would be short lived, any code that wanted to keep the reference around would need to dereference it into an entity id.
A typical use of names is to be able to designate particular entities as targets for animation or dynamic modification, cutting across the normal lines of the hierarchy.
However, in a world where behaviors are defined via mixin scenes, we could see conflicts where different partial scenes are attempting to assign names for their own internal purposes.
Or perhaps we need a different syntactical construct for the kind of use case I am thinking of. Something like React's ref param.
There's a particular pattern that comes up in widgets, especially complex ones, that I'm not terribly fond of: it's where a parent widget needs to grab some descendant by the scruff of the neck and do some brute-force mutation on it - examples are moving the thumb of a slider, or setting the gradient parameters of a color plane. The way this happens now is that we have to set some marker on the descendant, and either hard-code the knowledge of the descendant hierarchy in the parent, or call iter_descendants searching for the marker. It seems much cleaner if we could just get the child's entity id at spawn time and capture it in a closure, allowing us to go straight to the target without searching. But we can't use Name for this because it has too global of a scope.
With reactions, we have a choice: we can put the reactions on the parent and capture the descendants, or put them on the descendants and capture the parent id. But with observers we are more constrained, we have to put them on the entity that has all the state.
It's time to ascend to Markdown list syntax 
bsn! {
Node { width: px(100) }
BackgroundColor(GREEN)
// Child entities
- Node { width: px(10) }
BackgroundColor(RED)
- (:checkbox
InteractionDisabled
// Whitespace-independent nesting via () wrappers
- Text("Hello there")
)
- Node { width: px(10) }
}
Advantages: Very terse indentation, no "empty" lines
Disadvantages: Nested hierachies a bit cursed, you cannot use negative values anymore 
Fwiw in flecs script that's done with variables and/or new expressions:
foo {
bar {}
}
e {
const a: foo.bar // assign named entity to local var
const b: new { Position: {10, 20} } // assign new anonymous entity to local var
Target: {front: foo.bar, back: b /* can reference variable, entity by name or use new expression */ }
}
a and b are scoped to the { } and don't have side effects outside of the script. foo.bar is visible outside of the script
Which also lets you do things like
template Something {
prop val = i32: 0
const a: match val {
1: foo
2: bar
}
e {
// foo or bar depending on val
Target: {value: a}
}
}
I would be fine with that. I've done similar things using the older, clunkier pre-bsn APIs.
I still need to add support for refs (mut in flecs script) though. Const is more like Vue’s computed
Pretty nice, only thing that is missing is looking up entities by a Query instead of by-name.
That's true, though everything is setup for a smooth integration, where things like query variables can directly plug into script variables:
query(SpaceShip, (DockedTo, $obj), Planet($obj)) {
div(class: "planet") {
text("{this.name()} is docked at {obj.name()}")
}
}
& because queries are observable, you'll be able to do this from a reactive (template) context as well
This actually largely already the plan. #Name will exist in "layers" in a way, with each bsn! scope being its own isolated layer / scope (and references to that #Name on the RHS of an expression can only happen in the layer it was defined). I believe the only difference between my plan and yours is that the "highest" / final layer will be instantiated as the Name component.
I can see the argument to make these separate concepts, but aligning them means people don't have to do both #Button and Name("Button") if they want to do both things. And it means we get shorthand for Name("Button").
Its a tradeoff, but it feels pretty good to me
When I've talked about it being "super-powered" sugar, thats the behavior im talking about
You could theoretically extend the syntax to allow multiple names:
#animate #target #thumb :button(...)
Or if you wante to be more explicit:
#animate = #target = #thumb = :button(...)
I like the second one better
That's much more intutive to me
I don't know if there's actually a use case for this, though
Just throwing the idea out there
Part of the problem with names is that right now it's a free-for-all, and we don't have a lot of documented use cases except for animation
If we started to tighten up the semantics - like saying that names are unique within a parent scope - then I think it would be easier to identify potential use cases
Sander has talked about locating entities by path (I'm envisioning something like XPath: "foo/bar/**/baz") but this isn't useful if you can have duplicate names within a scope
IIUC, every entity already has an unique ID: the entity id + gen itself, so what we need is a more friendly way to use this id inside the context of bsn!. I know in "scene time" there is no spawned entity, but this is like a Handle or a Promise, when I spawn the bsn, I'll have an unique id, that's fore sure.
The Name component should be for interacting with external references, where the #id isn't relevant, like animation bones or other things that will be imported in Bevy, so I don't think we should build on top of Name, it should be something for users to use to debug, to display on editor scene view or other things.
So, to sum up my point, the #id should be unique inside where the bsn is defined, it should be totally possible to have two #id's on different bsn declarations, but on the same, this should be a hard error.
@split harness I know you've been very busy with other stuff but I was curious if youve thought more about the pre-world ast like bsn stuff?
Particularly how individual bsn assets are put together during that stage?
Maybe it’s the formatting on mobile but my dyslexia can’t parse the proposed angle brackets syntax at all. I don’t think it’s a novelty/familiary thing it would be permanently hard to read for me. I have no trouble reading html or xml but there’s something disjointed about the current proposed syntax.
It's weird how brains work very differently. For me <> is much more legible and that's despite (because?) almost never having used XML in my career. Before I saw that syntax I couldn't really understand BSN but now I think I understand it a bit better. Though I'm sure I could get used to whatever syntax. I do think some characters like ^ should be avoided since it's harder to type that on an ISO keyboard.
It is indeed weird and interesting
I'm with ya there.
My brain keeps seeing the multiline usage as bring arrows
Single line is fine but once its multiline suddenly looks jarring
They don't work well as a multiline item
Html gets by since they are encompassing a tag rather than the contents
We parse BSN into an AST, which then gets converted to an in-memory ScenePatch implementation that relies on Bevy Reflect internally to patch fields. Inherited scenes are resolved in the AssetLoader (similar to what we do for shaders) and with the asset preprocessor we could "pre-resolve" scenes to make them cheaper to load up and spawn.
Feel free to ask more specific questions as I'm not quite sure what you're looking for here.
I'm a bit confused by the purpose this would serve
Well, I have a plan to use reflection to let plugins declare bsn for the editor ui rather than them directly manipulat it, so I was wondering what would handle putting the various bsn together and if this would require the editor to do anything extra to achieve said usage api.
Entity Paths are for runtime / already spawned hierarchies with Name components. Using paths to resolve entities in the context of scene definitions makes less sense to me when you can specify #Root or #Grandchild directly, rather than "../.../#Root" or "#Child/#Grandchild"
declare bsn for the editor ui rather than them directly manipulat it,
I'm not sure what you mean by "directly manipulate" here
I was wondering what would handle putting the various bsn together
bsn!code can inherit from "bsn assets" and "bsn assets" will be able to spawnbsn!code (provided it is registered / reflected, the details of which haven't been solved yet)
In the past on the prototype we had the issue of plugins needing to change their UI and the queries to get the correct ui elements were getting ridiculously complex with tons of marker components to run around and track and plugins getting entities they werent wanting.
In this new api Ui management is a single crate's job and plugins "declare" a bunch of stuff to the editor like "I want this component fields to have xyz ui" or "I want to add a custom pane" and this crate will handle doing that and managing it.
The hope is to cut down on the amount of issues UI has in the editor. Ive been working on a prototype of it.
Curious to see what you cook up
Ive posted a little about it before
#reflection-dev message
Wdym? I don't quite get it
(and I've done a lot of UI work in rust/bevy/etc)
Multiple of the same pane causing crashes, view port camera management, accidentally adding ui within ui elements cause 1 parent/child nesting off, heavy reliance on entityids to traverse hierarchy and hope you found the right thing
List goes on, the requirements of the editor cause a lot of issues normal bevy usage doesn't see
Well its more a ergonomic json format than a programming lang
Pretty much any modern (and not so modern) UI framework has one
This is not unique to Bevy
Scene notation is a pretty kosher thing to define for an engine, and ECS is not a well trodden field for this kind of thing
What sander said
React, Vue, Angular, Qt, yeah Flecs too
I don't understand what you're suggesting though?
Entity IDs to traverse hierarchy is only really needed because we don't have a reactive UI thing. Normally you don't need to manually query UI entities.
Mm I'd say that depends on the shape of the reactivity -- I find querying by ID super useful and quite easy in my own reactivity solution. But it seems like cart's preferred solutions have more implicit targeting. Either way, I'm really not sure there's much to be learned from unreactive Bevy with regards to editor design practices.
It's quite ugly in a way that basically any reactivity system necessarily solves. At least, that's how it strikes me.
Agreed, hence why I stopped working on it
I hope by the way I'm not coming off as dismissive or anything! I just expect editor design issues to basically melt away as BSN and reactivity are introduced. While we certainly expect the editor to have complex UI, I think pretty much any non-trivial UI in games or apps will face the same problems. I'd hope we could come up with systems that work without lots of careful workarounds.
Its not just reactivity, its the dynamic nature of the editor.
Take for instance component fields, some types might need specialized ui or we can't infer the correct ui element from the type, we need a api for the component to declare the bsn to use.
I'm just taking that need and expanding the solution slightly to cover an even greater problem space.
Why aren't reflect attributes sufficient?
They could be and will probably pair with it, but a more flexible unified solution to the over arching "plugins adding arbitrary ui" is more appealing to me.
Even if its a hard problem I think it'll create a more enjoyable experience over all
i certainly have use cases for arbitrary editor UI 
It's not just about UI, it's for any hierarchy of entities/components. UI just happens to be a case that needs it a lot.
It would only make sense if there were consumers of scenes that expected predefined well-known names. However, what I am saying might not make any sense, since I don't really understand how scenes can be consumed.
Agreed that entity paths are only useful for dynamic lookups of Name components. The point that I was trying to make was that, independent of BSN, we don't have a solidly representative sample of common use cases for Name, and I believe that the ambiguity around how Name is supposed to be used is partly to blame for this. This only impacts bsn because bsn conflates the #Name syntax with the Name component.
In my own particular case, I've been using Name incorrectly for a long time, my own code is littered with misuses of this. Specifically, I was using it as a way to display the entity's purpose in my debug inspector.
Damn, now I feel like the only person who feels that <> makes it very easy to spot which part of the file is which entity, as opposed to using {} delimiters, where spotting a {} block can mean anything from an entity to a struct declaration or a rust block
I remember when C++ introducing angle brackets was considered controversial 🙂
Then again, I remember trying to mentor people who were having a hard time making the jump from K&R to ANSI C 🙂
(Okay so what if we just used /* */ for entities? And /* */* */... for lists of entities. /hj)
I was fond of most of the original syntax, and find it difficult to figure out where we've landed in this round of discussion 😅
there's value in making something different enough to force people to re-assess their assumptions of what the markup is doing
I'm not a fan of the angle bracket syntax but I can't think of a principled argument against it. While it's true that the way that angle brakets are used in C++ and in JSX are different from the way that other paired delimiters are used (for one thing, the items contained are always heterogenous and always determine type [yes, even in JSX], which is not true of []), this isn't distinct from the way cart is proposing to use them either.
I do worry that by sacrificing the child delimiters in the quest for less indentation, we may be kneecapping some important use cases (specifically various ways of composing child lists).
Less indentation isn't a good goal imho
If someone doesn't like the indentation they can just chop it up via multiple functions
That's exactly what happens in the webdev world, don't see why that's not acceptable here.
I kind of agree, although it's hard to make that argument for all the different domains that bsn is likely to be used in. OTOH, for bsn that is generated by machine by an editor, I really don't care if Children is spelled out in longhand or not. I mean, it should be concise from the standpoint of loading efficiency, but I'm not going to worry if the serialization algorithm puts in a few extra keystrokes.
Playing devil's advocate because I also don't mind the indentation, but the idea is to avoid how fast the indentation creeps up, not just how much there is. With the angle bracket proposal, I think you get one level of indentation between the components of the parent entity and the components of the child entity. With the old proposal, you get a level between the parent components and the child entities, then another level between child entities and child components. That means if you're splitting up files because you don't like all the indentation, you have to do it twice as fast.
The counter point tho is, your representing a hierarchy. Indentation is a necessary evil for a hierarchy to be any degree of legible.
Name is intended to be:
- The name that identifies an entity in the scene editor (useful for organization / navigation)
- The name that identifies an entity in entity inspectors (useful for finding the exact entity you are looking for / disambiguating between two entities of the same "shape" / type)
- The name that identifies the entity when dynamically querying the hierarchy at runtime (ex: EntityPath). For example, some theoretical animation editor in the Bevy Editor could produce animation assets that dynamically target entities/components via their EntityPath.
For similar real-world non-bevy scenarios, see Godot's "Node name", Unity's "GameObject name", Unreal's "game object names", and Blender's "object name"
When possible, it is worth either baking an EntityPath to an Entity, or skipping the EntityPath entirely in favor of marker components or something like BSN's "scoped #Name" functionality, as dynamically resolving EntityPaths cause overhead
And in theory, Names can change at runtime
Less indentation is a good goal when you produce too much of it. I don't think anyone would debate whether or not this is "too much indentation"
Node [
Node [
]
]
This specific real case in the current BSN syntax is on that spectrum:
Node [
(
Node
BackgroundColor(RED)
)
]
Compare that to:
<div>
<div
style="background-color: red"
/>
</div>
One clearly feels "better" when it comes to indentation.
(edited to be more representative)
And the proposed syntax for comparison:
Node
<
Node
BackgroundColor(RED)
>
I’m not sure how helpful it is to compare the trivial case. It’s pretty easy to read either at this level of nesting and number of components. The real test is when there’s multiple children with 5-10 components and some of those children have children… unless that’s not how bsn is meant to be used that is. Html hierarchies get reasonable deep
Yes but you can extrapolate forward from the trivial case, which illustrates how they are different
Or you can look at the already-presented feathers.rs port and compare the current to the proposed syntax
Not at my computer right now, but when I get home I’ll add those links to the discussion. Things get lost so easily in discord chats it would be good to have the complex cases easily accessible from the coordination point.
Would you mind pinning the discussion url message? I think only mods have that permission
Maybe it’s just the way that discord renders stuff on mobile vs github but looking at the gihub discussion more carefully I think I’ve come around to preferring the <> syntax lol
Could we atleast close the arrows?
The multiline open arrows is harder to read and it has way too much overlap with the <T> syntax rust uses
Node
<>
Node
BackgroundColor(RED)
<>
The potential draw back I see there is there’s no way to easily find the beginning and ends like there is with opening and closing angle brackets
there's also no information between the angle brackets in the closed arrow case, not sure what the point is.
Guess we could put the relation type inside it?
This is also notably how people think about html ids, which are filling essentially the same role (ex:
<#Name Node>vs<div id="Name" />)
shower thought: I think the main reason for the "ick" many feel when seeing free-standing<on its own line is that we're overwhelmingly used to reading<as a prefix for some type of "element identity" and they never get separated in any XML flavor
for example, this is okay:
<div
id="Name"
/>```
and this is not:
```html
<
div
id="Name"
/>
along those lines, given that bsn's #name is already "super powered" sugar because it's not just a Name("name"), couldn't we reify <#name as the "element identity" for a child entity in bsn?
bsn! {
Node { width: px(100) }
BackgroundColor(GREEN)
// Anonymous child
<#
Node { width: px(10) }
BackgroundColor(RED)
>
// Named child
<#MyCheckbox
:checkbox
InteractionDisabled
<#hello Text("Hello there")>
>
}
this still suffers from points 2 and 3 in cart's replied-to comment, but none of the others and i think retains most of the benefits while adding a new one: skimming BSN markup would become easier as scopes can be visibly named at the delimiter, whereas otherwise you need to visually "enter" the scope to find a name (or read the components) to understand what a specific child's function is
with # it could even be something like
bsn! {
Name("root") // if root entity needs a name
Node { width: px(100) }
BackgroundColor(GREEN)
// child
#{
:checkbox
InteractionDisabled
#{Text("Hello there")}
}
// child with name
#name {
:button
#{Text("Click me!")}
on(handle_click)
}
// custom relationship
#[InInventory] { Item }
// custom relationship with name
#name[InInventory] { Item }
}
Ok I could get behind this
that's the #{} proposal, which cart replied to here
Ah right, I must've missed that one.
same
I wonder can Name have whitespace? Would you write it like #"With space" then?
hmm yea that seems fine
@rare whale just merged this: https://github.com/cart/bevy/pull/45
Would you be able to pin this message?
I reeeeally don't like that. Feels unnecessary
And loses the <>-as-entity-bounds property
I think <# > will feel equally "icky" to people without context, as # on its own provides no hint to its "point". So its just more unfamiliar syntax
One issue with <> is that Path parsing greedily consumes <> tokens as part of the path
And we use Path to parse SOME_TYPE_PATH{}
Solvable by writing a custom Path parser
the openness of
<
Node {
...
}
BackgroundColor(RED)
<
Node {
...
}
BackgroundColor(RED)
>
>
feels alot worse than others suggested
i am likeing the # suggestions
Agree to disagree on the
<>
Node
<>
vs
<
Node
>
But I'll concede the point that < does feel "open"
But only marginally more so than { or (
The whole point is that they are "open" syntax for a context
i think i hit my feelings best earlier
<...> works on a single line but multi-line is harder to parse visually than the standard bracket methods
personally my brain keeps wanting to parse those < as arrows and nothing more
I'll call out that real-world html / jsx isn't winning any beauty / legibility contests:
agreed, webdev has spent ages trying to make stuff more legible
We could steal the "leave one on first line" formatting when it fits
<Node { ... }
BackgroundColor(RED)
<Node { ... }
BackgroundColor(RED)
>
>
i could see that working, so long as rustfmt wont fight me over it lol
rustfmt wont touch anything in bsn! {}. It will try to format bsn!() and do nothing if/when that fails
We're going to recommend (and perhaps lint for) bsn! {}
The plan is to build a bsnfmt tool that IDEs can call
yea i spent awhile with it fighting over bsn!() kept wanting to put like 5 brackets on a single line in a illegible mess
if rustfmt isn't touching bsn, another way to solve the double-indentation issue is to do something very common in javascript land:
Node [(
Node
BackgroundColor(RED)
)]```
Node [(
Node
BackgroundColor(RED)
), (
Node
Backgroundcolor(GREEN)
)]```
etc
We already do this one
This does solve the indentation problem. I suspect it would also ruffle some feathers
Worth considering though!
the full example from the proposal in this style:
bsn! {
Node { width: px(100) }
BackgroundColor(GREEN)
[(
Node { width: px(10) }
BackgroundColor(RED)
), (
:checkbox
InteractionDisabled
[Text("Hello there")]
), (
Node { width: px(10) }
)]
}```
it's a bit icky
how does it handle multiple toplevel ents?
i'm not sure how bsn! handles those to begin with
rn i just slap () around each one
bsn! is (currently) always a single root. See "BSN Sets" in my proposal for multi-entity bsn
I do this in some places
{
Position: {10, 20}
Color: {255, 0, 0}
{ // child
Position: {10, 20}
Color: {255, 0, 0}
} { // another child
Position: {10, 20}
Color: {255, 0, 0}
}
}
or in combination with control flow
for i in 0..10 {{
Position: {i, 20}
Color: {255, 0, 0}
}}
ah make sense
just for fun, here's how i'd do it in clojure:
(bsn
(node {:width (px 100)})
(background-color GREEN)
(children
[(node {:width (px 10)})
(background-color RED)]
[(checkbox)
(interaction-disabled)
[(text "Hello there")]]
[(node {:width (px 10)})]))
a more data oriented version using reader macros and implicit children:
(bsn
#Node {:width (px 100)}
#BackgroundColor :GREEN
[#Node {:width (px 10)}
#BackgroundColor :RED]
[(checkbox)
#InteractionDisabled
[#Text "Hello there"]]
[#Node {:width (px 10)}])
The problem is that the three use cases you lay out are ambiguous / in conflict. If Name is intended to be for developer readabillity, then there can and should be duplicates within a scope. If OTOH Name is meant for entity path lookups, then there should not be duplicates. When I said that I had been using it wrong, I mean like automatically adding the Name Button to every button template so that it would show up in the inspector as a button. This makes it impossible to look up a button by name.
The consequence of this confusion, I think, is not that users use name incorrectly and run into trouble; rather, it's that they avoid using Name components entirely.
I'm strongly in favor of separating these two uses of name into separate components (like DebugName) a proposal which has been discussed elsewhere.
Something tells me a war bouta break out here at that/j
Several people are angrily typing...
Name and EntityPath should be enough, one for debug supporting overlaps, and one for finding an entity supporting overlaps between different parents.
If you want to look up a specific button by name, you should probably give it a more specific name. #Button would just be the default name. You can always do:
:button
#MyButton
I also quite like blueprint's syntax https://gnome-team.pages.debian.net/blueprint-compiler/examples.html#id2
this is nice, kinda looks a bit like kotlin dsls which i also like aesthetically
lol looks like flecs
I agree. But its hard to extend that to multiple components in the same object
That syntax largely amounts to "structs that can nest inside of each other", which isn't semantically "enough"
I believe we can leave some of the less critical/non-core issues until after the bsn is released, allowing us to make improvements after more users have had a chance to actually experience it.
The other thing you can do is show the asset name/prefab in a treeview, so you don't have to explicitly assign a debug name. It'd just show up as "Button"
I might be weird for thinking that's one of the better options...
I really think you are conflating two things here that should be separate, and this will create problems.
i don't mind xml either and definitely fall into the camp of < > hurts my head for not being xml flavored, but also fully recognize that people would clown us for releasing an xml based configuration language in 2025
lol yeah that's amost exactly the same as my syntax
not that surprising though, it's basically JSON without the ""
and a bit more syntax for entities
Is that greater than or equal to the cost of needing to define two names instead of one?
<Entity>
<Node>...</Node>
<Foo>...</Foo>
<Bar>...</Bar>
<Entity name="foo">
...
</Entity>
</Entity>
I said earlier that Name is currently a "free-for-all". Names in XML, HTML, and flecs have rules: in XML and HTML element id attributes have to be unique within a document. In flecs, names have to be unique within a parent scope. Without those rules, the names are far less useful because you can't depend on them. In Bevy, there's no rules at all on how you use Name.
(Special syntax for entities)
<>
<Node>...</Node>
<Foo>...</Foo>
<Bar>...</Bar>
<name="foo">
...
</>
</>
The only difference is that ties go to the winner / earlier names take precedence. If within a parent scope, you need to distinguish between two entities logically, you should probably give them two separate names. I agree theres value in having uniqueness. I'm not sure that outweighs the cost of doing uniqueness checks at runtime
I think the most likely outcome is users inventing their own methods of uniquely identifying entities instead of using Names. Whether or not that's a problem is subjective.
It's not just that uniqueness is enforced or not. It's that we don't even have usage guidelines.
I'm not sure that outweighs the cost of doing uniqueness checks at runtime
If you'd also support a hashmap for faster name lookups, the cost of the uniqueness check becomes zero since you can combine it with hashmap insertion
Them building a different tool would require them encountering significant friction. Manually providing a unique name isn't significant friction, and if we required unique names they would have paid that cost anyway
^ EntityPath can be computed via Name or Entity ID if one doesn't exist or conflicts. (Or even support manual path-names).
EntityPath is optional like Name, and would only need to check for uniqueness on_add.
I'm trying to avoid this as a default behavior. I'm not sure its "worth it". Open question though.
Makes sense 👍 I was in the exact same spot a few years ago where I was fiercely defending my simple Name component which did nothing besides storing a string pointer :p
What changed your mind?
The constant barrage of questions and feature requests hehe
@rare whale I worked on the "spawn related scene list" stuff today. I believe I'll wrap up on Monday
Well that, and I landed a job where having unique names helped out a lot
HTML doesn't complain if you have duplicate ids. The difference between HTML and Bevy, though, is that any tutorial on CSS or HTML best practices will tell you not to have duplicate ids, whereas Bevy is silent on the issue.
I'm very adamant that you should name your entities uniquely if you want unique names 🙂
I think this might be the best option tbh (recommending uniqueness but not enforcing it). Panicking when a name is not unique is not very nice in a gamedev context
(forgive me for being cheeky)
I dont think mixing free-names and unique-names is very rusty, reminds me of Event before it was split.
Then again, I cant complain too much, we are still missing a large part of rust in our ECS, the traits 😭
As things are right now, a developer can't even answer that question. Do I want my names to be unique or not? I don't know, because I don't know how they will be used or what systems will use them.
That is, when I contemplate writing code that uses name components, part of what I have to consider is the general character of the larger ecosystem of names. If that character is unknowable, then that's a strong incentive to use a different tool.
If names are for unique identifiers then it stands to reason only the final user would be using them and therefore know what to name them?
It's a lot harder in a system like Bevy to ensure that names are globally unique, because prefabs might have internal names invented by some other developer. This is why I think flecs makes a good compromise of only encouraging uniqueness for each parent scope.
I've been getting some pushback against it when flecs is used in editor scenarios as accidentally naming your entity the same as another entity causes a panic
I think it's better to not enforce it, but just provide an API that assumes it's unique (so you can do world.lookup("MyEntity")
Why panick, why not use a fallback and warn/log instead? 🤔
You live and learn lol. It does simplify the code a bunch since you don't have to deal with the scenario where an entity with a name is not in the lookup map
But overall better to not panic
So if the #name syntax in bsn! were to add an Name, and if there are uses of #name as an reference in the bsn! then also add EntityPath derived from the Name, the EntityPath could be used since it will be unique, but from an user bsn! point of view though its simply #name. (Not sure if it can do forward-looking stuff like that)
Why do you need a separate EntityPath?
Cause Name is non-unique but a descriptive name, and potentially not all entities will need an unique name to be used for lookup, but checking for an use in the bsn can atleast locally tell you if its needed, and if so add it seperately.
I have a lookup map per parent that only stores the name (so uniqueness is only enforced at that level)
Would EntityPath store the full path?
From my thoughts of it, yeah would store the whole path, and would have to be updated when parent hierarchy's change.
Its equivalent to the ActorPath or whatever its called in UE, except no type at the end kek
The full path isn't that useful in many scenarios though, like
entity parent = world.lookup("Foo");
entity child = parent.lookup("Bar");
entity grand_child = parent.lookup("Bar::Hello");
assert(grand_child == world.lookup("Foo::Bar::Hello");
Also I imagine that if you're writing bsn and you want to refer to an entity, you want to refer to one that is in your current scope or a parent scope
what does world.lookup("Foo::Bar::Hello") do in this context? its given a full path correct?
Yup
So then I assume it has to lookup each entity in that path for the next entity?
With the full path you should always be able to look something up in one. Also you can reconstruct the full path given any hierarchy and their names.
So if you have access to the parent, and it has a full path, and you give it a name to look up as a child, then it just adds that name to its full path, and looks up as if you had the full path already.
Hm, so you'd always append the child name to the path of the parent 🤔 yeah that'd work. Bit expensive but simple
Yeah, I recognize that the conventional wisdom in software engineering is that everything not permitted should be forbidden. In the world of HTML/XML/JSX, however, id uniqueness isn't enforced, but only strongly encouraged. For example, look at the docs for getElementById: https://developer.mozilla.org/en-US/docs/Web/API/Document/getElementById
- There is no
getElementsByIdor any function to get multiple duplicate elements with the same id. You can, however, write a query using selector syntax that does this, but it requires pretty advanced CSS knowledge to do so. - The documentation doesn't say whether you get the first or last element if there are duplicates. It doesn't bring up the subject at all, it's effectively unspecified. It only says that element ids are "supposed to be" unique.
In other words, there is a very strong bias towards uniqueness in the design of the API and the documentation, but it is not an absolute prohibition.
^ yup, that's basically what I think I should do
Unfortunately, we don't have prefabs yet. Because the widgets I have been building are effectively Rust functions, there's no way to access the function name at the template level.
It's certainly possible to make the editor or inspector have a heuristic that lets it identify the "primary" component. However, the developer should have the option to override the description in the editor, and this override should be separate from the identifier used to search for the entity by path.
Thanks for mentioning the last point about separating out debug cases for Name - it reminded me to dig up my experiments and make a draft PR: https://github.com/bevyengine/bevy/pull/21509
TLDR: Adds a DebugTag component that's like Name but can be (mostly) compiled out. Might be controversial though.
Why didn't you newtype DebugName here? to have it using a different feature?
oh you wrote it nvm
I feel like maybe putting the “name” on the same line as the < might help with diffing? It feels intuitive to me that having lines of code that don’t contain any semantics, and only contain syntactic tokens, will cause pain with diffing
I think this is a problem I’ve had with self closing XML-style tags for example, the closing part (/>) often confuses diffing algorithms
I think this is an argument in favour of this. The closing > is still problematic but I think the trade off of not having to repeat the name again in the closing line might be worth it
Repeating the name on the closing line would also make it harder to see where the closing line is (without additional syntax)
some more thoughts: I think the proposed <> BSN syntax only seems weird if you try to imagine it as being equivalent to this:
<Node attr1="val" attr2="val2">
<ChildNode bgColor="red" />
<ChildNode bgColor="blue" />
</Node>
but if you imagine it as being equivalent to this it's easier to accept:
<Node
attr1="val"
attr2="val2"
children={
<>
<ChildNode
bgColor="red"
/>
<ChildNode
bgColor="blue"
/>
</>
}
/>
The BSN equivalent could be something like this I think (?) which doesn't look that different to me, it's basically the same but with the redundant parts of the syntax removed:
<Node
Attr(Val)
Attr2(Val2)
<ChildNode
BgColor(Red)
>
<ChildNode
BgColor(Blue)
>
>
I would note though, the children={ part is only redundant because BSN only allows a single "slot" for child nodes to be inserted into (fwiw, I personally think that is quite a big limitation from a UI dev pov but that's not to say it's definitely the wrong decision for bevy)
I think a part of me would like the closing /> in BSN but that's probably just my HTML experience bias
I do like this framing of comparing it to children={} it makes it easier for me to get it now
tbf, I never liked that when I was doing react 😅
this is an aside but when it comes to react I think children={ actually leads to a much better mental model than the other style, even if it’s more verbose, but that’s for reasons that probably don’t apply in this context so not worth elaborating on too much
i think this is partly what’s tripping me up. i understand the motivation for special casing children but it also makes it more confusing to parse in some ways for me
@rare whale
Do I want my names to be unique or not? I don't know, because I don't know how they will be used or what systems will use them.
If you don't know if your names need to be unique, then you are at a point in your project where the question doesn't matter. If you need uniqueness, then provide unique names. I really don't see the problem here. Users can easily set the name of any entity they spawn (or have the Entity id for).
@karmic rock
Would EntityPath store the full path?
In my mind, EntityPath is a reference that you resolve to an Entity in the moment, not something you would typically store indefinitely (ex: in a component intended to point to some parent entity)
Also I imagine that if you're writing bsn and you want to refer to an entity, you want to refer to one that is in your current scope or a parent scope
My current plan for bsn! is for #Name (right-hand-side value, resolved during spawn) to only be accessible in the current bsn! scope. Within that scope, it must be unique (across levels of the hierarchy). Some parent / inheriting bsn! scope is arbitrary and unknown, and might alias the current scope's names. Anything making assumptions about the parent's name should use EntityPath and resolve at runtime, and that will use the final Name runtime components (and not the spawn-only entity mapping). In this way, it is ok for multiple spawned button scenes to have the same Button name, as the scene that spawns those buttons isn't explicitly defining a #Name, so it isn't in that bsn! scope. I think this lets us have "one" unified system that naturally behaves the way it needs to in most contexts.
@rare whale I just added spawn_related_scenes
/ pushed it to my branch
commands.spawn(Node::default())
.spawn_related_scenes::<Children>(bsn_list! {
(Node {width: px(200), height: px(200)} BackgroundColor(RED)),
(Node {width: px(400), height: px(400)} BackgroundColor(GREEN)),
})
I won't have a chance to check out the code for a day or two. I'm wondering, does it handle the issue I brought up previously about replacing existing related entities without a temporal gap? Specifically, a lot of my use cases involve spawning a list of entities that replaces the previous spawned entities; and I can't simply call despawn_related before calling spawn_related_scenes. The reason is that despawn_related happens immediately (well, at the next command sync), whereas IIUC spawning bsn scenes doesn't take effect until the templates have resolved, which may take multiple frames. This would result in an unsightly flash. What I need is some way to defer the despawning the old entities until the bsn scenes are ready to go.
It doesn't solve that particular problem
You see where I am going with this, my goal is to use scene spawning as a kind of control-flow mechanism. This means that, in effect, scene spawning needs to support some kind of double-buffering.
From my perspective, in most cases you generally shouldn't be calling spawn()on scenes that can't be spawned immediately
Ex: after some initial loading screen, everything happens immediately in the same frame
especially in the context of UI
I want to use scenes in a fine-grained way: an if-statement has a then branch and an else branch, and both branches are bsn_lists.
If the if-condition changes, I want to destruct the current branch and replace it with the new one, with no temporal gap.
This isn't just UI stuff either
If spawn isn't the right way to do it, then what is?
Consider for example a tab deck widget with 3 tabs. Each tab is a different bsn scene, or has some function that can yield a bsn scene. When you click on a tab, the existing content is despawned and a new bsn scene is evaluated to populate its replacement.
It would look bad if there was an interval where the previous tab's content had been destroyed but the next tab's content wasn't visible.
Instead, you would want to wait until the new tab's content was ready and then do an atomic swap.
I suppose there could be some "ready" entity event which gets fired just before the scene is spliced in, but this sounds a bit rube-goldberg-ish to me.
BTW, my arguments about Name are not really related to the topic of BSN, they are really about the Name component; the only reason they are in this channel is because of the way bsn's #Name is joined at the hip to it. So it might be better to move that discussion elsewhere.
wouldn't the state of the art here be:
- user clicks switch tab
- temporarily nothing is shown (showing loading indicators too early makes interactions seem slow/etc)
- if enough time passes for a "load" of the tab content, loading indicators are shown
- content is shown (or possibly error state if loading failed)
so it would really be about being able to access the loading state of the bsn dependencies, either ahead of time for a pre-load call or after spawn.
for ex: if there was a long loading process, you wouldn't want to show the old content anyway, because that makes it seem non-responsive.
It really depends on how long of a delay we are talking about. If it's just a few icons being loaded from assets, and the delay time is only 1-2 frames, I would much rather delay the flip than put up a loading screen - a 2-frame load screen would be an even more unsightly flash.
Also, I want to stress that my tab example isn't some isolated corner case. I plan to use this kind of conditional logic everywhere:
- dropdown menus
- tree view node expansion
- tab decks
- popups
- toolbar modes
- scrolling lists (each list row is a scene)
You generally don't expect toggling a tree-view expansion to put up a load spinner...
yeah that's why I mentioned step 2, which doesn't show a loading indicator for less than say, 100ms, of load time.
You generally don't expect toggling a tree-view expansion to put up a load spinner...
If it takes long enough it absolutely should. That's pretty critical user messaging for long load times or progress indication (which everyone hopes doesn't occur, but very easily occurs on the web, etc). regardless I think focusing on the loading indicator is a bit off the mark. Long load times require load indicators. Small load times don't (and shouldn't) show load indicators. We also can't expect "instantaneous loads" for anything that wasn't preloaded.
Back to the original point, even in the block-then-flip case you want to be able to access the bsn dependencies and pre-load them so the spawn goes through as expected; which seems to align with the "don't spawn bsn that isn't ready to spawn" comment.
Plenty of tab implementations solve for this by doing things like "spawn offscreen and move onscreen to switch", which is basically what you're talking about here, as long as there's a "SceneInstanceReady" style event being fired.
being able to access the load status of a scene seems to solve all of those cases: both blocking and not blocking (which actually aren't even different situations, because block-then-flip has to fallback to the other approach on long enough timescales). In both situations the loading state exists, in your example you want to not show that loading state to a user and swap content when everything is finalized.
Maybe I misunderstood what you were saying. Your step 2 reads:
temporarily nothing is shown
By "nothing" I assumed you mean "show empty space" (which is what I don't want) - or worse, show no entity at all (causing flex siblings to collapse into the vacant spot). What I want is something that shows either the old view, the new view, or an intentional placeholder which takes up the right amount of space. It should never show "nothing".
These conditional scenes that I am envisioning will vary in size: a few very large subtrees with many asset dependencies that will take a measurable amount of time to load, and a very long tail of tiny conditional subtrees that have maybe an icon or two, icons that are likely to be already cached in the AssetServer, but even if not are likely to load quickly. Moreover, as the person constructing this hierarchy, I can pretty well predict how hefty a particular conditional subtree is - that is, I can intuit whether this conditional is large enough to warrant the extra effort of building a loading indicator, vs simply showing the old ui until the new one is ready.
My goal here is to re-write bevy_reactor, except instead of inventing my own templating language to use bsn everywhere. This means lots of bsn micro-scenes: a for-each loop will invoke a scene per iteration. But this plan will only work if the transition between old hierarchy and new hierarchy is seamless - which is something I think we want for bsn to have anyway.
I feel like we're going to eventually need to re-invent a lot of React's prior art for transitions and asynchronous rendering, but that's not in the MVP.
You could do non-deferred spawning like this:
/// Synchronously build and insert a [`Scene`] on an entity and recursively spawn its related entities.
///
/// Returns an error if any of the templates fail to build.
///
/// Panics if an inherited scene is missing/not loaded.
fn spawn_scene_sync<S: Scene>(entity: &mut EntityWorldMut, scene: S) -> Result {
let mut resolved_scene = ResolvedScene::default();
scene.patch(
entity.world().resource::<AssetServer>(),
entity.world().resource::<Assets<ScenePatch>>(),
&mut resolved_scene,
);
resolved_scene.spawn(entity)
}
/// Same as above, but for related scenes.
fn spawn_related_scenes_sync<R: RelationshipTarget>(
entity: &mut EntityWorldMut,
scenes: impl SceneList,
) -> Result {
let mut resolved_scenes = Vec::new();
scenes.patch_list(
entity.world().resource::<AssetServer>(),
entity.world().resource::<Assets<ScenePatch>>(),
&mut resolved_scenes,
);
for scene in &mut resolved_scenes {
scene.spawn(entity.with_related::<R::Relationship>(()))?;
}
Ok(())
}
I'm not sure non-deferred is what I want. Maybe? I'm ok with it being deferred. I just want to make sure that when it finally is ready, the old content is replaced with the new content, with no overlap between them, and no gap.
It seems a bit excessive to store every single "micro-scene" as (in-memory) assets etc. So it would seem like non-deferred (or a bsn gen1 style static resolution) would be the way for many of these. I see your point though, some of these micro scenes may inherit from assets, which may not be loaded... In which case we also might want to reactively show placeholders until they are
It would be pretty neat to have a separation between SceneWithAsyncDeps and SceneWithNoAsyncDeps on a type level.
That way the non-async scenes could simply be spawned as is, while the async ones could be forced to use some <Suspense/>-like mechanism
Yeah, I was only thinking about embedded asset handles (icons, fonts, etc.) not the scenes as assets themselves. In fact, not only are scenes not assets, they aren't static data either: all of the conditional blocks are closures that return a bsn_list.
When you spawn a scene using spawn_scene it instantiates a ScenePatch that is stored as an in-memory asset, with the handle stored on the entity, that is what I was referring to. It seems a bit unnecessary to do that for every single micro scene produced by conditionals or loops. Even if they don't have any async dependencies.
Ah
I can pretty well predict how hefty a particular conditional subtree is
Sure, but you can't predict network latency
Sure I can. If my editor or game isn't loading assets over the network, then it's zero 🙂
More seriously, I think that we'll need some form of Suspense-like mechanism. What I am saying is that it shouldn't be mandatory. Devs can choose to use it or not.
If your bsn doesn't include references to bsn inside of an asset file (which therefore needs to be loaded), then your bsn can be spawned in the same frame. We've talked about having a static split between "deferred bsn" and "immediate bsn". This feels like another case that would benefit from that (as it would make the needs clear statically).
If you're dynamically generating "deferred bsn" based on runtime context, then theres really not much that can be done. The best we can do is let people plug in to the lifecycle (ex: via observers).
If the "if-statement template" has access to the conditional deferred bsn even when the branch hasn't been evaluated, then we could report those bsn dependencies as dependencies of the root scene, and we could load it all in one go, before presenting the UI to the user.
Note that because spawn_related_scenes consumes the SceneList, we have to produce a new instance each time, unless we want our if condition to only be evaluated once. So it has to be something like if(condition, || bsn_list! { ... }) rather than if(condition, bsn_list! { ... }). So no, the scene list for the branch not taken isn't hanging around.
Yeah that was a discussion about a future behavior, not something that works currently. We can definitely add something that takes a handle to a bsn list asset instead of the bsn_list itself
@split harness Getting this error, trying to investigate it now:
thread 'Compute Task Pool (6)' panicked at /home/talin/.cargo/git/checkouts/bevy-6dde41ac0c36136a/0c78073/crates/bevy_scene2/src/spawn.rs:99:59:
called `Option::unwrap()` on a `None` value
I'm about to head out for the day, but it you want to repro yourself use https://github.com/viridia/bevy_reactor example effects. Update: I added a new "control_flow" example which provides a simpler reproduction of the panic.
So like dioxus's static and dynamic elements 🙂
I will continue to shill dioxus-core
@split harness So I've tried both the commands version and the world version of spawn_related_scenes: The call returns ok, but shortly afterwards I get a panic on this line in spawn.rs:
AssetEvent::LoadedWithDependencies { id } => {
let list_patch = list_patches.get_mut(id).unwrap();
let mut scenes = Vec::new();
This appears to have something to do with the context in which I am calling spawn_related_scenes.
That is, if I call it from a startup system in a simple way, it works.
However, I'm calling spawn_related_scenes from within a custom command. The command executes successfully, but the panic occurs shortly afterwards.
So was thinking about file preview thumbnails.
When we make the asset format, it'd be really nice to be able to store a preview thumbnail in the metadata so the editor doesn't have to make new ones every load or store them separately.
quick syntax question
bsn! {
:"scene://transform_1000.bsn"
Transform { translation: Vec3 { x: 11.0 } }
}```
This translates to "apply this component to the root of this referenced bsn"?
The other bsn is applied to the current root, then Transform is applied on top
Hmmm probably has to do with asset handle lifetimes
I'll look in to it
Today and yesterday I've been working on #Name-as-entity-ref in bsn!
Ex:
bsn! {
#Root
[
SomeChild {
entity: #Root
}
]
}
Would the entity Id be filled in here?
I thought IDs were pretty unreliable and need to have monitoring to keep updated?
For the purposes of construction, this logically makes sense. This isn't a promise that they will always be in sync. Just a way to initialize things
Mmn I see.
Ids won't change magically
Could this work cross functions?
They can be invalidated if the entity despawns
Passing a name into the bsn?
The plan is for this to work within a given "scope", where inheritance changes the scope
If you want to reference an entity by name (in a different level of inheritance), use an EntityPath
Hmm I see
I'll have to chew on this.
Been trying to think of how to orchestrate the editor ui resolution stuff, might be useful
Perhaps there could be another use for prefixing entity syntax:
bsn! {
// Contains:
// bsn! { #root Transform::default() [ (#first Transform::default()) ] }
:"scene://transform_1000.bsn"
#new_root
Marker
[
(#second Transform { translation: Vec3 { x: 11.0 } })
]
// Entity-scope applies to the prefix-ed entity.
#first( Transform { translation: Vec3 { x: -11.0 } } )
}
I think I'd prefer to use entity paths here, as #Name syntax behaves like a locally scoped variable. #first() violates that rule. There might be three different #first entities beneath it (at different levels of nesting)
(different levels of inheritance)
So with entity paths this would be like ?new_root.first(...) (cause #new_root would apply overtop #root)
Although in that case, I think Name doesn't have nearly as much value if its only local to the immediate hierarchy level its in.
We've discussed it as something like "new_root/first" (as a string representation), so the syntax might be ?"new_root/first"
is the plan to still have no line delimiters, or will there be commas or semicolons or whatever?
from the PR, it wasn't clear, since I don't think there was a response from cart
If Path is a string representation then Name should be too 😛
(in that thread)
Open question. Current impl has commas in lists and anything that is a real rust type
Although I'm still kind of leaning toward something like https://github.com/bevyengine/bevy/discussions/21431
We could do ?new_root/first, just a bit of an oddity
I mean could just do raw string "" not in a Rust context is either an Name or if it contains an / then its an Path 😂
Jk gotta be able to use those in the Rust contexts kek
Node { width: px(100) }
BackgroundColor(GREEN)
[
(
Node { width: px(10) }
BackgroundColor(RED)
),
(
:checkbox
InteractionDisabled
Children [
Text("Hello there")
]
),
Node { width: px(10) }
]
}```
> And here is the <> syntax:
bsn! {
Node { width: px(100) }
BackgroundColor(GREEN)
<
Node { width: px(10) }
BackgroundColor(RED)
>
<
:checkbox
InteractionDisabled
<Text("Hello there")>
>
<Node { width: px(10) }>
}```
why is the extra level of delimitation from the square brackets in the first example not necessary?
why is the extra level of delimitation from the square brackets in the first example not necessary?
Because in the proposalwe'veI've decided to make that implicit when you specify children
Two changes were made there, ()-><> and any entity <> not inside of an relation [] is implicitly in an Children relation.
and so a new node would be indicated by anything not in the braces?
I'm definitely a fan of that syntax much more than the parentheses
A new entity is indicated by <>
Within a given entity <>, if you see a type name, its a component on that entity, and if you see a <>, its a nested child
what if you want one not a child?
Like a different relationship?
Or a floating entity?
For other relationships, see the proposal doc
Floating entities aren't allowed in bsn!. There is always a single root
Node { width: px(100) }
BackgroundColor(GREEN)
[
(
Node { width: px(10) }
BackgroundColor(RED)
),
(
:checkbox
InteractionDisabled
Children [
Text("Hello there")
]
),
Node { width: px(10) }
]
Something_Else
}```
whatever this would be? Though I'm not sure exactly what relationship that would indicate, would it just not be allowed?
Something_Else would be considered a component on the root entity
Same in the new syntax
Although we are likely to disallow that ordering for clarity's sake
ohhhh ok
and it's one bsn! invocation per root entity right?
or is it possible to declare several sibling entities from a single invocation
bsn! has one root entity, so anything at the root-level is put on the root-entity it creates.
bsn_list! creates multiple root entities, meaning every entity is wrapped with <>, and it will create those multiple root entities.
bsn! {
#root
Node
<#child>
RootMarker
}
// vs
bsn_list!{
<
#root
Node
<#child Node>
>
<
#another_root
<
#child
Node
<#grand_child Node>
>
>
<
#third_root
>
}
ohhhhh yeahhh
ok yeah that makes sense, thank you!
In jsx, a virtual parent usually called Fragment, could be used to skip some layers of inheritance. Not exactly floating, but useful to avoid littering the graph.
Edit- thinking more about this it doesn't translate. Entities and elements aren't the same. Wouldn't really advocate for fragments.
Q: Does this require that Root entity be spawned first? In your example, the reference is flowing downward, from parent to child. Can it flow the other way, from child to parent?
bsn! {
Node { ... }
Parent {
thumb: #child
}
[
(
#child
Node { ... }
)
]
}
It can!
I'm encountering the same issue in a different app, one that doesn't involve a custom command, but wihch does try to call spawn_related_scenes from an observer.
hello, is LSP completion inside the bsn! macro possible ? for example completing component fields, like Node { .. }
I think if you don't specify a component field then it just defaults. At least in the examples it looks like that
yes it looks like it take the default for other field. I mean IDE tab/menu/auto completion for fields name, types, and thing like that.
"Support the scenarios above, while still feeling "at home" alongside normal Rust code. This includes playing nicely with Rust Analyzer for autocomplete, go-to-definition, documentation-on-hover, etc." in https://github.com/bevyengine/bevy/discussions/14437
thats awesome thanks 😃
I just pushed "entity name references as values" to my branch. So this is now possible:
fn root() -> impl Scene {
bsn! {
:base
#Root
SomeEntity(#Child)
[
(
#Child
SomeEntity(#Root)
)
]
}
}
fn base() -> impl Scene {
bsn! {
#Base
OtherEntity(#Child)
[
(
#Child
SomeEntity(#Base)
)
]
}
}
Which produces:
entity_0
Name("Root")
OtherEntity(entity_1)
SomeEntity(entity_2)
Children [
(entity_1 Name("Child") SomeEntity(entity_0))
(entity_2 Name("Child") SomeEntity(entity_0))
]
Accessing #Name as a value in a closure is not yet possible, and will require a bit of additional magic
It is only currently accepted in places that expect an Entity template in field position
I'll look into fixing this next. I suspect it will be simple / just a matter of persisting a handle
In this example note that there are two children, each named #Child, but due to the scoping rules of #Name, the references still resolve in the way the authors of root() and base() intended.
Also note that the root entity has both #Base and #Root in different scopes, which both resolve to the same final entity
I've put up a draft PR which is my WiP for feathers dropdown menus - this currently panics with the issue I mentioned: https://github.com/cart/bevy/pull/47
This has a couple small bugs and missing features (besides the panic) which I need to work on.
For one thing, I need to write a non-feathers example showing how to use popover and headless menu components
I'll give this a look + merge on monday
The feathers example has a menu button on the top row
@split harness I'm thinking about how to organize the bsn and non-bsn versions of the feathers widgets.
- We've already discussed the idea of using struct templates vs. functions:
Buttonvs.button(). I generally like this approach. - However, having two identifiers that only differ in case is a bit dodgy. Another alternative is
button_bundle()for the non-bsn versions. A bit more verbose, but users who access feathers viabevy_immediatewon't know the difference. - A third possibility is to refactor things so that the non-bsn widgets are in their own Rust module.
Question about the bsn "scene://..." References to other bsn.
When I call load on a bsn that has a reference to another, does the scene system automatically resolve loading it? Or does the referenced bsn need to be loaded prior?
If I have multiple references to one, and I call spawn on the referencing bsn, does that cause multiple of the referenced to be spawned or only one?
It will automatically load referenced scenes (if they aren't already loaded).
If I have multiple references to one, and I call spawn on the referencing bsn, does that cause multiple of the referenced to be spawned or only one?
Give me an example here as this is slightly ambiguous
I think button_bundle is the least controversial atm. My vote is to leave them in the same module (and perhaps the same file) to make keeping them in sync easier
I repro-ed the unwrap crash on your branch. It is indeed a Handle lifetime issue, which I easily fixed. I'll push in a sec
I'm looking into how I need to handle the spawn order of the various bsn that the editor will receive over reflection.
I'm currently envisioning building a tree like structure, but based on your awnser if I can pass the correct scene path strings to the bsn's then I should be able to nearly automate the build order and not worry about the details.
Yup!
Side note is there any helper functions to create those strings from a given bsn or do I need to have the reflection api receive them?
I saw you have free will to specify the path at load
The current scene:// thing is a bit of a hack to define scene assets in-memory. I suspect in your case you'll be defining actual scene asset files (when I add support for .bsn assets), and you will use those paths
Actually atm I'm able to pass the assets server to the implmentor so they can call load on their bsn.
I would prefer asset format but atm I'm working with just bsn!
I think it was Alice that told me to try find a solution that worked with the macro as well as the asset format
Full transparency I am skeptical of this Editor abstraction. Curious to see where it lands, but I'm not yet convinced we need it
(ping me in #editor-dev if you want to make the case there)
@rare whale I just pushed the fix
(and merged your pr)
I'm a bit jet-lagged atm so it will be a bit before I can get back to this
That PR is still very much a WiP and has stuff in it that's somewhat controversial - like adding area and scale methods to Rect
We can revert if you want. I have no problem with merging WIP feathers stuff. bsn is very WIP too
When it comes time to upstream we can break things apart and review the feathers port separately
(and the feathers additions)
That's fine then