Custom Signals on Instanced Objects Mini Tutorial

working on Godot 4.2

I ran into an issue and wanted to share a solution that worked for me in case anyone else ran into this problem. This felt very specific and most tutorials I’ve watched don’t really talk about custom signaled instanced objects or the code snippets didn’t work for me.

Tutorial:

In this example there are two scenes
The Listener and the Signaler.

An example of an instanced Signaler used in practice would be something like a bullet or projectile hitting an enemy. If the bullet hits the enemy then the bullet(Signaler) will emit a signal that will tell the Listener to do something. For example a function that calculates damage. The triggers here are only emulating that function as the example.

The objective is to instance the Signaler into the Listener scene. The only thing not shown here is a 2D rectangle that shows up as a proof that it was successfully instanced. That rectangle is a child/ subordinate of the Signaler.

The rectangle is not really important here, I just wanted something to confirm that the instance worked.

The Listener launches the functions while the Signaler waits for something to happen. In this case waiting for Trigger 1 or Trigger 2 which are initiated by pressing the “spacebar” or “S” key respectively. When that happens it will emit a signal to proceed with a function on the Listener’s side.

I created two custom signals inside the Signaler scene:
signal hi_square_signal
signal square_info_pass_signal
The first one will be attached to a function that just says “I got the signal” if triggered.
The second one takes the value (in this case an integer) then does something with that passed value. In this case printing it to the console.
I like to make custom signals to keep things organized for readability.

I then place the .emit() function where I want the signals it to trigger. Which is after either button is pressed for the desired signal to be launched.

This would be if the bulled collided with something or an enemy

Back on the Listener’s side I created a function to instance the Signaler.
When the “Initializer” (A button) is pressed then it creates an instance of the Signaler. First by attaching it to a new variable. The referenced scene is at the top to be referenced
const SIGNALER = preload(“res:/Signaler.tscn”)

This would be like referencing the bullet scene and getting it ready to be fired

Then I attach the ability for that object/ node’s Signals to trigger a function on the Listener’s side.
signaler.hi_square_signal. connect(hi_square.bind())
signaler.square_info_pass_signal. connect(square_info_pass.bind())

This tells the bullet what to do when it hits

Here the signals are attached to those specific functions. Interestingly I’ve seen a lot of ways to do this, but the .bind() worked well for me. The graphic attached to this thread hopefully helps illustrate the overall construction of this line of code.

After attaching the signals to the function it will be added to the scene when the Initializer is pressed.

This would be when the bullet is fired and now exists within the scene. This can still be launched multiple times. But I only need one here

The final outcome is that it prints out “I got the signal” and “6”. This doesn’t happen if the Signaler is not instanced first. With the 6 being the passed value into the square_info_pass function.

This would be an example of using the function to pass a damage value to be calculated or to launch an effect of some kind after the bullet hit.

There are likely other ways to do this so if there is anyone else with more information feel free to share. I just wanted to show this test I did since I had a hard time looking up this information and getting it to work.

I hope that it helps someone out there o7

1 Like

You usually don’t need to call .bind() with no arguments, you should be able to just .connect(hi_square).

Good tutorial :+1:

1 Like

Thanks!
That’s good to know, I just tried it out and that works.

Out of curiosity do you have an example of .bind() used in context? Since it’s not doing anything here

Yeah, .bind() works great for when you need to bind a signal which doesn’t emit a value to a function which expects one.

For example, if your square_info_pass_signal didn’t have any arguments in its .emit(), you could still hook it up to square_info_pass(number) using .bind().

# function expects an argument
func square_info_pass(number):
	print(str(number))

# signal explicitly has no arguments
signal square_info_pass_signal()

# connect signal with bound argument
signaler.square_info_pass_signal.connect(square_info_pass.bind(42))

# emit signal as usual
square_info_pass_signal.emit()

I’ve found this is particularly useful when working on UI code, e.g. where you might have an array of Button nodes that you need to connect to some on_button_clicked(button_index) method, and need to use .bind(index) because the button’s pressed() signal has no arguments.

1 Like

Ah I see, thank you very much for the example!

That does sound very useful, just as you said in regards to something that may not have an index handy. I’ll keep that in mind going forward, it lends some good options on how to handle variable UI

I really appreciate it!