Is this signal connection sloppy code?

Godot Version

4.3

Question

Is this code connecting this custom signal sloppy and hard to maintain?

I have to retrieve a node from the scene tree called “MovingDown.” It is a couple children down within the “Player” node/scene.

I’m very new to the engine, so I did it like this from my Label that keeps track of score.

extends Label
@onready var player = $"../Player"

func _ready() -> void:

# check if player exists
	if player:
		#get the MovingDown node
		var moving_down_node = player.get_node("../Player/StateMachine/MovingDown")
		#connect the custom signal from MovingDown node
		moving_down_node.score_signal.connect(_on_score_signal)

I’ve handled if the player doesn’t exist for some reason, but is there any easier or more maintainable way to retrieve the MovingDown node rather than using the node path (“…/Player/StateMachine/MovingDown”) in case something changes later?

I know it’s a very basic question, but I didn’t know if there were more extensible and modular ways of doing this.

Scene Trees:
image

image

Oh, yes, you are right, that is awful. And if you keep doing it you will produce such a mess of inflexible and unmaintainable code it will become a nightmare very quickly.

If I were doing this the first thing I would do is to create an autoloaded script called SignalManager. (Some people call it a SignalBus). (If you do not know about autoloads they are very useful!)

Here is a sample of SignalManager content.

extends Node

signal level_completed
signal wormhole_opened
signal wormhole_closed
signal player_died

Now, an actor, say the main player, can connect to any signal in the SignalManager at any time. So you remove the dependency on having parent nodes etc. I can run the player scene all by itself for dev and testing. It no longer needs to embedded in, say, the main_game scene to work.

So your connection would look like this:

func _ready():
    SignalManager.score_signal.connect(_on_score_signal)

And to emit signals

SignalManager.score_signal.emit()

And this is how you can send information in the signals too:

# SignalManager
signal score_changed(score_change: int)

# Emitting it
SignalManager.score_changed.emit(7)
SignalManager.score_changed.emit(-3)
etc

Hope that helps,

Paul

1 Like

Thank you @pauldrewett . I will have a look at all of this and see if I can implement it.

I also noticed on re-reading your question that you are talking about referencing nodes in the tree.

The rules are fairly simple, use signals if you are going up a tree, use references if going down the tree (in the same scene)

The only exception I have to this is that I often use the:

@onready var Host = get_parent()

So I break the rule about going up the tree if it is in the same scene and if it is only to talk to the parent. I find this helps me to keep signals to a reasonable minimum and maintain modularity.

1 Like

The singleton pattern + autoload in Godot worked great! Thanks again.

1 Like