Godot-proposals: Don't run `tool` scripts until scene is ready and values are populated

Created on 20 Sep 2020  路  10Comments  路  Source: godotengine/godot-proposals

Describe the project you are working on:
A spaceship game

Describe the problem or limitation you are having in your project:
I keep running into annoying issues with godot where the scene is not ready when tool scripts are ran

Describe the feature / enhancement and how it helps to overcome the problem or limitation:
I suggest the following...

  • properties are not serialized unless they are auto properties and then only their backed fields are
  • tool scripts are not ran until the scene is ready and the fields are populated into the script (in that order)
  • when scripts are saved they are paused until populated again

Describe how your proposal will work, with code, pseudocode, mockups, and/or diagrams:
basically this solve issues with exported properties throwing errors when they access nodes in tool scripts

If this enhancement will not be used often, can it be worked around with a few lines of script?:
idk but its not very beginner friendly if there is.

Is there a reason why this should be core and not an add-on in the asset library?:
Its really annoying to have to null check every time you use a node that might not be ready.
I think the would be a good way to keep GDScript easy to use and would make it so much less or a pain to write tool scripts

editor

Most helpful comment

EDIT: Sorry, there are issues with the below approach. It would seem tool scripts will not always send the 'ready' signal, leading to a sorta memory leak.

This looks very similar to https://github.com/godotengine/godot/issues/30460
In that issue, bojidar-bg recommends this style and combining that with DleanJeans style:

export var x = 5  setget set_x
func set_x(new_x):
  x = new_x
  if not is_inside_tree(): yield(self, 'ready')
  # something like:
  $sprite.frame = x

This may also be related to the fact that "ready" stuff is not re-called on tool scripts, so onready variables are not reliable.

I agree a change somewhere around here would be nice. Things as they are are super error prone.

All 10 comments

Does #325 solve the same problem?

It don't see what this solves. Functionality would be lost here as well.

when they access nodes in tool scripts

You need to be aware of init order in the editor _exactly_ the same way you need to be in your games.

325 is an example of a one line fix for the rare instances in which it matters to you to have a saved value specifically. (Which can't be a node, so I don't think this is actually relevant KoBeWi) (For refernce, the setter in a Node in Tool mode will be called twice. Once on Init with the default value set in the script, and once right after enter_tree() with the value from the scene file.)

func somesetter(value):
    if is_inside_tree():
        #do_tree_stuff

my issues is the nodes are not ready when the tool script is reloaded or ran for the first time
also I'm not sure why the editor needs to set the values more then once. it could be just deseralized after the scene is ready.

the idea would to be to do the following with tool scripts when loaded...

  • enters scene tree
  • deserializes fields
  • calls tree_enter
  • does this for the rest of the nodes
  • when tree is ready call ready

That is actually what it is doing.

Because many, many, Nodes and Resources need to run code when those values are set. Most resources for example will generate the "actual" internal data, the export field is just an input for that.

There is no use saving something that can be safely recreated from an easy to serialize and human editable input.

You can't just skip calling the setter when populating the fields.

sorry I deleted that because I realized how dumb it was.
so my big question is how does unity do this without things being null?

sorry misclick

Can you provide a concrete example of what you are trying to do? Perhaps even provide a minimal reproducible project?

@jonbonazza
I am trying to access nodes in properties setters
I don't have a minimal reproducible project but its as simple as trying to get a node in a exported property in a tool script.

the only solution to this problem is to null check every time I accesses a node.
the problem is that properties are being set before the script is ready

TheDuriel's way of doing if is_inside_tree(): only works if the current node is inside the tree and having to do is_inside_tree() for every node your using increases the amount of code we have to write alot.

EDIT: Sorry, there are issues with the below approach. It would seem tool scripts will not always send the 'ready' signal, leading to a sorta memory leak.

This looks very similar to https://github.com/godotengine/godot/issues/30460
In that issue, bojidar-bg recommends this style and combining that with DleanJeans style:

export var x = 5  setget set_x
func set_x(new_x):
  x = new_x
  if not is_inside_tree(): yield(self, 'ready')
  # something like:
  $sprite.frame = x

This may also be related to the fact that "ready" stuff is not re-called on tool scripts, so onready variables are not reliable.

I agree a change somewhere around here would be nice. Things as they are are super error prone.

Was this page helpful?
0 / 5 - 0 ratings