tooling shenanigans

A screenshot of the map of several discrete 2D rooms, which has a line colored with a gradient from red to blue indicating the path a single character will take during the game.

null has a day/night cycle, and during this cycle I wanted the non-playable characters to roam around the world. The way I implemented this was by adding nodes throughout the world indicating the time that a character should start walking to that position.

Soon, it became difficult to remember where all of the nodes are for each character were. I mean, I had to open a hierarchy view:

The hierarchy view, which has an open node at world > roomBedroom1 that contains several nodes Lemon Idle, Warden 3 Watch, Inspect Clock, Lemon, and Warden 3

And not only did I have to rifle through all the nodes to find what I wanted to edit, each node had its own time and character (which might accidentally not actually reference the character the name of the node suggests it is for). Plus, it was hard to visualize what the path would be. This was not ideal, so I tried to write an editor helper.


At first, the editor helper was easy to write. All I needed to do was group the nodes by character, sort them by time, then draw a line between the points in each group:

A screenshot of the map of several discrete 2D rooms, which has several overlapping lines colored with a gradient from red to blue indicating the path every character will take during the game.

…but, as you can see, this resulted in a really noisy map that was still hard to parse. Fair enough, I thought, I just need to draw the path that is currently selected. However, finding the currently selected node turned out to be Unity-levels of silly.

As it turns out, you need access to the EditorInterface in order to find out what node is currently selected. However, you can’t actually get a reference to one unless you make a new editor plugin (it will be null if you try to access it outside of a plugin). So, I made an editor plugin whose sole purpose is to exist so I can access the EditorInterface:

@tool
class_name EditorHelper
extends EditorPlugin
 
static var instance: EditorHelper
 
func _enter_tree():
    instance = self
    pass
 
func _exit_tree():
    pass

Then, in the tool script, I can access the currently selected Node:

func _exit_tree():
    if EditorHelper.instance != null:
        var s = EditorHelper.instance.get_editor_interface().get_selection().selection_changed
        if s.is_connected(queue_redraw):
            s.disconnect(queue_redraw)
 
func _draw():
    # We can't simply use `get_selected_nodes` because `_draw` will
    # not get called again when the selection is changed.
    if Engine.is_editor_hint():
        var s = EditorHelper.instance.get_editor_interface().get_selection().selection_changed
        if not s.is_connected(queue_redraw):
            s.connect(queue_redraw)
        internal_draw()
 
func internal_draw():
    var edi = EditorHelper.instance.get_editor_interface()
    for selection in edi.get_selection().get_selected_nodes():
        if selection == self:
            # elided: Draw the path...

And it works!

A screenshot of the map of several discrete 2D rooms, which has a line colored with a gradient from red to blue indicating the path a single character will take during the game.

That’s quite a lot of work for what I wish was just:

func _draw():
    if Engine.is_editor_hint() \
        && self.find(EditorHelper.selected_nodes) > -1:
        # elided: Draw the path...

But at least it works now, I suppose!