Logo Questions Linux Laravel Mysql Ubuntu Git Menu
 

Godot: Set editable children on certain nodes as default

Tags:

godot

My problem is that Editable Children is always disabled by default. However, certain scenes like a hitbox.tscn would really benefit from being able to set the default to enabled.

My question is: Is there a way to do this? Or at least is there a way to make it less painful to always enable Editable Children?

I am fairly sure that there is a way to at least make it less painful, but I can't remember the specifics.

Also, judging by these issues (1, 2) there doesn't seem to be a way to make it the default. Though I am fairly certain that there is a way to do it (at least to a degree).

like image 476
Racid Avatar asked Oct 21 '25 07:10

Racid


1 Answers

There seems to be a way to do it for Godot 4: set_editable_instance.


For Godot 3 I have found a work-around.

I see that PackedScene has a _bundled property of type Dictionary with a editable_instances key. But before we can access that we need to get the path of the scene file of the currently edited scene.

We can get the currently edited scene from EditorInterface.get_edited_scene_root and then read the filename property. Since we are already talking about using EditorInterface we are going to make an EditorPlugin.

Once we have the file, we could load it, modify it, and save it.


We need a tool script, because we need our code to run on the editor.

And instead of making an EditorPlugin addon, we will just make a throw-away instance to get what we want:

tool
extends Node # Or whatever

func _ready() -> void:
    var editor_plugin := EditorPlugin.new()
    var editor_interface := editor_plugin.get_editor_interface()
    var current_edited_scene := editor_interface.get_edited_scene_root()
    print(current_edited_scene)

And, of course we can get the filename:

tool
extends Node # Or whatever

func _ready() -> void:
    var editor_plugin := EditorPlugin.new()
    var editor_interface := editor_plugin.get_editor_interface()
    var current_edited_scene := editor_interface.get_edited_scene_root()
    print(current_edited_scene.filename)

And with that a PackedScene:

tool
extends Node # Or whatever

func _ready() -> void:
    var editor_plugin := EditorPlugin.new()
    var editor_interface := editor_plugin.get_editor_interface()
    var current_edited_scene := editor_interface.get_edited_scene_root()
    var packed_scene := load(current_edited_scene.filename) as PackedScene
    print(packed_scene)

Then access the _bundled property:

tool
extends Node # Or whatever

func _ready() -> void:
    var editor_plugin := EditorPlugin.new()
    var editor_interface := editor_plugin.get_editor_interface()
    var current_edited_scene := editor_interface.get_edited_scene_root()
    var packed_scene := load(current_edited_scene.filename) as PackedScene
    print(packed_scene._bundled)

The editable_instances key:

tool
extends Node # Or whatever

func _ready() -> void:
    var editor_plugin := EditorPlugin.new()
    var editor_interface := editor_plugin.get_editor_interface()
    var current_edited_scene := editor_interface.get_edited_scene_root()
    var packed_scene := load(current_edited_scene.filename) as PackedScene
    print(packed_scene._bundled["editable_instances"])

And write to it… Wait what do I need to write? Well, since I can now see the values, I can experiment. And after some testing, I can tell that what I need is the NodePath (not String). So let us do that:

tool
extends Node # Or whatever

func _ready() -> void:
    var editor_plugin := EditorPlugin.new()
    var editor_interface := editor_plugin.get_editor_interface()
    var current_edited_scene := editor_interface.get_edited_scene_root()
    var file_path := current_edited_scene.filename
    var packed_scene := load(file_path) as PackedScene
    var node_path := current_edited_scene.get_path_to(self)
    print(node_path)
    var editable_instances:Array = packed_scene._bundled["editable_instances"]
    print(editable_instances)
    editable_instances.append(node_path)
    packed_scene._bundled["editable_instances"] = editable_instances
    print(packed_scene._bundled["editable_instances"])
    var err := ResourceSaver.save(file_path, packed_scene)
    print(err)

This code executes correctly, but we don't get the result we want. We will solve that with this code:

    var editor_filesystem := editor_interface.get_resource_filesystem()
    editor_filesystem.scan()
    yield(editor_filesystem, "sources_changed")
    editor_interface.reload_scene_from_path(file_path)
    editor_interface.save_scene()

Here we ask Godot to look for changes in the files, we wait it to finish, then we ask Godot to reload the scene. Finally we save it so it does not retain a modified mark.

This is my final code:

tool
extends Node # Or whatever

func _ready() -> void:
    var editor_plugin := EditorPlugin.new()
    var editor_interface := editor_plugin.get_editor_interface()
    var current_edited_scene := editor_interface.get_edited_scene_root()
    if current_edited_scene == self:
        # modify scenes that instantiate this one, but not this one
        return

    var file_path := current_edited_scene.filename
    var packed_scene := load(file_path) as PackedScene
    var node_path := current_edited_scene.get_path_to(self)
    var editable_instances:Array = packed_scene._bundled["editable_instances"]
    if editable_instances.has(node_path):
        # the instance of this scene already has editable children
        return

    editable_instances.append(node_path)
    packed_scene._bundled["editable_instances"] = editable_instances
    var err := ResourceSaver.save(file_path, packed_scene)
    if err != OK:
        # something went wrong while saving
        prints("Error modifying scene to add editable children:", err)
        return

    var editor_filesystem := editor_interface.get_resource_filesystem()
    editor_filesystem.scan()
    yield(editor_filesystem, "sources_changed")
    editor_interface.reload_scene_from_path(file_path)
    # warning-ignore:return_value_discarded
    editor_interface.save_scene()
like image 159
Theraot Avatar answered Oct 24 '25 03:10

Theraot



Donate For Us

If you love us? You can donate to us via Paypal or buy me a coffee so we can maintain and grow! Thank you!