#Next Generation Scenes
1 messages ยท Page 2 of 1
No polymorphism required.
don't have time now but will get back to it.
Fair enough! ๐
The BSN part of it was less important, it was the fact that there was a required components proposal that would have access to the rest of the entity to decide between adding the UI-space or world-space transform. That said I don't think that part of the proposal is in the PR yet, since it relies on further work (iirc Construct?)
I don't think that would even solve the issue I'm describing, though.
The point is that, at the time that the component X is authored, and made to require Y, it cannot know about hypothetical future component Z that might want to replace component Y. How can it be written to know not to insert Y?
With ECS' we get this lovely flexible system where systems only run if entities match the requirements, and then we add and remove components as the primary API for choosing which behaviours we want to opt into.
Required components take away the users choice about what components are added, therefore taking away their choice about what behaviours they are opting into.
i lied, read it anyway. in the 'plugin dependencies' work, we also ran into this problem.
the solution was to add a 'provides: X' option, which lets you say 'any plugin that needs X can actually use Y instead'
in your example, you could annotate with SpecialComponent with provides(Transform) which makes way less sense semantically but would still maybe work?
idk, this defiantly is an issue.
basically, the rule is if X provides Y then bevy acts exactly as if you passed in a Y for the purpose of required component resolution when spawning a bundle containing X
Looking at your description I think the main thing here is that there isn't really any "user choice". Plenty of crates already entirely forgo using Bundles, instead using systems or hooks to add all the components the users forgot, because if you don't you'll get flooded by users saying "I spawn a X but it doesn't work" and it always turns out to be them forgetting to use XBundle instead
If Required Components had the other half of archetype invariants (disallowed types), you could do
#[derive(Component)]
#[require(Button, MyUiTransform)]
#[disallow(UiTransform)]
struct MyCustomButton;
What do you mean that there isn't any user choice? If you use systems to automatically add components users forgot, you're just manually implementing required components, and that has all the same downsides.
which isn't fantastically usable understandably
'X substitutes for Y' is probably clearer than 'provides'
Yea it's just manually implementing required components, but it actually makes the problem bigger. With required components you can do what @native mesa described. With manual systems you have very little control, often you can't even reconstruct the plugin yourself without those systems either unless you fork the crate
@crisp garden by "what @native mesa described" do you mean his "provides" idea?
Yes
That's great, but has never been on the table for discussion before.
I'm raising concerns with the required components system as implemented and up for PR right now.
There are mitigations (allowing requires to be dynamically overridden during app initialisation, or this new "provides" idea, etc.).
fair. i think it's a framework that we can work to solve problems within. but i'm really glad you brought this up, it is a serious flaw i had not considered.
Yes, because the problem has similarly not been brought to the table before. People already implement manual required components (which is extremely slow in comparison and almost impossible to discover), yet not many have thought about this
@crisp garden that's fair, but I'm surprised this feature has been in discussion & planning for 4+ months and this hasn't come up!
There are probably cases where this could make sense. Avian's Position/Rotation + some scale component could probably "provide Transform", instead of doing this funny dance where Position/Rotation update Transform, and Transform updates them
But now you're also getting into the realm of "If Y provides X, should they be mutually exclusive" ... The answer is probably yes in many cases, but now we also need archetype invariants 
there is a lot of material on bsn to get through. we are still working on vetting it.
I think this really comes down not to required components, but how annoying Transform is in general and how everyone just kind of hacks around it instead of trying to fix it ๐
it's especially hard to discover these edge cases before the code is in-hand.
Transform is easily the most obvious example here, but barely anyone tried to "fix" the problem
Interaction is the other problem case that I've seen worked around
For context I spent ~2 years working on a Unity DOTS Netcode game (that eventually got cancelled ๐), and it was frustrating when using third party code that made assumptions that limited your ability to configure them. I am now just very sensitive to the addition of ECS features that make it even easier for people to "just add an annotation" and limit your ability to override something in their crate.
Well, this is specific to required components, which has a lot less material. But overall it doesn't introduce any problems we didn't already have (since people already manually implement required components everywhere), while fixing easily the biggest footgun in bevy: Forgetting to use the bundle
Huh, anyone else heard of this/considered before bsn?
To try everything Brilliant has to offer for free for a full 30 days, visit https://brilliant.org/Acerola/ youโll also get 20% off an annual premium subscription! #ad
Creating a functional 3d tools pipeline that interfaces with multiple kinds of software can be an absolute drag due to data incompatibilities. Thankfully, Pixar has saved the day ...
this experience is extremely valuable. I used unity dots for 2 weeks when I was 13. it takes a village, and it's not surprising to me that we didn't catch this.
USD is impossibly complex, and not suited for this
I think ultimately the solution here is to have it go both ways: We make it easy easier to forget about those uscases, while also making it easier to override them. @native mesa's solution is really elegant in that regard, you can just override whatever other plugin authors say you need, and there's nothing they can do to stop you because they already threw their manual required components systems in the trash ๐
Yeah, @crisp garden, for clarity I'm not arguing against @native mesa 's solution. It sounds like a good thing to explore.
I have slight concerns on how you'd actually express the "provides" contract, but it definitely sounds like something to look at.
@rough cave are you comfortable opening an issue for the "must be able to override required components" requirement?
There's also, as I previously discussed with Alice, just allowing you to "disable" requires via a hypothetical app.remove_requires::<A, B>().
I'd like to get it properly written down and in the milestone
@thick slate yeah sure ๐ it's the least I can do after I've caused so much angst.
Awesome โค๏ธ
I really want the add_requires version of this, and having the remove version seem totally reasonable as a temporary workaround
I'm not going to block merging the main PR over this, but I will make sure we don't ship 0.15 without it
hey, that's what I proposed :D
#1264881140007702558 message
Yea that's exactly where I got the idea from!
I also have a few usecases like that ๐
if you like provides then i came up with it, if you hate it then it was Joy.
Does this count as a "New Feature" issue?
BTW @magic belfry what's your opinion on a world where we could do this?
So was the idea originally Joy's or do we just blame Joy for everything that's controversial? ๐
Yeah, use that template. It's the most useful all purpose one
joy did this in the context of plugin deps, i fooled around with it a bit
also: am I old now??? ๐ฑ ๐
I mean, I think the earliest available preview was like ~2020.
yeah no i'm not that young, i used unity only a bit ages ago is my point. the community's practical gamedev experience tends not to be very evenly distributed.
@thick slate how does this read to you? ๐ https://github.com/bevyengine/bevy/issues/14927
(or to anyone interested, pls review this issue to see if I've captured things correctly)
I'm not sure if I fully understood how exactly provides works or how it'd help here
Yep, this is solid!
provides would essentially signal bevy "If the entity has Y, consider it as already having X and don't add it". Meaning you would be able to replace built-in components with something that provides equivalent functionality, avoiding the double state like "Position says 0, 1, 13 but Transform says 3, 12, 5" that needs to be synchronized ... So basically a RigidBody wouldn't get Transform, and thus there's no confusion or synchronisation necessary (of course this assumes a tuple of components can also provide another component, since Position alone does not provide Transform)
Please add this to the issue? This is super useful
And then Position would need its own transform propagation and other core transform logic, right? Since Transform itself would no longer exist on that entity, so you need to reimplement the desired functionality for Position. And wouldn't this just break all queries that expect Transform?
Also most users would just add Transform anyway, not Position
We would definitely still need to sort out the propagation issue, we're gonna need reusable propagation the second we split Transform ๐
Alternative would be a "I know what I'm doing" api to opt out of required components
Oof yea this is a pretty mayor flaw ... Tho you could also use it the other way around, if there's Transform don't use Position/Rotation, but now we need trait queries 
Which part? ๐
(also Position is global and Transform is local, so the relationship would rather be that it provides GlobalTransform, which has its own issues... but that's unrelated to the discussion)
Explaining how the provides API would work
I think that's kind of what remove_requires would be ๐ค
I think there's a good chance we want both in the end
they seem complimentary
True true
subs_for is probably more useful for 3d-party crate authors who want to extend other crates, or let users add custom marker components or something.
In this specific case it'd also have the problem that users could mix and match Position and Transform in the hierarchy, which would likely be quite problematic
the remove api is good for code-monkeys who want to make their app work properly
I don't understand the use case of subbing out a required component. If a component is required doesn't that mean that systems will look for it?
Since it doesn't exist wouldn't that be a problem?
Unless we had trait queries or some such
sometimes you want it to stop working with the existing systems, and start working with a new custom system
you need to prevent the old component from being added in that case
#ecs-dev message
I see
I mean that is kind of the ideal API we were discussing in #math-dev ... That would actually be really useful, you have Position/Rotation for things that are physics, then continue on with Transform for visual children (like if the model for an entity is offset relative to the collider)
subbing out required components would work really well with trait queries
this is a goal of mine eventually, yes.
oh yeah, it's all coming together.
Would that also apply to mixing 2d and 3d transforms?
You planned all of this didn't you? 
That's originally where all the plans for changing Transform started, then it became worse with how UI abuses Transform, and we also got usecases where Transform allows weird invariants not supported by crates used (raymarchers can't have non-uniform scale, and hierarchies with non-uniform scale + rotation can introduce shear which messes with physics)
But the Transform discussion is probably off-topic other than the idea of being able to sub out components with similar components
Maybe such a system and split could also rid us of weird things like Handle<Mesh> in 3D vs Mesh2dHandle(Handle<Mesh>) in 2D 
I want to mix 2d and 3d though. Like a hybrid. Like not 2d but also not 3d. Something in between. Like... 2.5d
So orthographic isometric?
That'd give a 2d look with 3d space
Sidescroller with 2d physics and 3d graphics
sometimes it would be nicer to be able to mix 3d transforms to create layers, and then use 2d transforms within those layers
Right now using avian 2d. Would be disappointing not to be able mix things
i do have schemes for this
physics has its own global position and rotation types anyway, it doesn't really care if you mix transforms (except syncing them with physics positions would need to be supported for each of the transform types of course)
Just responded to the "overrides" conversation here: https://github.com/bevyengine/bevy/issues/14927#issue-2487241990. For this topic, I think we should keep the discussion on github at this point to avoid thoughts and info getting buried
How are animations in reactive bsn envisioned to work?
Including transitions (animating in and out) and value changes?
I can't speak for BSN, but the way I do this in Quill is by inserting an AnimatedTransition component: https://github.com/viridia/quill/blob/main/crates/bevy_quill_obsidian/src/controls/dialog.rs#L242
The transition component is driven by a separate system which updates every frame.
The template is responsible for updating the animation parameters: duration, target value and so on. These properties are updated reactively.
I was wondering if you'd need to keep both the incoming and outgoing trees around in bsn
I don't need to.
Because that's something I dislike in react
There's a helper hook called bistable_transition which provides a state machine that implements the exited -> entering -> entered -> exiting -> exited states. This internally keeps a timer and a state variable.
So we don't need to keep around a copy of the whole VDOM, just the state of the one thing.
Required Components is merged!
https://github.com/bevyengine/bevy/pull/14791
Introduction
This is the first step in my Next Generation Scene / UI Proposal.
Fixes #7272 #14800.
Bevy's current Bundles as the "unit of construction" hamstring the UI us...
Yeah I'm thinking something similar
Question I have been meaning to ask with required components: currently my game relies heavily on the ability to have more than one material for a mesh - it's how I do toon outlines. I'm worried that this might not be supported in the future, since it seems like it works only by accident. How will this work in a world where materials are required components? (Or will they be?)
How is this currently done?
And can't you just add the component?
Right now I create a normal MaterialMeshBundle, and then add an additional custom material component.
That workflow won't be disrupted at all
We will likely revisit the Material / Mesh instance API in the context of required components
The big question Required Components creates is "what do we do about raw Handle<T> components"
Either we embrace them, and build an API into handle that lets you add required components for a give T type, or we add wrapper components
I'm largely on team "wrapper component"
a raw Handle as a component is akin to a raw f32 component
it makes no sense imo
it's just data with no meaning
Ex: MeshInstance instead of Handle<Mesh>
Yeah, I'm on team
too
Will I be able to insert multiple wrappers with different generic types?
As in MeshMaterial<A> and MeshMaterial<B>?
If so, yes you can do that
All right.
Material cannot require mesh. I use it for MeshletMesh.
wrapper components also enables struct MyHandles(Vec<Handle<T>>)
And Mesh cannot require StandardMaterial
Is there any negatives or cons to that method over the other?
It's a bit more boilerplate
Also yeah material is a trait, so there's no way to express that anyways
Could that boilerplate be negated by macro then?
In my case, I currently have two materials for the same mesh instance: Handle<StandardMaterial> and Handle<OutlineMaterial>.
In this case, I think just the Deref/DerefMut derive is fine
Wrote up Suggested components: this is a nice bite sized implementation for anyone who wants to tackle it
problably not. the current design abuses the handle type by giving it component semantics imo.
when it's really just data
not behavior
plain ole data vs semantic data
check the issue, it points out a negative for each
All components are supposed to be data without semantics, that's the point of separating systems out ๐
i feel like components are often used to represent behaviors implemented by systems, but i'm no ecs expert
nah there's a difference between Sprite::image and Handle<Image>
and it's still just data
We have options:
- Add a new driver marker component that adds both
- Just make each consumer define each independently
- Revisit the Meshlet API to support a unified mesh component that can be required by materials
Strong disagree. Components should have semantics. Adding a component should be opting in to a set of predictable behaviors + meanings
- Would be fine if we split up "mesh asset prepared for the GPU to use" and "mutable mesh builder the user is using" into separate things.
I'm in favor of 1. I'd really like to be able to reuse materials across UI/2d/3d: coupling it strictly to 3D feels short-sighted, independent of the meshlet problem
@split harness @thick slate I'm wondering if, at this point, it makes sense to split off required components (and the follow-up refactorings of bundles) to a separate working group.
Required components seem perfectly fine in this context
I'm on board with that. There's likely to be a lot of follow-up here
i don't think the scene work can progress in parallel (unless we clone cart) so no
A lot of the micro decisions like "what do we replace ButtonBundle with" can be done indpendently of scenes IMO
Also it would be a good place to collect feedback mid-cycle rather than get off topic about scenes
If making all Bundles defaultable is less of a concern with required components, it could be nice to remove the Default impl from Handles where the underlying asset doesn't have a default (such as Font)
Though I understand if the Bundle API is meant to be preserved
Default for handles is still convenient when deriving Default for a component that includes a handle as a field
you can still do a manual Default impl ofc
Yeah the bundle refactor is something that should be done very carefully and very intentionally. Timing also matters on this:
- Some required components ports might be blocked on Construct (ex: the no-default-handle stuff)
- Doing a "partial port" of the engine across releases feels confusing / suboptimal
- Landing the new scene system "as a whole" might be good optically, rather than slowly bleeding it in.
bundles are not being deprecated but they are being removed from our api surface pretty much
and bundles won't have a representation in scenes
Well, a manual impl still wouldn't work in this case bc fn default doesn't have access to whatever you'd want as the default
(off topic, but i would also prefer land a full scene system, even if it means pushing back the release. the current workload is significantly different from how things have been done for the last two/three cycles. better optics, and first-impressions are vital)
Bundles can be represented in scenes, to be clear
I feel most strongly on 2. Optically, I think that incrementally shipping the hyped-up scenes work is valuable
I think the best case for Handle is to make whether it implements default opt-in per type
it might if the handle is a const
I'd use a builder in that case, where the first fn of the chain accepts a Handle
Oh interesting: even as first-class elements?
Yeah
Yup! You can implement Construct for a Bundle, and all of the code builds around bundles conceptually
Cool, I'm down for that
Components just "happen" to be bundles
But yeah in general we should bias toward "component only"
I have a few bundles that I can't swap over to required components since either combination is valid, so that's nice to hear
In terms of shipping this, I'd like to do the bundle -> component only changes in a single cycle, using deprecation notices on all of our bundle types
are we actually planning full deprecation?
I do want a lot of scrutiny and thought to go into the port. We are essentially re-defining the Bevy API surface / how people interact with the engine. Not something to do lightly
We should identify and codify the general patterns we're going for. And I'd like significant involvement from the maintainers and SMEs for pretty much all of these decisions.
<@&1064695155975803020>
Full deprecation of most built-in bundles, yes. But not of the concept of custom bundles
i wonder how Text2dBundle and TextBundle will be migrated
since they share the Text component
probably means Text won't have any requires. TextBundle is just NodeBundle in a hat.
Sprite and UiImage are separate rn otoh, although they're very close to being identical components
These are functionally very different things, so I think we should consider breaking them out into two different types
ic so either (Text, Node) or (UiText) for TextBundle
In general I'd like us to move in the "top-level driver component" direction that corresponds to a single unique concept
most bundles already work that way. Ok @thick slate i see what you mean about the new working group.
hm Text2dBundle in bevy main also includes a SpriteSource, which you wouldn't get as a required component from Text
I do think that Text should require Node in the context of: https://github.com/bevyengine/bevy/issues/14854
Yeah, I like that direction too. This is how I end up writing my own games
Given that Text in this case is "just" a wrapper over a string, im happy duplicating this into Text2d
Ex: Text2D(String) and Text(String)
They can still share internals
And components like TextStyle
ic. XyzBundle -> Xyz component is probably a good rule of thumb then
Agreed
FYI, we should consider how this concept of required components ties into inspector tooling. It might be nice to be able to track the "originating component" of an entity
Probably only in dev tool mode
With a bias toward moving relevant data into Xyz
(or we have a component / reflect attribute for "please consider me defining")
Might be hard to define in the context of multiple require trees. You could try to define it as "require depth", but I can imagine cases where this wouldn't work
Yeah, that's pretty valid
I just went less "Entity" spam in the inspector panels ๐
If we find a need for this concept (and I'm not sure we need it), then we could introduce it on top
Some of them don't have a single entrypoint, we probably do want visibility <-> inherited_visibility <-> view_visibility
Ex: require_defining or something, where an entity can only have one defining component
(and it would play nicely with require-inheritance)
But id prefer to not go down that route unless we absolutely have to
Yeah, I think this can mostly live in reflection land
I've done this in my next-gen-scenes branch:
Now that Required Components is in I'm back to getting that prepped
I haven't done much porting yet. Just Cameras (and relevant deps) and some UI things
wow, scenes branch code is now officially published 
Just having that for Visibility and Transform will make like half of the current bundles trivial.
Yeah the inheritance behavior is delightful. Everything feels so slim
turns out the oop guys were right all along
i think this is technically a mixin-based inheritance scheme?
We have a lot less bundles in-engine than I was expecting https://dev-docs.bevyengine.org/bevy/index.html?search=struct%3ABundle
Their only mistake was trying to mash data together during inheritance
(I'm actually in favor of supporting classical inheritance in Rust ... just memeing)
as of today, we do. macros are basically compiler code.
the bevy rust esolang is now oop-pilled
Definitely in a way. But in that case I'm talking about vtables / expanded data layouts / etc
yeah, i can see how that would fit. i don't think the lang team will ever consider it.
virtual / override functions, properties, give me it all
just not templates
I do think C#'s "give people lots of language tools" approach is the move
Does inheritance in bevy work similar to what flecs does?
I think it has rough parity with your With<T> api
We do have to make sure, though, not to have 50 tools do the same thing.
We have no concept of the "flecs component-tree override/inheritance" stuff
Gotcha, so you're inheriting the components to add, not necessarily component values
Yup!
Neat, yeah that's what with does
In the context of BSN inheritance (which is different from Component Requires), inherited component values are always stored per-entity and accessed directly
I do think the flecs approach is cool, but so far I feel inclined to keep the "every entity has its values" approach
Yeah that does seem to be in line with what most developers expect
In the newest version I've also switched the default to copying to the instance vs. inheriting
I think the inherit approach feels coolest in the context of quickly + cheaply applying inherited scene changes. Could cut down iteration times
Yup, and it can also cut down on instantiation time
Yup makes sense!
... but adds a lot of complexity all over the place
Yeah it can, get is more expensive for the "can't find the component on the entity" branch
It cuts both ways though, only loading one value in the cache vs 1000 can reduce cache trashing
Though most components are probably not inheritable so not a huge effect
Yeah I do suspect that in the grand scheme of things its barely worth thinking about
(the indirection overhead)
When you say indirection, do you mean in the context of queries?
This bit
Seems like there might also be iteration overhead because its no longer accessing data linearly
(unless im missing something)
Ah yep, that's the main reason why in these tests bevy is winning out in the empty/not found cases
There's a slight overhead yeah, only for query results that return inherited fields
Makes sense. I figured as much!
agreed. Handle<T> being inserted directly as a component always felt very smelly to me personally
or rather, the fact that sometimes it is inside a struct (like the layout field in TextureAtlas) and sometimes bare, which is pretty bad UX imo
bare somehow feels wrong, given that there are situations where the API is made clearer by having it inside a struct, like with the texture atlas stuff
so imo, for UX consistency, handles should not impl Component
btw, fyi, another use case for bundles that isn't often talked about is using them for removal, rather than spawning/insertion. for example, in my project, i have bundles representing a set of various optional components that may or may not exist on specific entities, to make sure i can easily remove all of them when my cleanup code runs
for example, i might have some gameplay-related entity, which could have various optional components representing different state, depending on what it is currently doing. i might want to "reset" the entity by keeping it alive (not despawning) and keeping all the various components that should always exist, but removing any of the optional components that may exist. having a bundle with all the optional component types makes that very convenient. i can just do commands.entity(e).remove::<MyOptionalComponentsBundle>();
this is just another one of my arguments for not removing/deprecating bundles as a concept in bevy_ecs completely ๐
I hope bundles will stay. I usually limit myself to only using 1 bundle per entity โ I define a bundle for every type of entity that I expect to exist. It works but I haven't made anything more complicated with bevy yet so I dunno if it scales well.
I even think that bevy editor should work like that too and require specifying a single bundle for every entity. I feel like most level editors work like that. After all it doesn't make sense to create for example a Player AND an Enemy as a single entity.
I know that bevy editor is planned to be more than just a level editor, but it would be cool if it still supported that, so you can get a level editor for your game for free and release it with your game.
1 bundle per entity sounds unnecessarily restrictive and dogmatic. i use extra bundles for extra optional modular state. bundles are generally useful for ensuring correctness at compile time.
IMO the best course of action for bevy is to:
- add required components
- migrate "spawning common kinds of entities like cameras, sprites, ui, etc" (i.e all the stuff that is currently bundles in bevy) to required components
- deprecate these bundles
- do not deprecate the concept of bundles in general
that is, have the "spawning your entities with the right set of components" use case be handled by required components, but keep around bundles for the "inserting/removing optional extra components" use case
this is what we are doing.
though i welcome the suggestion, there's broad consensus about this, so i don't think we need to discuss this proposal. the editor will not be built around bundles.
okay
sorry for taking up space in the chat for something that was already decided. i kinda lost track.
no problem, sorry didn't mean to be curt. just trying to gently keep things focused ๐
it's never an issue to make sure we're all on the same page, and it's a good overview!
Maybe this is just me, but this sounds like components as traits. ie. Transform is both a component, and a trait, and propogation actually gets whichever the entity has that impls Transform.
Personally, I don't find bundles particuarly useful now that required components are in. I think some higher level concept of "packages" where a package has some kind of insertion function that allows for more complex insertion behavior. More like sickle_uis UiBuilder. If that makes sense.
I'd love to help migrating to required components. Would it work to submit multiple smaller PRs gradually subtracting from the engine bundles, or is it something that needs to be done all at once?
I imagine bevy_transform would be a good place to start in the former case
I'd imagine 1 PR per crate would be good, if that works out well and there's no cross-crate issues
Then I'll probably just start with Transform -> GlobalTransform and remove GlobalTransform from a bunch of bundles
Actually, wait
then there's the question of which one is actually "required" by the bundle itself
nuke spatial bundle. discussion with cart seems to imply it's Transform -> GlobalTransform (Transform is the end-user facing type) and Visibility -> ViewVisibility & InheritedVisibility (again, only Visibility is end-user facing).
-> is requires
There's also lots of bundles (e.g. MaterialMeshBundle) that use transform/visibility components directly instead of inherient SpatialBundle, so watch out for those.
Yep I'm on board with this ๐
nice. PR should be up in a few minutes then, once my rust install stops dying for whatever reason (thanks arch)
deprecation notice
But should Transform require Visibility? I feel like it would be a footgun for a lot of users to forget to add Visibility if there's no SpatialBundle
cart posted code from his dev bsn branch, and Transform did not require Visibility.
There's plenty of things which can have a location without visibility making sense.
Nav-targets
sound sources
it's awkward because there's a SpatialBundle but no Spatial component
SpatialBundle will be replaced with Transform + Visibility. It's not that bad imo.
does visibility not imply transform?
we could introduce a Spatial construct or something maybe
technically no
Yeah, I guess Mesh/Sprite will be the one with the Visibility being required
I wrote a little toy parser for BSN examples as an excuse to learn the nom crate, it doesn't use bevy_reflect (or anything else). Is this of interest to anyone?
In the interest of ensuring (1) and (2) aren't problems, I'd like us to have a clearer blueprint of the "built in bundles ports" before merging anything
I don't want to get half way through and then get blocked (or find that its time to cut a release)
Happy to see required components merged. Is the plan to post the constructs pr soon? Or a break while required components are digested?
I think the biggest question is the Mesh / Material / PbrBundle question
absolutely that yeah, rendering is going to be difficult
could we consider merging into a dev branch? it's going to be a hassle managing a ton of prs that build on eachother.
I was going to tackle Visibility and leave the rest pending more discussion
I think we should just make the plan first and then execute on main
cool cool
The complexity isn't particularly high
hack md then on just in discord?
Also there shouldn't really be any merge conflicts afaict, we're just removing fields
so they can all (or most of the simpler ones) be just off of main I bet
Is there a good way to break this up into multiple documents on hack md? Maybe one for each bundle (or at least category of bundle)
With one central "hub" document?
yeah we can do that. it's a bit clunky, but they have a "book mode".
I'd like to get my branch a bit more functional as a next step, in parallel with ensuring Required Components can land in a complete way next release
With landing Construct (or even more of the proposal) as a stretch goal
as a procedural thing for dealing with affected bundles:
- should we delete newly "useless" bundles like
TransformBundle(in the short term)? Alternative would be deprecating and removing affected fields. This would makeTransformBundlea simple newtype for example. - If the latter, should we remove those bundles from the crate prelude?
Excellent. I'm thinking one page per bundle (to organize the discussions), with a list of every component in the bundle, followed by an initial proposal for the new layout.
Each proposal should be a ## Proposal 1 followed by a code block with the require section. We can then discuss, add new proposals, and at the top have a "currently selected proposal: Proposal X"
alright, i can set that up when i'm not at work
lets get multiple editors involved if possible so people can add proposals when necessary
anyone who is part of the bevy hackmd org can edit
Also as a rule, lets make proposals append-only to properly preserve comments
let me post the link to join again
@native mesa fyi here's a gist, the important part being all the tests at the bottom showing what currently is supported, so you can decide if it's interesting https://gist.github.com/omaskery/42a67ebca36b5ad73897355b2c3ac1dc - the code isn't nice at all, so please forgive that. Was just learning nom on a day off. As a note, I generally write recursive descent parsers for fun in my spare time, so if somebody was actually interested in a "proper" parser for BSN that was reflection-independent etc. then I might have the spoons for that. Not sure if an actual useful requirement though, or if everything in carts private branches has all this sorted ๐
https://hackmd.io/join/B1PtvsG_R link to join the hack md team
@native mesa note some caveats, the syntax is ambiguous without type information, so I don't bother supporting call syntax explicitly, as at the moment all the BSN examples only use Rust syntax that is indistinguishable from a tuple struct initialization. Similar for enums, which are syntactically just struct literals.
I also do a bunch of hacky stuff to just get it working, loads of fragile cheats around strings/characters/numbers/type-names/etc.
Eh actually I think the "category of bundle" does make sense, for things that are actually related
Ex: Camera2d and Camera3d should probably have a unified discussion
What about NodeBundle/ImageBundle/TextBundle etc? Is that a separate effort?
Anything specific youโd like me to look at?
For sure. Pretty much anything rendering related:
- Mesh Bundles
- Cameras
- Camera Render Features
I'll have some proposals there in a sec, as I've already given these areas some thought
I've filled this in
(with all of the bundles)
I'm looking at NodeBundle and wondering what is meant by "required". Almost all of the components in NodeBundle can be left out, defaulted, and frequently are. However, internally, the engine might need some components to be present, even in default form (e.g. transform). This is not something that I, as a Bevy end user, am going to be able to judge.
Like for example, if your node has a transparent background color, there's no need to insert a BackgroundColor component - but it's present in the bundle.
I'm guessing the reason it's there is for purposes of suggestion, not because it's required.
required means the default will be inserted if you do not supply one, same behavior as bundles. There's an open issue for 'pairs nicely with' components https://github.com/bevyengine/bevy/issues/14946
I understand that, I guess what I mean is, how do we decide whether something should be required or not? It is "the engine requires this component or it gets unhappy", or is it "it makes no sense from an end-user standpoint not to include this component" or is it "end users frequently include this component so we might as well require it."
oh yeah i get you. my preference is the former. with bundles there was an incentive to add stuff even if it wasn't invariant-violating to not have it, because it helped with discoverability.
i personally think we should stick to required components being just for invariants.
and just add docs to explain how components interact
Leaving out all of the non-required components from NodeBundle means that UIs are going to get a ton smaller, memory-wise.
Moreso if we adopt one of the various suggestions for breaking up Style
break up of style is probably the next thing we should do after this
since we are already in the business of restructuring all our apis a bit
Unfortunately there isn't a clear consensus on a new design
Just some vague hand-wavy proposals
that might be something cart has to bless us with his infinite wisdom on
At the very least, it's out of scope for this WG
I'm modifying this rule:
- Do not modify existing proposals, add new ones (this will help preserve conversation history)
I don't want a bunch of duplicate code with minor changes
Done
I would (personally) like to move away from "implied default values":
- It makes it harder to reason about what those are, as they often don't live in / near the struct, but rather in a system somewhere
- This also makes it possible to have different implied default values, as each system can choose how to interpret it
- It forces archetype moves when changing to a non-default value
- It makes it hard to query for things (ex: give me everything with a transparent background) because now you need to check both
Option<BackgroundColor>andBackgroundColor(Color::NONE), and it means you need to know to do both. - It makes it harder to discover functionality, as the "optional" component is now floating / decoupled
For truly optional functionality, I'm still down for optional components
But imo it is best to consider all UI Nodes to have a BackgroundColor, a FocusPolicy, etc
Follow up questions, then:
- Should nodes have a grid_start, grid_end etc. even if they aren't in a grid layout?
- Should nodes have a border color even if they have no border?
- Is focus policy even meaningful anymore in an upstreamed mod_picking world?
- ZIndex has an unambiguous default (0), do we still need to include it?
This is something that is starting to bother me with the ECS paradigm, after doing some extensive Ui work in bevy.
The amount of things related to nodes etc that I have to keep in my head. OOP languages and bundles with everything on at least have a lot of help from the LSPs. Optional values in bundles could be a solution for truly optional components that has effects on things.
The style component has to many values, that are actually optional or only does things depending on other members on style. and its parents and children.
which is also a problem
Focus needs to be keyboard accessible as well, weโll need to do some work to align picking with it imo.
Hmm, I can see the utility of this for UI, but would you consider this to be a "best practice" that the ecosystem as a whole should generally follow? ๐ค
For example, in Avian, I have implicit defaults for things like:
CollisionLayers(which layers colliders belong to, and what they can interact with)Dominance(allows bodies with a higher relative dominance to act like they have infinite mass in interactions, 0 by default, can be positive or negative)GravityScale(scales how strong gravity is for a body,1.0by default)SpeculativeMargin(affects speculative CCD,f32::MAXby default)- Etc. etc.
I feel like if every default like this was made explicit by always requiring the component, you would end up with a ton of unnecessary data and increased memory usage. And as a library author, I'm not sure if I'd still be comfortable not handling the cases where components are missing, since users/plugins could still remove the components.
Imo physics should work without a GravityScale on every entity, and if it doesn't, then I feel like you lose one of the big reasons for using an ECS and end up with a ton of unnecessary data on entities.
There's a lot of confusion around focus due to naming conflicts. I've always interpreted the word "focus" as strictly meaning "keyboard focus" and nothing else; however FocusPolicy uses a different meaning of the word "focus". (And then there's bevy_a11y::Focus which is more in line with the first meaning.)
If you recall early unix desktop GUIs (Sun Microsystems, X11) there was a user preference called "focus follows mouse", which would cause whichever window the user moved the mouse over to gain focus.
yeah, it's all mixed together. needs a full review. I think picking only makes this more complex, rather than simpler.
i think of picking as providing "hover" information rather than "focus"
Probably should take this part of the discussion elsewhere
I've added a basic proposal to those that where missing one.
I think I'm with Jondolf here. I am sympathetic to all four of these points. But I would say to (4) I don't see how this is discoverable in a way that docs are not, and to (3) it feels philosophically to me like if the behavior is optional then the component that encodes that behavior should also be optional. Lack of component = lack of behavior is core to the ecs design, and this could violate that.
But I also don't really see anything that in the UI proposal that violates this principle. BackgroundColor being set to clear by default makes sense, as does having a boarder with radius zero. The behavior is invisible, i guess.
what does "implied default value" mean in this context?
It's the default behavior or value used when the entity doesn't have the component. Like if BackgroundColor doesn't exist, you probably have an implicit default of a transparent color, or in my case with CollisionLayers, colliders assume a default layer 0b0001.
It's not something that "exists", it's just how systems should handle the case where the component doesn't exist
i dont see how it would be possible to not have implied defaults
if a component is missing, there will be a different behavior that happens
๐ค so is this convo a thing about, like, "is it better to use implied default values, or attach those components with their default values?"
ig "no implied defaults" means "the systems that operate on this component just skip the entity instead"?
i feel like that should be w/e makes most sense on a case-by-case basis, which is why Option<Component> is a thing
Lack of component = lack of behavior is core to the ecs design, and this could violate that
Having a component opts an entity into certain behavior, but the reverse is not true because you can obviously query for Option<T>.
like "no GravityScale => gravity systems don't affect that entity" vs "no GravityScale => gravity systems affect the entity with a scale of 1.0"
I think, in this specific case, its a moot point. Since we already are going to represent clear as a possible background color, I think it's better to always have the background color component.
there might be other instances where it would make more sense to not have a default, but I think we've got to go case-by-case.
I agree with cart that it's better to have a required component when the default is trivial. Eg "a line with no width" or "a color that happens to be clear".
basically if there's a "does nothing" state in the component.
if you write a system that animates the BackgroundColor of a UI node when you hover over it, but entities spawned with Node don't get an implicit transparent BackgroundColor, you'd have to start explicitly adding it everywhere to make your system work
I don't really see a reason to discourage one or the other, either seems OK.
but yeah, entities aren't covered by GDPR, they don't get to opt out of behavior (unless entities can be hidden/disabled), a system can involve whoever it wants
right but we're talking about api design principles.
not having a Transform component says something
is there a way to insert without required components? like an escape hatch
or is that planned if not
no and it is very controversial
ope
in that case i'll just say that IMO it should probably exist but no need to derail the conversation
hm that's not exactly what i meant
not changing the required components tree, just having a .spawn_without_required(components) api
Should nodes have a grid_start, grid_end etc. even if they aren't in a grid layout?
Based on the proposed (admittedly not-yet-fully-thought-out) "style system" in my doc (styles do not add components, they just set them if they exist), I would assert that Nodes should unconditionally have grid_start and grid_end, to ensure that the style would be applied if someone defines Display::Grid in a style.
Alternatively, I think its worth trying to tie grid properties directly to the "uses grid layout" state, to ensure, this behaves as expected. For example, these could be fields on Display::Grid { start, end }.
Should nodes have a border color even if they have no border?
I'm thinking we should consider merging border properties into a single Border component. If a Node has a border, it has a radius and a color by definition.
#[derive(Component)]
struct Border {
left: Val,
right: Val,
top: Val,
bottom: Val,
color: Color,
radius: BorderRadius,
}
Is focus policy even meaningful anymore in an upstreamed mod_picking world?
I've sadly been out of this loop so I don't have a good answer here.
ZIndex has an unambiguous default (0), do we still need to include it?
In general I think if you ask yourself "does this Component have a X", then it should have the component. For example, the answer to the question "does this Node have a z index?" seems to be pretty clearly "yes it has a local z index of 0", so it should have a ZIndex component to reflect that.
Imo in pretty much all of these cases, if you ask yourself the question "does this entity have this property", the answer is "yes". For the reasons stated above, I think they should be required.
I feel like if every default like this was made explicit by always requiring the component, you would end up with a ton of unnecessary data and increased memory usage.
I think this is the biggest downside to my proposal. But imo this is barely worth thinking about for roughly-single-primitive components like these. Memory isn't so constrained (or so slow) that I think we need to be this precious to this degree.
That being said, if in specific cases adding a component is proven to be problematic for scale, its worth considering making that component optional.
And as a library author, I'm not sure if I'd still be comfortable not handling the cases where components are missing, since users/plugins could still remove the components.
I do think core library authors should be defensive here / cover these cases. But I don't see it as an argument to encourage reliance on those cases being covered.
Imo this is the more important question. My assertion is that "if a component has an implied default value, it should be a required component". There is the separate argument of whether or not we should make missing components gracefully fall back to default values or break in a louder way, but I see that as more context specific and less important to resolve for the current effort.
On styles, I've been going down the exact opposite path: all my styles are set via a helper (StyleBuilder) which offers a fluent API that automatically inserts (or removes) the components as needed - which means that the user doesn't even need to know which component a given property lives in. StyleBuilder is also an extensible trait, so for example even though mouse cursor shapes are not natively supported in Bevy (not as styles), you can can set the cursor shape for a node just like any other style property, and it automatically inserts a Cursor component.
Nico also has another idea for styles which is different than either of ours.
In any case, the mental model is similar to CSS, in which there's just one big bag of properties, and how it is broken up into components is an implementation detail.
I see the appeal of this approach, as it gives styles the power to modify and extend the behavior of an entity. My primary counter argument is that it feels like styles shouldn't be responsible for changing the "shape" / "type" of an entity. The "shape" of the entity should be defined by logic and the style should just twiddle values on it. Not messing with the "shape" also seems important if we're going to be selecting styles based on entity shape (aka "apply this style to every entity with the shape [Button, CoolButton]"). The recursive behavior of allowing a style to change the shape, and thus select new styles, which can then change the shape, etc seems gnarly / hard to reason about.
However I'm not yet convinced that global "shape based" styles are the right move.
Selecting styles by shape is not yet something I have encountered. A lot of things are selected by shape, but so far styles has not been one of them. All of the systems that I have written that selected based on styles were for my own components that were "style-like" but not official Bevy styles: Cursor, Floating, ScrollArea, and so on.
In the context of "shape based styles", I'm largely thinking about the CSS approach of doing things like:
.my-list > li {
/* list item style here */
}
Right. And that was what I set out to solve in bevy_peacock, which allowed CSS-like selectors. But I got a lot of pushback from people in ui-dev who didn't want this kind of selector logic, so I abandoned that approach.
Converted to Bevy, that might be:
.my-list > ListItem {
/* list item style here */
}
or
// only style selected list items
.my-list > (ListItem, SelectedItem) {
/* list item style here */
}
My read on the crowd is that people want to set styles explicitly on the node, and if they want inheritance or themes or whatever they will do it via another ECS mechanism.
Makes sense. Again, idk where I stand on this particular issue. My current stance is "lets start with scoped direct styles and then evaluate when/how to expand that scope"
OK, let's talk about thing I think people really care about, and which relates to the required component discussion: animated transitions
In my world, if you want to animate a background color, you have to explicitly set a background color
You can't animate a component that's not there, but it's your responsibility to make sure it's there when you set up the animation.
Yup this makes a lot of sense to me
To be more explicit: the animation curve component isn't going to automagically insert a background color for you.
I think thats the right way to do things, at least to start. I can see a world where people ultimately want to be able to animate the adds / removes of components, but I think that should be a separate feature / have explicit semantics
I do automatic insertion and deletion of the Disabled component for greyed-out widgets:
node.insert_if(self.disabled, || Disabled)
The disabled component is removed when the condition is false.
Makes sense! Within the context of widget logic (Reactive Scenes in my proposal), I think reactively adding / removing components should absolutely be supported
In the comments of the issue that is one of the discussed options.
2 not only forces moves, it also makes the DX pretty awful, since in most apps these components never get removed so you could be saving a lot of options, branching, and commends.entity(entity).insert(T). With the other points also making DX worse
Can I add a required component at runtime in addition to dev time (macro)? I didn't see anything in the PR at a quick read.
Use case would be making my first party component a requirement of some third party component.
IIRC there was a different discussion/PR for this, so I believe it's planned as well
It's mentioned as part of https://github.com/bevyengine/bevy/issues/14927
And Jondolf also brought up a usecase where this would be helpful (an optional SleepingPlugin for physics, where only when it's added are the components required).
I don't remember actually seeing cart comment on this specific thing so I'm not sure how easy/hard this feature is ๐ค
FYI, I'm aware there's no official consensus/approval on these proposals yet, but I did an initial required components migration for the following things listed in the HackMD:
- Visibility
- Audio (note: this is slightly more involved than Proposal 1)
- Cameras (no render features ported yet)
- Lights
- Fog
These are all on separate independent branches, and would become even nicer once required components ports for transform and visibility are merged. You can see the diffs for each of these behind the links.
Are these something we'd want PRs for already? If so, I could improve the docs and make proper PRs later today / soonโข. I know things like meshes, text, and UI might require a bit more design work, which is why I didn't port them yet, but the ones I did shouldn't be too controversial imo (except maybe audio).
If it helps fuel the discussion, the use case I was thinking of was something like a PreviousInteraction(Interaction) component added to everything with an Interaction, adding a system to update it with mut Query<PreviousInteraction, Changed<Interaction>>, and triggering events when transitioning from pressed to hovered (sort of a pseudo mouse-up event)
Interaction is getting
in 0.15
See #1236111180624297984
This is really helpful, thank you!
@magic belfry I think hold off on PRs until Cart is back from vacation: I'm not comfortable merging any of them until he's around
Yup, will do! In the meanwhile, I'll probably try to migrate (some of) the rest of the bundles to required components as well. No need to use any of this of course, just wanted to help this progress and see what it'd looks like in practice instead of letting things stagnate in the design/proposal phase
-# Really I just want to work on something useful that isn't related to physics or math for a couple of days ๐
the proposals in the doc have not been evaluated yet
you may have to redo stuff, just so you're aware
yes I'm aware, and I'm fine with throwing things out and redoing things
I would expect a lot of the components like visibility, lights, and fog to be pretty safe though, there's not much open-endedness in terms of what the end API is like imo (i.e. just add PointLight) apart from maybe the details of which components are required
the main pain is updating all the examples to
bundles, it took me like two hours yesterday for all the components
i really wonder if there's anything an auto-migrator could do
this is going to be a huge pain at work, when we eventually update to 0.15
Well, we can / should keep the bundles around for at least a cycle as deprecated
Which should help
Can't wait for PRs to get marked C-Needs-Migrator
Version bump separate from the painful migration is always nice. Tho I will
bundles from my game literally the commit after the migration ๐
Thanks! I'll start looking today
Can't seem to find this but am really curious: what is the current planned timeline for BSN? Is it meant to be implemented soon, or what are the blockers (if any)?
Work is progressing on carts side to complete and submit reviewable chunks. The first chunk was required components. We will probably have to complete the migration before anything else.
Are bundles anywhere/everywhere in bevy going to be replaced with required components? If so, what are some low hanging fruits for starting the migration. I'd like to contribute
https://hackmd.io/@bevy/required_components
There's a bunch of proposals for the migration here, a few have been implemented
but we're waiting for consensus on the overall situation currently I think
Has anyone taken a look at this In-line Required Component Values hackmd
https://hackmd.io/@bevy/required_components/%2FHwaPiH3GRWqxrEkCKRzXYQ
I like this, but IMO it should be added as follow-up
could probably be worked on in parallel though, if they are interested?
I'm poking at the macro rn and it seems like there isn't an easy way to differentiate a function path or value path
you probably want to disambiguate it with a different syntax, like
#[derive(Component)]
#[require(
FocusPolicy = FocusPolicy::Block
)]
struct SomeComponent;
or some other syntax
ya thats what i was going to try next. I assume though that passing a constructor function was the way it was setup up purposefully to prevent big defaults from cluttering the Component definition
might be able to use closure syntax:
#[derive(Component)]
#[require(
FocusPolicy(|| FocusPolicy::Block)
)]
struct SomeComponent;
Personally I don't think bundles should be replaced fully, they are still really useful even with required components
Theyโre still needed for actually adding components to entities, so the mechanism isnโt going away
Just most or all engine bundles
at least for user and library code. For example creating a bundle, with many different "default values"
Like, any component with a enum that has the None setting shouldn't be included as "required" since obviously they aren't required if there's a literal "None" on it
you probably want a requirement in the opposite direction in that case
Yea
Iirc @magic belfry was working on some PRs to replace some of the built-in bundles with required components, idk about the scope of those changes tho
I'm also not sure if "everywhere" is possible yet. There are some parts of bevy that have very suboptimal designs for the planned required components & BSN world so there might be some cases where it needs to be reworked first
I've kinda been waiting on Cart to give some form of approval on the proposals since I've been under the impression that I shouldn't make PRs until then (or until he takes a look at my branches, although they're not too polished atm).
I'll probably just open PRs this week anyway though, since I want to see actual progress here, and I feel like I'd be able to better give context and explore different approaches in PR form
The push for required components was driven by the needs of BSN. So if a component is expected to be used in BSN (especially hand written BSN) then a required component will likely need to be considered
Not necessarily just by the needs of BSN. Anything that doesn't need bundles for some edgecase will likely become required components and have any bundle version of it deprecated (with removal being a version later I'd imagine)
Yeah sorry I've taken a look at some of the branches and all of the proposals. I think the biggest open question is still "what do we do with materials and meshes"
I'll try to have strong opinions on that soon. Sadly lots on the plate right now
I've gotta frontload some foundation work this week
@split harness feedback as requested ๐
https://github.com/bevyengine/bevy/discussions/14437#discussioncomment-10674018
This second point is a good one, I generally think we need some more clarity on .bsn files vs the bsn! macro. Solutions like "callbacks as assets" or using entity names/paths to rever to observers seem a lot more sensible than creating macro-only features
Personal interpretation is a .bsn is just like if I had a .rs file that only contained the bsn! and was imported by main.
The only difference is a .bsn allows for editing the game without a recompile (assuming its not trying to use components that dont exist yet)
That's sadly not the case since some of the example bsn! features are only possible because it is inside rust code, while your running bevy app has no rust compiler nor enough debug info to compile callbacks in a .bsn file. Of course bsn could internally turn the inlined data in your macro into a list of inputs, and allow bsn files to similarly accept inputs, just by names rather than inlining rust code. I just haven't seen anything about input to bsn files
Passing callbacks as input might however be less flexible and benefit less from hot reloading than accessing registered callbacks by name/path
Yep, and shipping a Rust compiler to end users definitely feels like an opt-in feature at best
Although my game isn't currently using BSN, I have similar challenges around exemplars and aspects, which are assets that (among other things) influence the behavior of scenery and actors. The key design problem is how to encode behavior in a way that is serializable. There are two approaches: behavioral components that have tweakable knobs, and scripting languages. I plan to use both of these approaches - for example, you could envision a "Goal" component that contains a reference to a LUA or Rhai script module which is also an asset. So for example, the goal "Territorial" adds a set of prioritized goals to the actor's goal list, which causes them to aggro when the player gets too close to their spawn point.
The sweet spot here is being able to edit assets where various behaviors are built up by composition, by adding reusable behavior modules to an entity.
A lot of games aiming for moddability will definitely want a way to link a behavior defined in a scripting language to an entity in a BSN asset file.
I don't think those are "two different approaches", you can combine scripts with scene files that define behavior trough composition of reusable bits. BSN doesn't need built-in scripting, but it needs a way to refer to reusable behavior, and that reusable behavior could be from a script
Script as an asset type could be constructed using the Construct::Prop syntax described in the proposal. Past that it comes down to language bindings which I don't think need special syntax for BSN
Scripting would be awesome but I feel like it should be considered beyond the scope of BSN for now
Scripting is out of scope, referring to reusable behavior from a scene file should be in-scope tho
Doesn't really matter if it comes from a script or rust code
I don't think anything specific is needed to support scripting
#[derive(Component)]
struct Behavior(Handle<LuaScript>);
impl Behavior {
fn update(...) { // call lua script
}
fn aggro(
player_q: Query<(&Player, &GlobalTransform)>,
enemies: Query<(Entity, &Behavior, &GlobalTransform)>,
) {
for enemy in enemies.iter() {
let player = player_q.single();
behavior.update(player, enemy);
}
}
bsn! {
(
Enemy,
Behavior(@"my_behavior.lua"),
)
}
It is absolutely out of scope for now, the reason I brought it up was to illustrate an alternative to the BSN callback problem, which is also out of scope ๐
This is a mistake. I understand that they cover a use case pretty well (UI/Scenes) but bundles are fine for most cases and provide a useful structured way to manage components. Don't replace everything for something that that isn't well supported yet (required macro type definitions don't work on RR) when both can coexist.
The plan is to deprecate them first, and wait until the community is happy and tooling is mature to fully remove the first part bundles
Deprecate the current usage right?.. Not deprecate bundles themselves? Right?
Bundles themselves being just tuples of components right? And the current usage being named bundles with constructors as the blessed way to create a thing that has required components?
Yes. Bundles will continue to exist: only the first party ones will be deprecated
No more collisions between bundles with duplicate components 
Tuples are bundles sure, but I don't think the names bundles are going anywhere as a concept either for at least as long as we haven't found nice patterns for all edge cases required components can't handle
The main one being "I need to construct multiple components from a value that isn't in a format I store it" ... Like say calculating the mass and inertia of an entity by density and a shape
I was going to ask how required components are going to tackle things like SceneBundle that have spawning logics built in
I'm not sure if any first party bundles actually have this issue, but some end user bundles and crates have this issue (though there's also just as many crates that don't use named bundles at all, like avian)
Bundles don't have any spawning logic built in. SceneBundle just stores a handle to a Scene, and some systems later spawn the scene based on that. That doesn't really change with required components, just the API does
Replacing the usage of first party bundles, and throwing things out that never had much use should be easy, and bundles don't conflict with required components in any way. Inserting (Transform, GlobalTransform) does not conflict with either of them requiring the other, which means bundles can coexist with required components nicely
This is the main thing iโm excited for haha. I ran into so many issues with multiple bundles providing transform components when i was just getting started with bevy
yeah bundles the api isnโt going anywhere, the main point of the change is letting users add the components they want to enable functionality, without fear that theyโre missing some hidden dependency
Yea this is probably the most annoying thing about bundles, especially combined with the fact that rust has no nice way of expressing "This is a SpatialBundle with some extra stuff" which causes you to just put the same fields in all bundles 
Oh I had no idea, but that makes sense. One of my long standing questions was how do Scenes get spawned
You spawn a handle and in most cases it loads the scene during the SpawnScene schedule between Update and PostUpdate. It's not a nice system and hopefully something BSN will do better because this ordering makes scenes an abject nightmare for anything gameplay related
I also like how I had to dig trough code to find out how it's ordered now, because the function doesn't mention it, the plugin talks about SpawnScene, the docs on that refer to Main which doesn't mention it all, but luckily I happen to know it's defined in MainScheduleOrder (something the linked Main doesn't mention either)
Are required components dependencies overwritten or only missing components are added?
Missing components are added
Only missing components are added. If a mesh requires Transform, but the user spawns the entity with a specified Transform, it won't be overwritten
Similar to specifying component values for a bundle and using default values for the rest with ..default()
That's kinda my doubt, because if you insert a bundle on an already spawned entity it is overwritten.
Okay yeah in that sense it is different. But no, required components don't overwrite
It doesnโt work that way. In fact, thatโs pretty much exactly what itโs designed to address.
Nice. Thanks a lot!
@thick slate Can you direct me to where the documentation on required components is so i can unblock the closure PR
Per https://github.com/bevyengine/bevy/pull/14791/files#diff-221cfc82f177ee917343fde1601d0b7ebdfff6140cce435c39746bc3a1f896e2R100, it should be added as part of the docs on the Component trait.
thank you ๐
Dropping this โ๏ธ-Controversial (or X-Contentious according to Alice) issue here. I know the related issue on overriding required components is heavily controversial, and I agree that being able to remove requirements arbitrarily is bad, but I think purely adding requirements in plugins is needed and valuable.
https://github.com/bevyengine/bevy/issues/15367
Yeah, I'm close to saying that this is uncontroversial, but your two requests there can be split apart :p
The conditional requirements is more contentious due to the need for careful API design
But just slapping more requirements on at runtime via App / plugin code is totally consistent with how we do scheduling constraints
And should be pretty easy
Yeah, agreed. Should've probably split it into two separate issues, but the conditional requirement thing doesn't work without being able to define requirements externally :P (at least with this API)
I guess the alternative there is something like
#[derive(Component)]
#[require(
PhysicsTransform,
if(without(Static), Mass)
)]
pub struct RigidBody;
We could always add more derive macro DSL syntax, yeah :p
๐ฅฒ
But yeah, we should first add App::add_require that takes just a single component for the first type param, and add conditional requires in a follow-up
Oh right, we also need to consider how to do custom constructors... it probably needs a separate App::add_require_with that takes a method similar to the require attribute's syntax
app.add_require_with::<RigidBody, SleepTimer>(my_constructor);
I'll add that to the issue briefly
Made a comment
it's a great idea
the conditional requirements are probably a good idea but I haven't thought about all the edge cases hard enough yet
I love the plugin required component thing
The syntax and some edgecases (adding requirements that already exist) might be contentious but I think adding requirements isn't really controversially, it doesn't break any assumptions nor create new invariants that weren't previously possible. And it can save more dumb archetype moves that otherwise would've happened in mostly useless observers
What exactly are the plans for how/when bsn scenes would be spawned? We had some examples of scenes being spawned immediately from the bsn macro, but what about bsn from assets? Would it happen immediately unless the asset is unavailable, in which case it would happen after the asset is loaded?
Mostly curious since the current bevy_scene spawns things at this weird timing between Update and PostUpdate which works for visual stuff, but bsn would probably be used for a wide range of things within the same app, and I can't find anything about it in the proposal nor do I remember reading about that ๐ค
Question going off of the above that may have already been addressed, but it would be cool to be able to preload bsn asset dependencies so that you can quickly stamp out copies of the scene when needed (I'm thinking for using them as prefabs)
The question is, which assets do you preload or do you preload all of them? Seems like there are challenges with both. A reasonable alternative is the first time you add a prefab to a scene it gets loaded and subsequent copies are fast.
I'm fine with preloading all of them, not as a default though
But essentially an asset server function like, preload_all(bsn_here)
So you would explicitly opt in on a scene by scene basis
Yeah from an editor perspective this would definitely need to be an opt-in setting. Iโd probably enable it too because all my assets fit in memory.
Hrm from an editor perspective it's interesting, my thought process was, "if I'm going to shoot a bullet prefab, I don't want to wait to load the sprite on the first shot"
I could almost see a "dependent scene" resource or something that stores preloaded scenes you can opt into
Maybe not a resource
But basically if you load a scene, it can automatically preload prefabs for it
Responded!
Thanks! Yeah, I agree conditional requirements are probably much more controversial and require thorough consideration and design work.
I started working on a PR for runtime requires already. Got this working:
#[derive(Component)]
struct Bevy;
#[derive(Component, Default)]
struct FirstPartyPhysics;
struct MyBavyPlugin;
impl Plugin for MyBavyPlugin {
fn build(&self, app: &mut App) {
// Make `Bevy` require `FirstPartyPhysics`
app.register_component_require::<Bevy, FirstPartyPhysics>();
}
}
and register_component_require_with that takes a custom constructor like the require API
(names up for bikeshedding, requirement instead of require might read better here)
I'll probably have a PR tomorrow, mainly need to add tests and more docs
Making bevy require first party physics doesn't sound
at all
hmm this will be trickier than I thought because of recursive requires and ordering stuff
If I'm understanding correctly how required components work, a basic edge case here might be:
- Plugin 1 adds requirement
X -> Y - Plugin 2 adds requirement
Y -> Z
In this case, addingXshould also addZ, butZisn't in the list of required components forXsince plugin 2 was built later. I think I need somerequired_byhash set or vec inComponentInfoto propagate requirements up the tree as well
Yeah you're understanding correctly. Adds a bit more init work, but probably not so much to be worrisome
would be nice to have async plugin init, then they could all register their requirements, and wait for every one of them to be finished with that part to continue ๐ฅท
I think I might have it working now with recursive requires and everything... One of the trickier parts is handling different importance levels correctly, like if X -> Y and Y -> Z and you add X, then a separate X -> Z should be "more specific" than Y -> Z. The macro version handles this with just ordering and recursion, but at runtime there's no distinction between "direct" and "indirect" requires or priority levels. The way I'm currently handling this is by just tracking depths for the "requirement inheritance tree", which does add a bit of complexity, but it seems to work so far
(need to do a lot more testing tho, there are very likely edge cases to iron out still)
Alright, here we go! Might need a bit more testing, but it seems largely functional
https://github.com/bevyengine/bevy/pull/15458
my first time really touching ECS internals so lmk if I'm doing something dumb anywhere lol
(cc @tribal yacht, I think you wanted this for automatically adding SyncToRenderWorld for retained rendering world stuff)
Yes, I do want to use it that way
A small use, but maybe a nice bit of motivation for your PR
I think I'm comfortable moving forward with ports. The Mesh and Reflection Probe APIs still need some more thought, but I don't think they're unreconcilable. I've added (Selected) to the designs I think we should implement
~~the transform migration should be ready to merge then: https://github.com/bevyengine/bevy/pull/14964~~ (pending conflicts but Iโm busy for the next few hours)
@magic belfry I forget which of the items you made progress on, are there any others I could take?
I haven't done text, UI, sprites, picking, or iirc scenes
Sounds good, I'll start opening some PRs this weekend
Text is hopefully getting reworked https://discord.com/channels/691052431525675048/1248074018612051978 which should handle that.
FYI @magic belfry
I'll open a PR for Visibility in a bit, it'll make a lot of the other migrations nicer too since they don't need to require the other visibility components separately
I'll also deprecate SpatialBundle in the same PR if that's fine
Or actually I'll leave that to a follow-up, deprecating SpatialBundle will be nicer once sprites, meshes, lights, etc. have been migrated since I wouldn't need to manually add Visibility everywhere ๐ค
Yeah, that's a good order
Should we add C-Needs-Release-Note to all of these PRs? I'm assuming these will be combined into one or two sections in the post
Yeah I don't think we should add it
There'll be a section on Required Components already
Which will get into the end user changes
Reviewed and merged!
Awesome ๐ Those are the two most important required components PRs: should make cleaning up the rest easier
Currently ruminating on the Material and Mesh port
Just added a Proposal 3 and left some comments on Proposal 2
I think both are pretty satisfying answers
Personally, I don't think bundles are a good option for it at least (worse ergonomics + inconsistency since we won't use bundles much elsewhere), and the combined Mesh2d/Mesh3d proposal with the mesh and material in the same component has the query problem, so I landed on proposal 2, with Mesh2d/Mesh3d and MeshMaterial2d/MeshMaterial3d in separate components. It also produced the nicest diff in my experiments (note: the branch isn't polished but the core migration is largely done iirc)
And yes, my idea was also to display a default material when the material is missing (I don't do this yet)
So I like proposal 3 is what I'm saying
Cool cool. Proposal 3 also has my preference. I suspect we might want "unrendered material-less meshes" at some point, but those should probably have new components anyway
Lets select Proposal 3 (modified to account for Material2d and Material).
I kind of want us to re-unify 2D and 3D meshes and materials, but thats a separate conversation
(Done)
@split harness Did you have thoughts on the camera proposal? One thing I found to be kind of confusing in practice is that you'd expect to only need to add e.g. Camera3d, but quite often you'll also want to configure Camera, which is the core camera definition. So you end up adding two different camera components that look very similar based on the name.
Or more importantly, maybe you forget that Camera3d even exists, and assume that Camera is the 3D camera, especially since we drop the 3d postfix for many 3D types like Transform. And if I understand correctly, cameras without Camera2d/Camera3d don't do anything. I expect this to be a very common footgun with the current naming.
An example from my migration:
commands.spawn((
// Forgot Camera3d? Too bad!
Camera3d::default(),
Camera {
hdr: true,
..default()
},
Transform::from_xyz(-4.0, 5.0, 10.0).looking_at(Vec3::ZERO, Vec3::Y),
));
I would be inclined to rename Camera to CameraConfig or similar to make it clearer that it's not the true "defining component" here, but I'm not sure if that's entirely accurate either, since according to the docs it literally is the defining component currently, and it has most of the data (even though the proposal doesn't show it). I think we could also split it into several smaller components, but that'll quickly expand the scope of the refactor here.
Also re: audio, we can't call it AudioSource since it conflicts with the actual AudioSource data. We could do just Audio, or something like AudioHandle or even AudioPlayer
I like AudioPlayer
I have always found Camera to be rather odd considering there is no real component that actually defines the camera, and as a marker Camera2d/Camera3d are probably the closer ones ๐ค
Another thing to note is that Camera2d/3d are very tied to the core render pipeline
From what I remember
Theyโre basically configs for that
IMO in this case Camera is the defining component, and Camera2d/3d are actually markers for the appropriate render graph to use, though we use CameraRenderGraph to actually change the behavior there
Even Camera doesn't really define a camera well since it's split up into so many pieces, Projection being a pretty major one. Camera2d/Camera3d are the only ones that are really in a position to be able to require the rest tho ... It's kind of weird how Camera2d is a ZST, while Camera3d has data baked in tho ๐
The defining component is essentially always the most specific component. In this case, that is clearly Camera2d and Camera3d
Yeah CameraConfig would help resolve that confusion / help people reach for Camera2d / Camera3d. Definitely worth considering. Although for some reason my gut is fighting back against it. Haha lemme ruminate for a bit
for the existing api definitely, but I think we could do with better defining what Camera2d/3d are for moving forward
If we go that route, we should probably use CameraSettings over CameraConfig
they're not the entry points for 2d/3d cameras, but 2d/3d cameras for the default render graph specifically
There are instances of both in Bevy, but we're preferring XSettings these days
https://github.com/bevyengine/bevy/pull/15035
Innocent bystander comment: Settings suffix was already removed. And Config feels like the same kind of suffix.
I wouldn't say that. They default to the built in render graphs, but you can definitely set a different graph. The render app does prepare some resources under the assumption that they'll be used in the default graph, but i hazard to guess that in most cases they'd want those things set up anyway
I thought it was the opposite tbh :P GizmoConfig, GizmoConfigGroup, IntoSystemConfigs, WireframeConfig... but yeah fair enough
Worth discussing though. Definitely not saying we can't change things
it feels a bit weird, e.g. if you are using a custom CameraRenderGraph, maybe it's a good practice to defien MyGraphCamera component though
that's pretty much the workflow I imagine yeah
like ExtractedCamera == Camera, so it's kinda weird to have it be CameraSettings -> ExtractedCamera
at least regarding my thoughts in #1262090782211309629
imo
Honestly feel like we should be decoupling some of these common concepts from the render internals a bit better, with jondolf's PR to add requires that would also be viable now, while with bundles it would've been quite the mess ๐
Yeah if you're defining a fully custom graph, I think defining a new camera type is often the move
yeah, i think that makes sense, relative to the idioms of required components favoring nice marker components even if you don't require specific extra config e.g. for your graph
For the initial port (landing in the impending 0.15), I think the conservative "dont rock the boat" port is the move
yes
Feel free to come up with proposals though for whatever you think should happen next
(said PR is here, needs reviews!)
Yea that's totally fair for now. Eventually it might be nice if we could at least do basic things like defining a scene with cameras and lights without needing bevy's built in renderer enabled
Sure! One way to do this would be to introduce graph label aliases or something. So instead of CameraRenderGraph::new(Core2d) we use CameraRenderGraph::new(Default2d)
Or we could just move the Core2d label out of bevy_core_pipeline (and maybe rename it)
And same with the Camera2d / Camera3d components
Unless you have a concrete use case, I would not be in favor of this. The split between bevy_pbr/bevy_core_pipeline already causes problems sometimes.
The main usecase would be the ability to disable pbr shading or swap out the renderer entirely without losing half of the engine and the ecosystem, and needing to update all your scene files, because everything is coupled to the renderer or bevy_pbr in one way or another for common concepts like a mesh, light sources, cameras, etc ... But I do agree that we shouldn't be adding on to the already confusing boundaries of the rendering crates, and probably have this be a part of efforts to improve it ๐ค
sprites should be done unless ci/check-doc decides I did something wrong again: https://github.com/bevyengine/bevy/pull/15489
this PR easily doubled my speed with vim 
Here's the big one:
https://github.com/bevyengine/bevy/pull/15524
@split harness fyi
is there a ticket for deprecating Handle as component? this is kind of churny outside of just changes spawn sites so it would be nice to communicate rationale for impending change to how handles work here
ah i see you want to do it in 0.15
I remember seeing a ticket but it's a long weekend today ๐
i agree with the rationale in the pr that the bandaid should be ripped off if the desire is to move in that direction
that was my concern ๐
I linked a related issue
https://github.com/bevyengine/bevy/issues/14124
but I don't remember there being a comprehensive one with proper rationale. It has been discussed though, and the idea has been largely blessed by Cart and Alice
nice that inventory is helpful to see
I also had this related comment #1282366304057036891 message
Do we need an SME review for the mesh/material required component PR? It's mostly an ECS api change IMO and there's a ton of rendering PR that I need to get to so I'm trying to figure out the priority list for myself
Nope, I'm fine without it
Welp, I couldn't help myself. I looked at it and I have some opinions so I'll leave a comment ๐
i totally agree with the 3d suffix bikeshed but didn't want to say anything lol. i also have been a bit confused about plans for the 2d vs 3d split, because as you mention it mostly seems like a question for higher level apis. like it's really more a split between pbr and non-pbr render graphs in that sense. for example, in nannou, which is mostly a 2d api with an ortho camera, we use the the 3d render graph even though it doesn't really make sense relative to our use of pbr features, i'd like to move to the 2d graph, but it feels too coupled to sprite stuff right now
Yeah, other than sprites, 2d should essentially be 3d with orthographic projection (at least at the renderer level). At least that's what I want but I don't really have a super clear path forward. I'm mostly annoyed at how much code I copy from the 3d pipeline every time I try to improve 2d stuff.
Personally I feel the opposite way about the 3D suffix for APIs. Viewing 3D as "the default" at the API-level seems wrong to me, since it makes 2D feel like a second-class citizen in comparison. I like Godot's consistency here, like how it has Transform2D/Transform3D, Sprite2D/Sprite3D and so on. Leaving the suffix out also implies that it's a sub-type and not its own thing, for example if there were RigidBody2d and RigidBody types, then it sounds like RigidBody2d is just a kind of RigidBody, which isn't necessarily accurate imo.
Bevy itself is also wildly inconsistent in its use of suffixes at the moment
Also we do suffix tons of things with 3D already: Camera3d, many primitive shapes, 3D AABBs, numerous helpers...
My point wasn't to say that the 3d suffix is bad. Just that introducing it here now also introduces an inconsistency. Bevy is inconsistent with many suffix, but AFAIK we don't suffix 3d anywhere currently
Oh, right, camera3d
To be clear I also don't like things that are suffixed with 2d. To me rendering is always 3d anyway. 2d is just 3d with an axis we try to ignore sometimes.
i feel like in a perfect world we wouldn't even have camera2d/3d, since it's really just a convenience to help figure out what view bindings should be used and whether to do lights, which we could also determine based on whether any entities with a given pbr/sprite material share a render layer with that camera
Camera2d/3d are used in a lot of places as filters for preparing pbr resources
right, but that's mostly a performance thing, it's an implementation detail
I mean Camera2d/3d aren't the defining camera type anyways (at least not what specifies the actual camera parameters)
they're markers for the rendering path to use
agreed that they're not necessary, or at least need a rename
right, which is more a function of the entities being rendered than the camera. like there's no intrinsic reason that you couldn't render a pbr material and a color material using the same ortho projection
not saying we should refactor this or anything but i think speaks to some of the ambiguity here of 2d vs 3d
we agree here ๐
I misunderstood
it comes to mind, because in creative code, we actually often will do things like use a "constant" (color) material in a 3d space, which wouldn't make any sense for games but does for creative uses
I don't want to derail this thread too much. I wanted to voice my opinion just in case someone comes up with something better, but I'd still prefer seeing required components with 3d suffix than sticking to bundles. This can always change in the future if we ever find something better but this will likely be part of a bigger refactor.
yes, sorry for getting too off topic
slightly more on-topic but not entirely: why MeshMaterial3d rather than Material3d?
i went back and forth on this, right now the material render command assumes you are using a mesh when queuing the material, but personally i'd like it to be possible in the future to specify your own render command using the mid level apis for a material such that you could manually supply the vertex buffer yourself
Going with Material3d could cause conflicts for the 2d path because there's already a Material2d. This way 2d and 3d can have the same name (except the suffix)
(just a guess to be clear)
does that affect the material component itself though? All you'd have to do is register custom draw commands I think
though I guess you want to skip a lot of the prep work in MaterialPlugin, etc.
it's not currently possible
hence why all our stuff is like queue_material_meshes
in that sense, the api is the "mesh-material" api, not just the "material" api
Mostly because that's what the selected/blessed API was, but also because the 2D version would conflict with the Material2d trait (like IceSentry mentioned)
although it sounds like per consistentency the material trait should also eventually be Material3d
Yup, assuming it can't be reused for 2D (which currently it isn't)
We also need a followup for MeshletMesh
I can probably prep one in a bit, although I'm not sure what naming to go for ๐ค
Since MeshletMesh is the actual asset type
I guess MeshletMesh3d to be consistent with Mesh3d, but there's no 2D version so idk
Ignore 3d, just MeshletMesh imo
Or rename MeshletMesh to MeshletMeshData or something
so the component can be MeshletMesh
I haven't kept up with the other PRs in this area, but when you have something request my review and I'll take a look
i.e.
#[derive(Component)]
pub struct MeshletMesh(pub Handle<MeshletMeshData>);
Just started looking at this. Should wrap up my review after lunch.
Cool, thanks! I now have another, smaller one ready as well
https://github.com/bevyengine/bevy/pull/15554
Wrapped up my review. Just two small comments
Reviewed / approved / merged
So excited for the improved clarity / terseness of bevy code. Once everything is ported everything is going to be so beautifully simple
The reduction in bundle clutter and improved performance characteristics make me want to port my game to main ๐
Give us like... 5 days?
tooooooo looooooooong ๐ฉ /s
I would need to get DQF merged before I can port to main anyway 
Not to mention remove that one bit of really awful code that would block the single biggest gain from required components my codebase could get 
Thanks, I'll fix those up in ~9h when I'm back home, and open a few more of these PRs as well
Fixed-ish, not sure how to address the material conflicts with some vertex attributes or if it should be handled in a follow-up
(also I guess the problem could be something else entirely, it just happens specifically when I use different vertex color formats so I'm assuming it's related :P)
@magic belfry I left some feedback on the mesh/material migration PR
Also, can I do the scenes migration PR? Or do you already have a bunch of PRs cooked up locally :o
Yeah, I was going to ask if you'd like to do it since you had a somewhat related PR already ๐ I haven't done that one yet, so feel free
Required components train ๐
sprites as well once I fix merge conflicts ๐ฅฒ
I think this is all I have done right now ๐ Or I do have the camera migration 95% done too, but I need a blessed solution to the problem of Camera vs. CameraSettings vs. something else
oh and render probes too
I had them in the light PR originally, but cut them out since there's no chosen API for it
After those, we'd only be missing sprites, scenes, text, picking, and UI. And the first three are being worked on by people
picking should be a quick and simple PR at least
after I fix sprites I can pick up picking, though I don't have a ton of experience with that side of bevy
ah ok
I could probably do UI without the text part and hope #1248074018612051978 gets that part done to some extent with the text rework
Would we also be able to
Component from Handle at that point? ๐ค
I think so
It's still being used internally in bevy_gizmos somewhere. Might be other places
The list as of March was this
https://github.com/bevyengine/bevy/issues/14124
and all of these would be in a component I think. But yeah idk if they're used elsewhere internally
UI mostly (texture atlases), I cleared it out of sprites
Should be easy to find them once the PRs are done and we try to remove it ๐
yup
@magic belfry If the PR description looks familiar at all then you must have imagined it c:
Just created this issue / added it to the milestone: https://github.com/bevyengine/bevy/issues/15580
There's something already built here: https://github.com/bevyengine/bevy/pull/15026
Oooh excellent
FYI @ebon river
Definitely worth thinking about the removal logic. The naive "remove all requires" feels like a tool we want, even if it has footguns (given that it is a direct replacement for the bundle equivalent, which also suffers from that problem).
We may be able to build a smarter solution that handles most cases "safely" but I'm dubious
At first glance it looks like this PR tries to tackle both problems?
That's fair. Had not considered that when I complained ๐
The first version of PR contained only a footgun solution. After comments, safe versions of this method were made and the footgun was removed. I can tidy up the PR tomorrow to have both the footgun removal method and the โsafelyโ removal method in there
I think my preference is to remove the "safe" impl and merge the "footgun" impl on its own first. Having now looked at the code, computing that information on the spot is too expensive. I'd prefer it if we waited for a cached / full solution.
Will prepare a finished PR with footgun solution then tomorrow. Foongun's solution is fast.
The safe solution is indeed costly, and I didn't dare to implement caching, as it would go beyond the ะกommands struct.
Cached solution as in keep track of what components on an archetype aren't required by other components, then only removing those?
Yes, I suppose
That would be a pretty useful tool then yea, there's a few examples with some ugly code that could've been avoided with a decently performing "safe" solution
But yea I do agree that having the "footgun" version is necessary for feature parity and a decent migration
BTW, the migration to required components for scenes was very straightforward and should be easy to review :)
The diff is nice to look at as well :o
Haha, nevermind
And merging https://github.com/bevyengine/bevy/pull/15573 too ๐
And merging https://github.com/bevyengine/bevy/pull/15572 ๐
Yeah storing computed work in the archetype so we aren't doing expensive computation / allocation per entity. However before moving forward with any solution I think this needs more thought (see the "alternatives" section of this issue: https://github.com/bevyengine/bevy/issues/15580)
As a heads up to everyone else, I'm spending most of this week reviewing @rare whale's reactivity work, with the goal of establishing how to best integrate those ideas into BSN
My initial plan was to focus on getting the BSN baseline impl ready for people to look at, but Dreamer has been waiting long enough that I'd prefer to make some forward progress there first, in the interest of hopefully unblocking the ability to work in parallel toward a common result
Ah yea, if we want to make it real nice we'd need to track "things that got added manually", kind of like how package managers work with dependencies ... But either way we wouldn't want to be removing components that that are still required by something else. The biggest example is that a naive remove required would constalty remove your Transform ๐
https://github.com/bevyengine/bevy/pull/15026 footgun method for remove bundle and all required components by bundle is ready for any review
Now that the RC is delayed, I'm feeling a bit more experimental;
So, for the camera required components migration, we still have the issue of Camera sounding too similar to Camera2d and Camera3d. I imagine many might just use Camera on its own, especially as the docs state that it is "the defining component", which will be a huge footgun since it won't do anything without Camera2d or Camera3d afaik.
One option I proposed was that we could rename Camera to CameraSettings to make it clearer that its not the primary component for adding a camera. However, I don't think this is entirely accurate either. Overall, the component is currently treated as the "core" camera component, and even still, there's a lot of camera settings outside of the component as well.
I don't know how controversial this would be, but: could we just split Camera into several components? Instead of this:
pub struct Camera {
pub viewport: Option<Viewport>,
pub order: isize,
pub is_active: bool,
pub computed: ComputedCameraValues,
pub target: RenderTarget,
pub hdr: bool,
pub output_mode: CameraOutputMode,
pub msaa_writeback: bool,
pub clear_color: ClearColorConfig,
}
There would be something like this:
#[derive(Component)]
#[require(RenderTarget)]
pub struct CameraViewport {/* ... */}
#[derive(Component)]
pub enum CameraOrder {/* ... */}
#[derive(Component)]
pub struct ComputedCameraValues {/* ... */}
#[derive(Component)]
pub enum RenderTarget {/* ... */}
#[derive(Component)]
pub struct Hdr;
#[derive(Component)]
pub enum CameraOutputMode {/* ... */}
#[derive(Component)]
pub struct MsaaWriteback;
#[derive(Component)]
#[require(RenderTarget)]
pub enum ClearColorSettings {/* ... */}
If we wanted, a CameraMarker component could still require most of these components, and then Camera2d and Camera3d could require that. A CameraQuery world query could help keep large queries manageable, if there are any.
In my opinion, this is more ECS-like and consistent with how we generally compose features and configuration. It makes queries more granular, moves the camera definition away from a central component, and fixes the original issue of there being a confusing Camera component.
With required components in place I'm a lot more comfortable moving towards this sort of design
We probably need approval from Cart and rendering people for something like this, but I'll start working on an implementation to test it in practice
I'm not sure how I feel about things like Hdr. It would make it a bit harder to discover, but I guess that's true for anything optional. Other than that the idea seems good
And it's nice to have more things in the ECS
the queries in the RenderWorld are gonna be massive though ๐
Splitting viewport from computed values will make the world_to_viewport methods trickier to place
Also, how would the is_active thing work? Would it be a component to make it active or inactive?
Things like Bloom, ChromaticAberration, MotionBlur, etc. are already like this, so imo this just makes things more consistent. And of course there can still be some central place for documentation for these things, along with ## Related Components sections for the core camera components
Interesting thought! Definitely worth considering. I'm more and more biased toward unified / bigger components as i think it makes discoverability and ergonomics better from a user perspective
Ex:
"where does this 'common' camera property exist?"
"Oh yeah thats on the Camera component"
Probably yeah (or wait for component disabling :P)
Although in this specific context it may be worth breaking some things out
A CameraQuery world query could help keep large queries manageable, if there are any.
Imo any API that needs a custom query to make it manageable should be rethought
I don't think we ever want all of the camera fields at once ๐ค
(in the context of data layout)
I meant a QueryData to group the components together for some queries, is that considered bad? And yeah, I doubt you'd often need all camera components anyway
I consider it undesirable, as it increases the complexity of an API. Users then need to know that the query exists and know which components it pulls in. With this, there are then two official ways to access a given camera component:
Query<&CameraViewport> and Query<CameraQuery>, and when to use what is arbitrary
I intended that it would be mainly for internals, not public APIs
I can't think of when users would need more than three of the components for common use cases
Ah gotcha. Better, but imo still undesirable for complexity reasons. It should always be a last resort to eliminate large unwieldy queries. Imo it is (often) better to build an API where that isn't necessary in the first place
Would a better name help? CameraOptions or CameraConfig or CameraParameters?
That's what my earlier proposal was, but I'm currently unsure if that's the right direction
In general I think the "API design rule of thumb" should be:
- Start by identifying a "core concept" (ex: in this case, a Camera)
- For all properties common to that "core concept", add them to a component of that name.
- For "optional" opt-in functionality, consider using a separate component (but in some cases it may still be preferable to use an Option<T> field)
Worth mentioning that there's three or four optional/boolean properties in Camera
Yeah we can definitely rethink whats in there currently
Whatever the final solution is, renaming the Camera to something like CameraParameters is nice.
And splitting the things up in multiple Components will be very detrimental in terms of discoverability, and even more so with the required component thing.
Since that eliminates any kind of discoverability from LSPs
That was previously provided through bundles
I'm starting to convince myself that Camera still makes sense. It is behaving as the "core camera type", and I think it would be good for people to think about it that way
Another name CameraBase - because it's the base of both Camera3d and Camera2d
In the C++ or Python world, Camera would be an "abstract base class", and have a name like "AbstractCamera".
I think the difference here is that Camera is not abstract. It is a concrete type with "driving" functionality
Yeah, I have definitely reached for camera, when I wanted camera3d
Of course, the nerd in me wants to point out that cameras are just a metaphor anyway, it's really just a set of constraints on rendering, there's no lens or image sensor ๐
Bawhaha good luck convincing developers they don't want cameras
Maybe HDR and such are the lenses?
Not yet at least. I'm sure some people would like some lens simulation built into cameras
Galileo will be weeping when we re-introduce chromatic distortion
We do ship it as part of the post processing shaders now ๐
Disabled by default fortunately
Perhaps we just need to provide a warning when someone spawns a standalone Camera?
Functionally that would amount to "you spawned a camera that doesn't have a render graph configured"
Ex: "Entity {entity} has a Camera component, but it doesn't have a render graph configured. Consider adding a Camera2d or Camera3d component, or manually adding a CameraRenderGraph component if you need a custom render graph".
I'm pretty against the concept rename, as it feels like a rejection of the idea that "Camera2d is a Camera". We should have a Camera concept
We've built a system that operates under the assumption that Cameras exist. And game developers expect a system where Cameras exist
mm I don't love it but I can't really verbalize why, it just doesn't feel ideal to me... but I could make a PR with that to get the initial required components migration going at least
feels wrong to configure both Camera and Camera3d :P
commands.spawn((
Camera3d::default(),
Camera {
hdr: true,
..default()
},
));
There are like 50 instances of this in Bevy's repo. For both clarity and ergonomics I'd prefer something like this
commands.spawn((Camera3d::default(), Hdr));
Which is why I suggested splitting Camera into several components
hdr I can imagine being split
Iirc we have ambitions to enable HDR by default, which would make this particular case moot
but the rest feels like it might make things in the render world painful
It is still kind of a fundamental camera concept, in the context of the current implementation
yeah
I don't like the situation with Camera and Camera3d/Camera2d, but I don't think splitting things to this extreme improves things
I can imagine a world where it's instead handled by Camera2d/3d since it's part of the render graph config, but that's not how things work currently
Ideally the vast majority of cases look like this:
commands.spawn((
Camera3d::default(),
Transform::from_xyz(0., 0., 0.),
))
I like this approach personally
If most cases are like this, what's the cost of calling it CameraConfig? You reach for the config when you need to configure something.
Doesn't need to be an extreme split with every property in its own component, but some form of granularity and splitting based on logical concerns. To me Camera looks like a bunch of random properties thrown together with only a few being tightly related, but I don't know rendering so they probably all relate in some way
It implies that this is configuration for some other camera concept, rather than the thing that defines that this is a camera
they're all pretty much related
The X requires Y "inheritance pattern" (aka "X is a Y") is something that we will see increasingly. This conversation kind of feels like a rejection of that concept.
(one of the reasons im holding this line so hard)
I agree in theory but I see it as pretty footgunny and implicit in cases like this where the names are so close
Transform isn't a GlobalTransform
oh yeah true. If I want to define a custom camera component for whatever custom rendering thing I don't want to remember to require a bunch of different stuff rather than just Camera
Feels like "RedPlayer is a Player / RedPlayer requires Player" is the exact same category of thing / suffers from the same "problem"
But its also a pattern I think we should encourage
In this case its only a problem if we don't provide guardrails
I'll happily reject inheritance. Required components looks more like composition than inheritance. You are collecting components and their behaviors into your final 'thing'. So Camera3d is your 'camera thing', and CameraConfig or CameraBase is gathered as part of your final (conceptual) object.
What about cases where your Base is also a viable thing
(aka this case)
That's the nice thing about composition. You can compose things into other things :).
they aren't pure data though. A lot of rendering systems filter for Camera to prepare resources and set things up. So regardless of whether you call it that or not, Camera is what defines camera functionality
In both of your cases, talking about spawning a CameraConfigor a CameraBase as the "top level concept" is pretty unsatisfying
You aren't spawning a CameraConfig. You're spawning a Camera
Then I'd promote CameraBase. The name itself invites you to build your custom camera.
if a name change is in order I don't think it should be for Camera
I agree that this is preferable to CameraConfig. But I still think Camera is optimal.
You mean something like Camera2d -> CameraMode2d?
I feel like this is something that is becoming a bit more pronounced.
For example, I am not a rendering dev, I am using a game-engine to produce 3D applications. I have never wanted to spawn a Camera, I have always spawned a Camera3D, then it just happens to require all the things from Camera
The problem with Camera is the name implies you can just spawn it and viola you get a camera. When in reality you need to add other stuff to hook it up right.
its a matter of perspective
looking from inside the "Game Engine" and out on the application. Tends to give quite a different perspective as to looking into the "Game Engine" from the application
I'd want to rename the Camera2d/3d components yeah. It's tough though because what they represent is changing the rendering path/graph you use, but what we actually want to convey is "this is a 3d camera"
What I imagine a user would want (and what I would want) is to just spawn a Camera2d and then add optional components to configure that camera. They shouldn't need to care that rendering actually uses a Camera, and that Camera2d really just enables some rendering pipelines to facilitate 2D
I believe this entire discussion forum is by right mostly composed of people being inside the "Game Engine" looking out onto applications, since the actual Game developers seems to be a minority, and not very vocal
Camera3D + Camera is a 3d camera
yeah, and for better or for worse I think it should stay that way
Imho, this would be where bundles would still make some sense, because Camera3D without the Camera is... nothing?
hence the requires I assume
I'd love a world where it's more clear that Camera3d is specifically a hook into the built-in pbr stuff, but anyone doing custom rendering is going to know that caveat anyway
PBRCamera and then every user would be confused ๐
So since we can't (imho) rename/split anything without pain in other areas, a warning that you haven't configured a render graph is the best way forward I think ^^^
or a world where Camera3d is optionally less of a hook into the built-in pbr stuff
you're going to end up with a component for configuring your render graph no matter what, really. And I can't really imagine a situation in which you'd want that to be portable. The one change I'd like to make there would be to formalize that relationship (but that's a discussion to be had in #1262090782211309629)
yep, reasonable. i feel like the way to do that might be to add a runtime required component to Camera3d in the PbrPlugin
idk
would prefer if it was explicitly tied to pbr
Again in my mind there shouldn't necessarily be just a Camera component, but rather different components that describe a camera in a way that doesn't have the confusing semantics. I.e. split out the viewport stuff, render target, camera order, and so on in some form (where possible). Then to define "a camera", you compose it out of those components, or in the case of Camera2d and Camera3d, you just require them.
I don't think a camera should necessarily be described by "this has Camera" but rather "this has the components that make up a camera, driven by CameraX".
I think down that path lies pain for the rendering devs
also, if this is mostly about specification from a user perspective, doesn't the fancy bsn patch stuff avoid that entirely? (misunderstood earlier point about (Camera3d, Camera { hdr: true } )
i get where Jondolf is coming from. The X is a Y pattern works really well for UI and other areas where we do have a basically fixed set of behaviors, but do to this for Camera3d would really have the effect of locking down the modularity.
the key difference is, i think, that game engine systems don't need to update the camera data.
users configure the camera data, and that causes various other engine systems to run.
this is a pretty different control pattern, compared to say Node where user and engine systems are going to be updating the state all the time, or GlobalTransform where it's an 'output' of the engine.
We are planning to do basically exactly what he is proposing with transforms, i think.
I'll make a PR for the approach Cart suggested, simply leaving things as is but emitting warnings when a Camera is inserted without Camera2d / Camera3d / some other pipeline. I still don't love the approach though, so I'll probably experiment with what I'm personally imagining later.
This would really fix the ergonomics here. Setting hdr: true and maybe order is 100% of all my camera configs ๐ค
what if there was a with_hdr() method on Camera3d that set hdr to true on the Camera using a custom constructor on the require? (which iirc is a thing) ๐ค
Can the custom constructors match on their source?
I'm not sure. I think they only allow for deriving the value from the world
unless you could pass an In to it
ah, they're simple argumentless functions actually (at least in the required components PR example), nevermind
FYI @split harness I suspect you didn't see this PR, but I'd like to merge it now that it's rebased and documented: Closures for required component defaults
I actually did a very quick pass earlier. Just approved. Merge away
kk here it is
https://github.com/bevyengine/bevy/pull/15641
the diff is pretty nice (but I still dislike having Camera and Camera2d/Camera3d in the same spawn
)
the earlier example of "RedPlayer is a Player" makes sense, and the same logic works for cameras, but I would also find it weird if I had to add both RedPlayer and Playercomponents manually to configure something
and not just add RedPlayer that requires a Player marker, and have config in separate components
anyway, I'll stop arguing about this for now and get some sleep
Reviewed and approved!
Imo this is just a downside of implementing inheritance patterns on top of composition. It has pretty significant benefits (especially in languages like Rust without traditional inheritance), but it also means people need to address multiple levels of the "inheritance hierarchy" at the same time.
If someone creates some new:
#[derive(Component)]
#[require(Camera3d)]
pub struct MyCamera3d {
}
They will also encounter the MyCamera3d, Camera3d, Camera "conceptual stutter". Imo its just something people will need to get used to (and using the pattern internally for things like Camera3d will help normalize it).
It has pretty significant benefits
Ex: even if Rust supported "traditional inheritance", it would still probably make sense to implement this with "compositional inheritance" for queryability and memory layout reasons
I'm a bit nervous about the amount of discussion orienting around inheritance, it feels like the use of inheritance increasingly comes under scrutiny these days, to the point where the language we are using has opted to omit it entirely. Some, including me, generally encourage the avoidance of all behaviour & data inheritance (at least in public APIs), only encouraging interface inheritance. Are we potentially going down a dangerous road by trying to introduce inheritance concepts into Bevy design/discourse (discussing "is-a" relationships?), e.g. worrying about "RedPlayer is a Player", "BaseCamera", etc.?
I don't think inheriting data (like what BSN would support) is as much as an issue, but I think it's less about "focusing on inheritance" here, but rather "The API we now have resembles inheritance, which isn't great with our current pattern"
Or well, it works fine, it just looks stupid ... Which is probably a good thing, because it isn't the ideal pattern
I feel this kind of inheritance is acceptable. You can reskin it a different way but at the end of the day itโs just a format to define entity structure. Whether you โinherit RedPlayer.bsnโ or โadd RedPlayer.bsnโ via a component/strategy/other pattern, it still has to figure out how to apply those templates, allow overrides, etc.
And it all still flattens out to just components once spawned
And Iโd rather reduce duplication across BSN files than have to re-define components multiple times
Yea as long as we're still doing composition it shouldn't really matter, all it is is components with slightly ugly stuttering names if they all get listed
Like MyCamera3d, Camera3d, Camera looks bad but it doesn't really matter
there is a very natural link between ecs composition and inheritance: if one component requires another component, then it's very similar to a multiple-inheritance system.
what we don't have are interfaces, abstract classes, or mixins, as far as I can see.
A lot of the problems with "inheritance" are its relationship with wider OOP patterns, in this situation it's a looks-like-oop-inheritance behaves-like-composition thing, or am I wrong?
i think that's it. it is just composition when it comes down to it, but it's composition which does lend itself to the "parent/child" or "is a" semantics. and because OOP has been around for a while, I think it's natural to use the language it introduces for dealing with "requirement relations".
trait queries, or required trait components would start to push us down the actual OOP path, but there's not much interest in those (and the latter may be impossible).
the requirement relationships between components are actual data dependencies, so I don't think it's really the bad part of OOP inheritance
instead of a behaviour-that-overrides-behaviour-that-overrides-behaviour approach, it's behaviour-that-sits-beside-behaviour-that-sits-beside-behaviour
(apologies for the semantic satiation)
(as well as data-that-sits-beside, but that's true across both compositional and object oriented inheritance, afaik)
Sits beside or replaces
if anyone wants to take a look, this diff somehow broke sprite rendering and I can't seem to figure out how it's not an exact port: https://github.com/bevyengine/bevy/commit/f4a628a1dfde71e155bb7fabd3d7717f51ef8b65
It worked before the changes to texture atlases though, which is strange. I'm not the most familiar with sprite code but I feel like I should be able to spot this ๐
I think you need to require SyncToRenderWorld on Sprite too, it's a new thing so it's not showing up in that diff
yep, thanks!
And I was even a reviewer on that PR 
sprites are done! https://github.com/bevyengine/bevy/pull/15489
wrong link?
Oops, fixed
picking's done as well: https://github.com/bevyengine/bevy/pull/15690
so much easier to migrate a bundle with less than dozens of uses lmao
Thanks a ton ๐
@fallow cloak do you mind taking a look at porting meshlets, and figuring out why they're not rendering on main ATM?
I could take a look yeah
Canโt promise anything though, starting tomorrow my week is incredibly busy
Np
ok yeah this is strange. They're getting rasterized and written to depth properly, but getting completely clipped from actual rendering?
Heck? I would've thought it was just something dumb missing component or something causing rendering not to run.
Maybe try bisecting to find the commit that broke it
that's at least what I'm seeing from the example, I haven't even gone into debugging land yet
My beatiful bunnies, like what they've done to them D:
reviews for sprites pls? ๐ฅบ trying to avoid more merge conflicts if possible 
There are 2 different sprite PRs, is there consensus about which one we're going for?
mine is blessed according to the hackmd proposals but feel free to review both and see which is better
I like the way I implemented it though, splitting Sprite up into two components feels weird
The other one was a separate way that wasn't listed on the hackmd iirc? But I'll just review yours then.
yeah the other one wasn't working off of the hackmd stuff
naurrrr there's already more conflicts ๐ญ
ok that wasn't too bad. fixed!
what's left before we can remove impl Component for Handle<T>?
Try it and see what breaks lol
Yea remove it and check the errors
meshlet meshes and materials at least, and sprites (which you have a PR for)
meshlets might not give errors in editor since they're behind a non-default feature flag
I think UI used it as well, though I forget if that's been migrated
it hasn't, I was told to wait for text rework + Interaction 
but I might do it tomorrow or on wednesday anyway even without those
meshlets 
Looks like the query_gltf_primitives example somehow made it in without being ported to the new materials/meshes rework
I'm making a PR to remove Handle component usage from inside bevy_gizmos, will take a look at animation after
Probably because the Handle<T> still counts as a valid component 
Yeah, almost forgot to remove the line where the handle was being inserted in my PR just now ๐
Made a tracking issue for yeeting the component trait impl from Handle<T> https://github.com/bevyengine/bevy/issues/15716
Why is handle as component considered an antipattern btw?
they don't indicate what functionality they enable usually
look at the billion bugs it has caused us during this migration 
Or -- for most assets -- they don't have any functionality to begin with
in some places adding a handle component will do something, other places it's a requirement that other components have implicitly
you also shouldn't really ever have a system that queries all handles of a certain type, since they're probably used in different contexts
The scariest one I've seen is when plugin A exposes an asset but no functionality, and then there are two separately developed plugins (B and C) that both do something with Handle<AssetA>
"Oh you didn't want this mesh to be a collider AND rendered? Well that's unfortunate"
Not to mention the issue of not being able to define required components on them (tho at least Jondolf's PR added a way to do that anyway, even if it is extremely hard to discover)
btw, I've caught another bug caused by this issue https://github.com/bevyengine/bevy/pull/15719 ๐
And then ignoring the inherent issues with handles as components; specifically in the context of this migration, previously you could have a Handle<Mesh>, but now it must be wrapped in a Mesh3d because of required components (meshes must require e.g. transforms and visibility, and you can't do that with handles, unless adding runtime requirements). This change is a huge footgun and migration pain as long as handles are components, since all queries and inserts that use the handle are now implicitly broken with no warnings whatsoever. We've already had like 3-5 bugs merged into main because of this
ooh fancy subissues
Yeah, I normally would have punted on fixing this, but the migration is so bad
it's like having f32 be a component. a handle is unlabeled data
i see
I mean it's not exactly the same, like if we had f32<Speed> (similar to Handle<Mesh>) then it does have some associated semantics given by the type argument. But yeah it's still not good either, and like Nise said, you could want e.g. both meshes and colliders to use mesh handles, but different ones, which requires wrapper components
ig with Handle<T>, the T could itself be labeled data. but it could also be unlabeled data like f32, so the blanket impl is problematic
You might want someone else to double check this, but I suspect that in the case of AnimationGraph, the handle could just become a field of AnimationPlayer
Oof, look like Handle as component is tied pretty closely to a bunch of ExtractInstance stuff ๐ฅฒ
I'm kinda confused though. It doesn't look like that can even function at this point 
https://github.com/bevyengine/bevy/blob/0c959f77007c29eead7f902bddd3342a1ecbca20/crates/bevy_render/src/extract_instances.rs#L138-L149
Another benefit of wrapper components is docs. With Handle<Mesh> you can't really have any docs unless you put them on some module, but with Mesh3d you can
This could be changed to take a generic type storing/returning the handle presumably
what change made it so that Handle<T> components break everything?
required components
I considered reworking the ExtractInstance stuff for the meshes + materials migration but took the easy route and just made the material plugin have custom extraction logic :P it's kinda needed there anyway iirc
Oh isn't that extraction plugin only added by LightProbePlugin now?
we can easily replace that logic I imagine
and
the plugin maybe
Oh, yeah. It doesn't look too bad
I'm hitting errors from the UiMaterialPlugin so that will have to come first then :u
working off bevy main and noticed Observer is no longer generic which breaks queries like this:
Query<(), With<Observer<MyEvent, ()>>>
is there a new equivelant to querying for observers of specific events?
moved to #ecs-dev