Custom Godot signals with variables

Disclaimer: This is intended for very new users to Godot.  This may not be the best way to do this, but it works for me.  Any feedback is appreciated to improve the code below for a beginner.

I’ve long struggled with sending custom signals that also have variable.  The only reason for the struggle was not finding a learning resource.

I’ve finally figured it out so to help anyone’s pain in the future here’s a quick lesson  to send a signal to another node and send some kind of information with it (a variable).

A Signal Manager

I had many issues with signals in the past where I couldn’t figure out the node that was emitting the signal as it may be instanced somewhere in the tree that I might not have been expecting.  Due to this I now use a global script that I call signalManager.gd  This script is setup as a singleton and will always be where I expect it.  That way I can use the signalManager to send the signals and when I’m looking for a signal, I can always call the signalManager, knowing that it will be there.

To setup the signalManager:

In you Godot interface, in the scripting area.  Create a new script.

I put my scripts in a folder called scripts as you can see here.  Simply place it wherever you want it then add the script.

Next we want to have the signalManager always available, so I’m creating an autoload/global/singleton for this.  To do so:

  • Head to the Scene MenuProject Settings
  • Select the Autoload tab
  • press the … button in the path input area and select the signalManager.gd script you just created
  • Press the Add button in the Node Name input area on the right.
  • Close the Project Settings and you now have an autoload script that can be accessed from anywhere

Here is an example of my signalManager.gd showing various signals I’m sending:

extends Node
#This node exists solely to collect and send signals to othr parts of the application
#So any signal needing to be retrieved can simply come here to get the data.
#My first ever manager.  The "Signal Manager"
#Note this IS a singleton.  Not sure if I should have my hand slapped or not?

signal scoreAdjusted(adjustedScore)			#score/money in top right corner
signal menuSlide							#toggle sliding of main menu
signal pauseGameToggle						#pause and unpause game
signal modalVisibilityToggle(whichDialog)	#can triger a modal to fade out with its animplayer "fadeOut" and "fadeIn" animations using this

func _ready():
	pass

Note: See how two of the signals, scoreAdjusted and modalVisibilityToggle both have (brackets) after them with a variable name.  This is a key point.  Without that variable name in a bracket the signal will not carry the variable to where it needs to go.

How to use it:

OK so let’s use my example of switching visibility of a dialog.

I have a scene and let’s say it’s called “newScreen.tscn”.  I have a script called “newScreen.gd” that’s attached to its root node.

When this scene is instanced/created I want it to hide a scene called “oldScene.tscn”.  oldScreen.tscn has a script called “oldScreen.gd” attached to its root node.

Here are the 2 gd scripts and their code.  Note that I’m still using the sceneManager.gd code from above.

newScreen.tscn
func _ready():
	#hide the oldScreen to avoid confusion when this one loads
	signalManager.emit_signal("modalVisibilityToggle","oldScene")

Note 1: that we’re sending the signal from signalManager by using signalManager.emit_signal.  This is a key point.  The signalManager is the one that’s sending it, even though we’re in newScreen.

Note 2: See I have sent a variable called “oldScene” in the emit_signal line.  This variable will be sent along with the signal.

oldScreen.gd
func _ready():
	
	signalManager.connect("modalVisibilityToggle", self, "showHide")
	
func showHide(whichDialog):
	#only hide this modal if we are mentioned in "whichDialog"
	if whichDialog == "oldScene":
		if fadeStatus:
			get_node("AnimationPlayer").play("fadeIn")
			fadeStatus = false
		else:
			get_node("AnimationPlayer").play("fadeOut")
			fadeStatus = true

Note 1: See how the connect line in the _ready function is connecting via the signalManager.  It’s this way that allows us to always know where the signals are coming from.  So no matter where we are in the scene tree, the signals always come from the same place.

Note 2:  See how the connect line in the _ready function doesn’t show the variable that’s being sent.  It’s not required to show it in this line even though there is a variable.

Note 3: Notice the func showHide has the variable as (whichDialog) and this matches the signalManager.  This shows that it’s expecting a variable to be passed.  Without this your variable will not appear.

Conclusion

So above you’ve seen some key points.

  • A signal manager to allow signals to always be called from the same place
  • The signal must have () after its name when defined in the signalManager (if it has variables)
  • The signal doesn’t need the variable name when connecting to the signal later from another node
  • The function that is triggered from the signal connect line does need the variable declared to be used.

I trust the above is helpful and I hope it makes sense.  All the best of luck with improving your skills and working towards releasing your game.