Sergey Belonozhko
Sergey Belonozhko

Reputation: 549

Display scene with scripted changes in the editor

If I have a sprite or any other predefined node in a scene and change its props in the inspector, I can see the change in the editor immediately. But if I have some programmable changes to the scene (let's say in the _ready() function), they are not shown in the editor and are only visible if I run the project. Is it possible to show such changes in the editor as well?

For instance, I was doing a component which is basically a color palette. It's a horizontal bar of colored squares. It exports an array of Colors, so I can instantiate this component in my scene and set the colors in the inspector. Then in the _ready() method I insert a colored square for each configured color. But it will stay empty unless I run the project.

Is it possible to make my components render some dynamic(programmable) changes in the editor?

This is my palette code:

extends Node2D

@export var colors:Array[Color] = []
signal color_selected(color:Color)
# Called when the node enters the scene tree for the first time.
func _ready():
    var x = 0
    var size = 100
    var white_texture = load("res://white_pixel.png")
    for clr in colors:
        print("Adding color: " + find_color(clr))
        var btn = TextureButton.new()
        btn.size = Vector2(size,size)
        btn.texture_normal = white_texture
        btn.stretch_mode = TextureButton.STRETCH_SCALE
        btn.modulate = clr
        btn.position = Vector2(x, 0)
        btn.connect("button_down", color_clicked.bind(btn))
        add_child(btn)
        x += size

func color_clicked(btn:TextureButton):
    emit_signal("color_selected", btn.modulate)

Upvotes: 1

Views: 690

Answers (1)

Theraot
Theraot

Reputation: 40285

By default scripts do not run in the editor. So if you write code that changes anything, it does not happen until you run the game.

You can change by adding the @tool annotation on the first line of the script. See Running code in the editor.

Be aware that this means that:

  1. Your script will be modifying the working project.
  2. Your script will be executing in the context of the editor, and will have access to the editor itself.

Thus, making @tool script is very powerful, but also a risk. For example, you could make changes that you cannot undo, and you could crash the editor. It is not a preview, and it is not a sandbox.

In your code, you can detect if it is executing on the editor by reading Engine.is_editor_hint(). You might also be intersted in OS.is_debug_build(), and OS.has_feature("editor").


You also want your script to reach to the changes made in the editor... But _ready will not run for every time a change in the inspector is made.

You need to add a setter to @export var colors:Array[Color] = [] which will be called every time it is modified, and handle the change there.

I have explained a pattern I use for this in other questions. See:

Not only that but you are adding nodes on _ready. So you cannot simply run _ready again every time colors because that will result in new nodes being added. So you either remove those nodes, or handle the case where they are already there and modify them.

The next thing you will ask is why you do not see those nodes in the scene dock... The answer is that you need to set their owner for them to show and to be saved in the scene, but for this use case you probably do not want that.

Upvotes: 1

Related Questions