Proper way to build relatively complex tool plugins?

:information_source: Attention Topic was automatically imported from the old Question2Answer platform.
:bust_in_silhouette: Asked By Footurist

After having created some plugins for me for accelerating game development, I just went about making plugin for some kind of shop panel, which is quickly customizable. But I always seem to end up in a ridiculous nested function hell due to having to call some functions in both _enter_tree() and _ready() to see the effects in the editor preview. Anyone aware of a more overlookable way?

:bust_in_silhouette: Reply From: Zylann

Hard to say what to do without seeing an example of your problem.

I know that all built-in nodes made in C++ had to be made as if they were in tool mode (because tool is just a scripting helper thing, in reality nodes all run in editor like in game but their editor-only code is within if blocks), so what happens is, some of the logic may be in the node if it makes sense, but also many other things (such as gizmos or edit menus) can go into the EditorPlugin itself.

When I made a terrain plugin, the terrain node didn’t have much to worry about in the editor, because it works exactly the same in game (so what you see is what you get).
On the other hand, almost all edition logic is outside, within other objects owned by the EditorPlugin.

Could you provide an example of the problem you are having?

I think I am just a bit confused with when things are called when tool mode is on. Often times I got null pointers because of that. I now understand, that you have to make sure for example, that if you do

tool

extends Node2D

export (StreamTexture) var tex setget _set_tex

func _set_tex(value):
    tex = value
    $Sprite.set_texture(tex)

, you will get an error at runtime, because the setter is called multiple times and one time the value of tex is null, because _ready() wasn’t called yet. So you have to do this

tool

extends Node2D

export (StreamTexture) var tex setget _set_tex

func _enter_tree():
	var sprite = Sprite.new()
	add_child(sprite)
	sprite.name = "Sprite"
	
func _ready():
    ready_called = true
    _set_tex(tex)

func _set_tex(value):
    tex = value
    if ready_called:
        $Sprite.set_texture(tex)

So, I am wondering why not do this

tool

extends Node2D

export (StreamTexture) var tex

func _enter_tree():
	var sprite = Sprite.new()
	add_child(sprite)
	sprite.name = "Sprite"
	
func _process(delta):
	if $Sprite.get_texture() != tex:
		$Sprite.set_texture(tex)

, which proves much simpler, when the complexity grows and it works. So why the complication with setget??

Footurist | 2018-02-26 15:22

setget is still useful if you want direct feedback in editor with zero overhead each frame. _process is overrated for this, because it feels easier to write, but it has drawbacks too such as every frame overhead and it never happen immediately when you set the texture. But heh, sometimes it’s enough so feel free to choose your solution^^

You could also do this:

tool

extends Node2D

export (StreamTexture) var tex setget _set_tex

func _enter_tree():
	var sprite = Sprite.new()
	add_child(sprite)
	sprite.name = "Sprite"
	sprite.texture = tex

func _set_tex(value):
	tex = value
	if is_inside_tree():
		$Sprite.set_texture(tex)

But I agree setget and in-editor logic can be a bit tricky to get right, especially when child nodes are involved. It demands to be more thorough when implementing things, thinking about more corner cases than ingame (who knows what designers will do with those nodes).

Zylann | 2018-02-26 19:07

Thanks. Your comments were very helpful!
One last question : How do you decide if the overhead created by _process(delta) is too much? Do you just try and fail (maybe) or is there a way to know before?

Footurist | 2018-02-26 23:24