How to make exportable variables apply visually in the editor?

Godot Version

4.7.2 stable for Linux

Question

I’ve had this situation many times with visual elements that I export in my nodes in Godot. I have a scene with a Sprite inside it, and I have to place the instanced scene in the editor inside a level, but I want to use the sprite that will be final in order to get a better feel for how it will look like.

For example, let’s say I have a simple ResourcePile class I’m making for a colony simulation test.

extends Node2D
class_name ResourcePile

enum ResourceType {WOOD, FOOD, MATERIALS, MAGIC_THING}

@export var sprite_tex: Texture = null

func _ready() -> void:
	%Sprite.texture = sprite_tex

I want to place different resource piles over the map with different types of sprites. I have the base class and the scene, and then I instantiate many of these piles in my scene, and distribute them around. But since they are for different resources, I have to drop a different texture for each, however I can’t do that since they are instanced scenes.

This is simply an example with sprites, but sometimes it’s more complex stuff, like having a patrol guard with exportable path points, and I can’t be adding a bunch of marker2Ds in the scene to drop into the `@export var route_points: Array[Marker2D]`.

Are there some editor trick shenanigans I’m not aware of?

Make another scene for your other types. Use the same script or even create the scene as a “New Inherited Scene” which will replicate changes from the base scene. Make use of tool/editor scripts too.

You can edit the children of instanced scenes, right click the node and enable “Editable Children”, probably not the solution for mass-content editing though.

What worries me about using @tool is that I have to guard all the code in the file that I don’t want to run in editor. The documentation says “Pieces of code that do not have either of the 2 conditions above will run both in-editor and in-game.” That means that if I have a long script I have to be adding a check and return to every other function that I make, like

func _process():
  if not Engine.is_editor_hint():
    return
  ...

func calculate_next_task():
  if not Engine.is_editor_hint():
    return
  ...  

and so on. It’s not the end of the world for me but I wanted to check if there’s a cleaner approach to this.

For some (most?) of those functions you can disable on _ready

func _ready() -> void:
    var in_game: bool = not Engine.is_editor_hint()
    set_process(in_game)
    set_process_unhandled_input(in_game)
    set_physics_process(in_game)

    if not in_game:
        return
    # normal ready etc...

I get that though, it certainly adds complexity so tools scripts aught to be worth it.

This plugin “Hoist” might help, I’ve kept my eye on it, but I usually benefit enough from making tool scripts or dive into making plugins instead.

You mean the built-in ones, right? For the others I’d simply be replacing the editor hint check for the variable check :thinking:

I’ll give the @tool approach some weeks, maybe I’ll get used to it. Plus I get to unlock things like setting 3D points from the editor into the node’s variables.

Yes, I should’ve been more clear, I do mean the built in functions. Many of built in overrideable functions will have a set_process_<thing>(bool), chances are those are your major entry points to the script and since signals won’t be emitted by non-tool scripts it may be your only entry points to the script, disabling those functions where applicable may be the only extra lines to add. Usually the case for me any ways.

Setting points did seem like a major benefit, as setting Marker2/3Ds for points is your best stock option (though for a patrol route a Path3D sounds better) and tools allow for much more and better data manipulation in-editor.

Wait, you just unlocked a new worry for me. Can a @tool script emit a signal when running in the editor? I’m guessing only other @tool scripts can actually listen to and respond to said signal, right?

This could be a super advanced edge case, but may be useful for maybe triggering a recalculation of navigatable areas when placing an object that doesn’t have collision but is desirable to avoid maybe.