Add the ability to expose nodes for direct access in instantiated scenes#84018
Add the ability to expose nodes for direct access in instantiated scenes#84018yahkr wants to merge 4 commits intogodotengine:masterfrom
Conversation
|
This will work with imported gltf scenes? |
|
Also please update your branch by rebasing instead of merging, important skill to get used to with contributing, see the pr workflow for details |
aacbd8c to
c85dded
Compare
3d44605 to
ac26465
Compare
ac26465 to
2144053
Compare
|
You have reset your branch and this closes the PR, if you update your branch this can be reopened |
|
Sorry, still trying to get a grip on correct way of updating my repo from main while keeping my changes. I've pushed my new changes up. This includes a somewhat functional version of this pr. |
3233eac to
5257a35
Compare
5257a35 to
03e1647
Compare
AdriaandeJongh
left a comment
There was a problem hiding this comment.
This PR has been iterated on and scrutinized massively over the last 2+ years. I'm only joining the conversation now as a new member of the usability team, but I read every comments on this PR, tried the PR a bit, and I think the summary is that this PR adds the ability to 'expose' specific nodes in a scene so that when the scene is instantiated in another scene, you don't have to enable Editable Children to see that node and make changes to it – in theory, keeping the hierarchy of the scene tree cleaner, and allowing you as the game dev to be more specific and intentional about overriding children from the PackedScenes themselves. One point that kept coming back in this thread is this PR's relationship to Editable Children, and whether it should replace it. The way I see it is that these two features can live alongside each other, and really, they are two different approaches both with their merits. I do think that the terminology of these two methods could probably be merged, but that can also happen in a future PR.
I found a bug trying the PR – the top-to-bottom order of the nodes in the instance is not the same as in the source PackedScene:
Aside from that, trying the PR with usability in mind, I think the most controversial change about this PR is actually the fact that the exposed children aren't shown in their hierarchy. I think that is somewhat core to this PR, and al though it is 'cleaner', it is still very disorienting. It breaks the agreement that the tree you have in front of you is the source of truth. I wonder if there isn't a way to indicate that there are nodes in between, without adding too much noise to the scene tree. Or come up with a special look for the exposed instance items that makes it clear that these exposed nodes are not in the tree in the same way the other items are in the hierarchy, but more or less 'floating' children of the instance. Like this ugly mockup:
Still, I do wonder whether Godot effectively needs this. I get the added value of making some nodes intentionally exposed and the affordances that come with that. But it is also very much an incremental optimization of a system that's already there (Editable Children). In the two commercial game projects I've touched, the scene trees can get very big and complicated –even when there are no editable children– and I do really wonder whether the addition of this system will really make much of a difference. There are also ways to architect your node tree without using editable children or exposed nodes at all.
I'm happy to be convinced of otherwise, but my current conclusion about this PR is:
- the improved user experience that this PR introduces is limited to a specific part of the workflow, namely in the navigating the tree to finding the editable children you want to adjust. It doesn't add a user flow that isn't already possible with Editable Children – it is simply a different user flow that can be slightly more intentional and cleaner, but with one disorienting caveat:
- in the current iteration of this PR, the 'floating' children misrepresent the actual underlying hierarchy.
|
@AdriaandeJongh commenting on your 1st point: The difference between this and Editable Children is that with Editable Children it's super easy to accidentally change things you are not supposed to change. Even more, this PR also acts as a form of "documentation without written text" for which children nodes are intended to be modified or where to put children nodes into when using a particular scene whereas with Editable Children, I have to provide written documentation for other people (or myself in the future when I forgot how things work exactly) when I design reusable components in Godot. For me, the fact that the tree is not displayed fully here and intermediate nodes are collapsed away is part of the improvement because it declutters the tree view and lets me focus on the important nodes. I can see however that this might be confusing at first but I think that is a solvable problem. I also refuse to use Editable Children at all in projects, mainly because it feels dirty and adds a lot of clutter and traps. But it has been very handy to have a feature to say "this specific node is supposed to be touched/edited/extended in instances of this scene", especially if the scene contains multiple levels of hierarchy and the node you need to put children into (the "slot") is at a deeper level. Edit: I think the main question I have with Editable Children is: "Why would I make something a scene, when I edit the nodes in the containing scene anyways?". I think even a button "Expose all nodes" on a scene would be better than Editable Children, because then the scene clearly signals that it was designed to be edited completely (disregarding how much sense that would make or not). |
|
It's not just about exposing a nested node in an instantiated scene (although that is a big bonus), it's about exposing a node to which additional nodes can be attached at the usage site. The most obvious example of this is UI scenes, where the container node itself is nested within the scene, surrounded by things like titles, the header bars, a close button, other containers... etc. In frontend development this feature is referred to as "slots" or "child props" and is central to creating any kind of re-usable component (or in this case, scene). I really don't see what there is to bike-shed here. This feature is a universal pattern outside of Godot, and would massively clean up and stabilize a lot projects. Editable children, outside of the pure UX pain of cluttering the scene tree also have a few bugs, such as changes pushing themselves "upstream" when being collapsed, and properties randomly flagging themselves as changed, preventing them from receiving changes from their base scene. Almost any game requires that you set some property or expose some node or signal that is internal to the scene. |
|
I'd also like to mention an additional benefit, not only is this feature useful for better organizational clarity for other members and better control over instantiated scenes, but this also greatly improves future-proofing compared to editable children. if a scene only exposes one node, there's no worry about changing the structure of any other part of that scene. once you enable editable children for a scene, changing that scene in any way may have unintended consequences somewhere else in your project from that point on. Exposed Nodes gives better clarity on both sides. I also agree that "Expose all nodes" feels like a superior replacement to editable children, I don't necessarily think that needs to be changed for this PR but that is probably how I would prefer it in a vacuum. |
|
I understand the affordances of this PR. I really do. But do you also see my point of view? Editable Children can do all the things that are mentioned in this PR and more, including “slots” and “child props”. And yes, it is more verbose in the tree, adds clutter, can be unwieldy. And yeah, maybe there are bugs (that should be reported and fixed). But it also works and is conceptually straight forward. You “expand” an instance, and you can make changes. Simple. But above all that: Editable Children are already in Godot! And to avoid bloat, it is the duty of all contributors to carefully consider every new feature added. This PR is currently separate from Editable Children, and provides an alternative method. I hope you can see that if there’s a PR that adds a second way of achieving the same thing, contributor’s bells start ringing. My thinking is currently: this PR would either have to add substantial functionality next to Editable Children (which, as i’ve explained, it doesn’t; it offers a different workflow with different affordances), or replace Editable Children entirely with backward compatibility. So if yall want to avoid Editable Children so desperately, then maybe it is more useful to help figure out how this PR can also gain some of the affordances of Editable Children, as well as address my concern about the misrepresentation of the scene tree? Timo mentioned an option to expose all nodes on an instance. would that only work on that particular instance? that sound precisely like editable children, effectively. are here any ideas on how backwards compatibility could work? are there more ideas to address the misrepresentation of the tree in the scene tree? i’m willing to go down the road with this PR, but if we’re going to call careful consideration ‘bike-shedding’, then we’re just a bunch of dudes on the internet yelling at each other achieving no progress at all. i’m here to help and contribute. that’s why i joined the usability team. this PR has been stuck for years, im trying to get it unstuck, because i recognize there’s something in this PR that is potentially a better workflow / replacement of editable children. but it needs work. |
|
IIRC, the discussion last year was that ideally it would replace Editable Children since it's much more straight-forward and flexible, but we can't even start a discussion about that until 5.0... so unfortunately, that leaves us with three options:
2 was seen as a much more difficult and dangerous route, especially considering it'd require reworking from the ground up if/when we ever go to 5.0, so it was decided it'd be better to keep them separate if it's introduced. I may be misremembering though. |
If it is true that this is the ultimatum for this PR, then my opinion is that exposed nodes should replace Editable Children, with backwards compatibility by exposing all children in previously Editable Children-enabled scenes. But I also personally would prefer there to be multiple solutions to the same problem over not changing anything. |
I wouldn't be against some sort of UI indicator to show the hidden hierarchy above an exposed node, but I'm failing to see why the lack of this is a fundamental problem, especially considering there's already an icon to indicate exposed scenes. To me, this "misrepresentation" is no different from how Godot currently handles instantiated scenes. When you add an instanced node into a tree, it "hides" its internal nodes. If someone is confused about the internal structure or where an exposed node lives, they can simply click on the scene icon of the parent to open it and view the full tree there. I understand the hesitation about the overlap with Editable Children, but Godot is currently lacking a good solution to this workflow. The few minutes I spent testing out this PR just confirmed to me how valuable this feature is to my workflow (especially coming from a web dev background). I agree with the others that a complete replacement or merger with Editable Children is impractical for the scope of this PR, and that's not even considering the implications of such a huge breaking change. Honestly, I don't view them as being as related as they seem to be. Editable Children very much feels like a hack in a "I need to adjust a specific property a few nodes deep" way, while exposed nodes are intentional architecture choices to create flexible and reusable components |
|
@AdriaandeJongh Thanks for taking the time to read the full history, test the PR, and write such a clear summary. That kind of review is genuinely appreciated, especially on a PR as old/long as this. To directly address some of your concerns (besides the known bugs that still need fixing):
|
What I meant with that is that having the toggle for Editable Children on the scene itself for all instances of it rather than on every instance separately would already be a better solution for me. |
|
Interfaces were added to gdscript despite the fact has_method exists and can basically be used to do the same thing, nobody complains about that because it's a really good change ergonomically. I really don't agree that because editable children exists, this PR isn't necessary. It is basically impossible to encapsulate anything or create stable APIs with editable children. To contrast with @export, if you make a breaking change that means you don't need this export any more you can just remove it. Or if you do still need for the scenes that haven't been updated yet it you can keep the export, deprecate it and add a method which gracefully handles converting to your new API. Or, you can make your change and manually go fix all your uses. It's up to you. Whereas with editable children, any minor change to the implementation in the scene immediately breaks all your scenes that use it, and your only option is to go through every use case and fix it. And you won't find out till runtime if you missed one. It's the very definition of a leaky abstraction. To the point it's not usable for complex types or large projects. Not to mention the fact you're exposing a bunch of implementation detail in the scene you've enabled editable children for. This PR would let you create safe, stable APIs in the scene tree like you're supposed to do with code. And misrepresentation of the scene tree? There's still a very simple axiom of "a scene can only expose it's own children or grandchildren", which is not hard to understand. The whole point of abstraction and composition is to hide stuff that doesn't matter. You can already export the property of a child node through top level export using the getter/setter, is this really that different? Regarding proposals for backwards compatibility it really seems you can just make Editable Children use the new interface and expose everything. It's like how GDscript doesn't have private methods and you can call your private methods externally if you really want to, but it's clear it isn't intended. Users can then set up the exposed nodes and disable editable children when they get around to updating the scenes that use it. |
|
Are there any use cases for this feature apart from the following?
For the first one, I much prefer godotengine/godot-proposals#9536 (specifically the "third suggestion", where you right click on a property and expose it which makes it editable on the scene root of instantiated scenes). So that leaves the second use case of parenting nodes to exposed nodes, which is what the examples in the PR show. Also I don't think this is a viable replacement for editable children as a whole. It's not "more flexible", the point of it is that it's a stricter/limited version of it to provide a safeguard from accidentally editing properties and a cleaner UI. To me the main use for editable children has been when I have that one case where I need to make an edit to a scene in a specific situation but it's not worth making an entire system around because it'll only ever get used once in the project. If editable children was completely replaced by this system, if you ran into a situation like this you'd have to expose a node project-wide if you were already using the system for something in that specific scene. |
|
While I personally avoid godot signals, most users seem to use them. Connecting signals in-editor with nodes inside the scene is another thing you can add to that list. |
Yes, doing any of the above then refactoring it later without breaking all the scenes in your project that use it.
Editable children is fine for small hacky stuff like that yeah, but I (and evidently others) have many good use cases for editable children I don't use because I know it will be an enormous challenge to maintain it later. I agree you have to make entire systems to avoid using editable children on a large scale because it's so unwieldy, that's not exactly a point in the current systems favor. I'll echo the point made by others earlier in this thread that having functionality like this is worth having even if it isn't integrated with editable children and is separate with some overlap in functionality, because editable children as it stands now is unusable on a large scale. |
|
My point was just that for me this won't work well as a complete replacement if editable children was removed, not that there isn't a problem to solve because there very much is and I would like it solved, just that I would personally prefer a more "specific" solution. If the way this PR works is preferred and exposing node properties gets implemented (which I've been strongly advocating for), maybe the functionality of this PR could be cut down to:
|
|
I'm seeing mention of implementing this without the ability to modify properties of the exposed node. I don't feel this is the right call. I can think of many examples where I'd want to expose a node and modify it's properties. For an RPG, I might want to expose the Stats node and tweak values during development, or expose the AI controller node, and drag and drop different strategy resource objects. For UI, the same grid container might need different column counts depending on where it's used. This also helps mitigate a common godot antipattern where the root node of a scene ends up being a god-class that needs to constantly traffic the scene's internals to the outside world. This is why I much prefer this PR over something like godotengine/godot-proposals#9536 |
|
@AdriaandeJongh
This PR's issue is directly analogous to the need for encapsulation in code, which has been considered essential to API design for 50~ years. Leaky abstraction, data hiding, principle of least privilege, modularity - these are all represented/addressed here. To add to the other testimonies, I've also wasted time on searching and making mistakes with Editable Children.
I agree! Displaying the exposed nodes alongside children with no visible difference definitely implies that they're children - even though they may not be (as you said). Adding some visual distinction would be great. I think wrapping them in a panel like that or putting some icon on them like 🔌 is enough. Plus a tooltip showing their full path would be great. |
|
Exposed nodes via this PR are to editable children as a public class API is to a class' full internals. A public API is a statement of intention and support: These are the things you are meant to interact with, when you instantiate this thing, and everything is designed to keep functioning as documented and as you would naturally expect. And yet many languages, including C# (where private members are a clear language feature) and Godot (where private members are mainly a naming convention) also allow circumventing this and interacting with an instance's internals regardless, because there are some situations where you want to bypass the intended and well-supported path. It's not good to have both things. It's essential. If having more than one possible way to achieve the same task was a bad thing, we would all still be writing machine instructions directly without a compiler or assembler. This feature is such an obvious win, especially for anyone working on a little bit larger projects, or working in teams as opposed to solo. It's seriously frustrating how long it's been sitting in limbo. |
As for something like the following, im not certain how to approach modifying the TreeItem itself to appear like this at the moment but can dig into it more if a consensus is reached. |
|
I've read the discussion and I'm in favor of the PR. I understand there were concerns about adding code to core, but now that it is a single property in I echo the sentiment that, due to the need to preserve compatibility, it is best implemented as a separate and parallel feature to editable children.
I disagree with this. This feature is essentially encapsulation for instanced scenes, and it carries the benefits of encapsulation that are present in programming. It communicates intent and self-documents, and it further saves time and reduces mental load when working with scenes either forgotten or developed by someone else. "This scene has 100 nodes and is marked editable elsewhere. I can see how it was used in another scene but if I want to be really sure I need to inspect the entire tree and read the script." vs "This scene has 2 nodes exposed, I can confidently modify or add children only to them, and if something breaks it's probably a bug and I would need to inspect the tree and script, much like how I would reason about using a public API." I should also note that there were times I wished it was possible to do something like this, even before coming across the PR. |
passivestar
left a comment
There was a problem hiding this comment.
This is a UX/design review, I skimmed through the discussion, thought about this and did a quick test. Here's why I think this is valuable:
On the overall idea of the PR
Currently the most practical way to do scene encapsulation is not editable children but a script on the root node that exposes an API for dealing with internal nodes. While there are cases where exposing all children can be useful (i.e for imported gltf scenes), for encapsulated repeating entities this is suboptimal because of the tree clutter and the increased risk of overriding things you aren't supposed to touch.
Since editable children can't provide a solid solution to that problem, realistically this PR should be compared in UX not to editable children, but to scripting. Exposing individual nodes is often cleaner because it takes less effort to achieve the same thing due to the lack of redundant boilerplate code, not to mention this is an editor feature and you get full WYSIWYG in editor which is trickier to achieve with tool scripts, such as the ability to preview the final UI with the nodes that you inserted into i.e an instantiated UI scene slots.
On property exposure and architecture
Godot's primary editor-friendly way of doing composition is nodes (i.e to add a collider to a rigid body you add a child node to it), which by extension makes exposing properties of different aspects of an entity via different nodes the idiomatic Godot way. This is different from i.e Unity where composition is a core part of the engine and you can look at different Components of a GameObject in a single inspector. This distinction is why I believe this PR would be the proper way of addressing proposals like godotengine/godot-proposals#7803. While I understand the inclination to aggregate properties in the same inspector, it doesn't currently make much sense in Godot inspector which shows the node class hierarchy.
I also understand the concern about the potential ways to misuse this feature, but on the user app architecture side Godot is mostly an unopinionated engine. In editor everything is just a node and you're mostly free to do whatever. Both inheritance and composition can be useful, and both can be misused. The choice of where to use one or the other falls on the user. Best Godot can do is to provide a good UX for using both of those paradigms.
On duplicated functionality
I'm not against adding a second way of achieving the same thing in a project like Godot. Godot mostly evolves via gradual feature deprecation and replacement, as far as I can tell it's not uncommon for the engine to have double functionality until an obsolete feature is removed. As long as there's a vision for how the two features will be merged into one in the future, and as long as it's communicated properly to the user it should be fine.
If editable children is removed in the future entirely it's important to ensure not only complete feature-parity but also UI/UX-parity (i.e it would be good to find a way to avoid icon spam if every node is exposed), but UI problems are solvable.
I didn't do any bug hunting or code review, but the UX and the overall design of this feature makes logical sense to me.
| @@ -156,6 +157,7 @@ class SceneTreeDock : public EditorDock { | |||
| Label *delete_dialog_label = nullptr; | |||
| CheckBox *delete_tracks_checkbox = nullptr; | |||
| ConfirmationDialog *editable_instance_remove_dialog = nullptr; | |||
| ConfirmationDialog *revoke_node_exposure_dialog = nullptr; | |||
There was a problem hiding this comment.
| ConfirmationDialog *revoke_node_exposure_dialog = nullptr; |
This appears not to be used.
| @@ -89,8 +89,10 @@ class SceneTreeEditor : public Control { | |||
| NodeCache(SceneTreeEditor *p_editor) : | |||
| editor(p_editor) {} | |||
|
|
|||
| HashMap<Node *, CachedNode>::Iterator add(Node *p_node); | |||
There was a problem hiding this comment.
| HashMap<Node *, CachedNode>::Iterator add(Node *p_node); |
Same as above.
| @@ -66,6 +65,7 @@ class SceneTreeEditor : public Control { | |||
| bool dirty = true; | |||
| bool has_moved_children = false; | |||
| bool removed = false; | |||
| bool hidden = false; | |||











Updated 9/8/2025
Description
This pull request implements a feature that significantly enhances Godot's scene editing capabilities. It allows specific nodes within a scene to be exposed, making them visible and allowing their properties to be overridden when the scene is instantiated elsewhere. I believe it is an improved version of editable children.
Note
Important
The use of unique names has been removed from this PR (#84018 (comment)) as there were too many issues with it, once #106837 is merged this PR should function like originally planned
Lets say we have this window scene that we want to re-use everywhere we can exposed the title label and the content nodes:Example 1
With this PR we can modify the properties of the exposed nodes and append child nodes to them, this lets us create super flexible scenes and use them like so:
and this is the same scene with editable children enabled. Far messier and poorer UX
Updated Example 2
[gd_scene load_steps=2 format=3 uid="uid://bg7bu82kitjht"] [ext_resource type="Texture2D" uid="uid://dvj2xcm5vrvy8" path="res://icon.svg" id="1_mdjal"] [node name="scene_0" type="Node2D"] [node name="Parent" type="Node2D" parent="."] [node name="Exposed_0" type="Sprite2D" parent="Parent" index="0"] texture = ExtResource("1_mdjal") + [exposed path="Parent/Exposed_0"][gd_scene load_steps=3 format=3 uid="uid://da3f5wvbqv0bv"] [ext_resource type="PackedScene" uid="uid://bg7bu82kitjht" path="res://scene_0.tscn" id="1_mdjal"] [sub_resource type="CompressedTexture2D" id="CompressedTexture2D_3sd7o"] load_path = "res://.godot/imported/icon.svg-218a8f2b3041327d8a5756f3a245f83b.ctex" [node name="scene_1" type="Node2D"] [node name="scene_0_instance" parent="." instance=ExtResource("1_mdjal")] [node name="Exposed_0" parent="scene_0_instance/Parent" index="0"] self_modulate = Color(1, 0.666667, 0, 1) [node name="Child_Of_Exposed" type="Sprite2D" parent="scene_0_instance/Parent/Exposed_0" index="0"] position = Vector2(100, 30) scale = Vector2(0.5, 0.5) texture = SubResource("CompressedTexture2D_3sd7o") +[exposed path="scene_0_instance/Parent/Exposed_0"] // Re-exposure +[exposed path="scene_0_instance/Parent/Exposed_0/Child_Of_Exposed"] // Initial ExposureSample Project
TODO
I think that this pr addresses the following proposals: