Creating Signals the Basics

im new to Godot and to my understanding this is how you Create Signals
Without Singleton.

This is how you create a Signal Inside of Godot
Note: Both the signal and emit_signal must have Same Name.

extends Node3D

signal My_Signal

func Emit_Signal():
	My_Signal.emit()


Calling Signal Via Code: (You need a Reference to the Node)

extends Node3D

@export var Receiver:Node3D

func _ready() -> void:
	Receiver.My_Signal.connect(Send_Message)

func Send_Message() -> void:
	print("Hello_world")

With - Singleton (Autoload)

You have to Create a Script that is Base Class: Node
Call the Script “SignalBus” and make it Global
(Project > Project Settings > Global > Select Script > SignalBus.gd)

Create Signal Name in SignalBus
(This is the area where you want to Create Your Signal Name)

extends Node

#Put all your signal names here
signal Hello_World

to Emit Signal Name

extends Node3D

func Emit_Signal():
	SignalBus.Hello_World.emit()

to Receive Signal

extends Node3D

func _ready() -> void:
	SignalBus.Hello_World.connect(Send_Message)

func Send_Message() -> void:
	print("Hello_world")

Issue with the Current Code is one Thing.
There is NO Safety Nets for any error types.

this is also for my self so i don’t forget how to create Signals.

*Edited: My_Signal.emit() Instead of Using STR to get it



a Simple Method to Create a .bind with passing variable

extends Node3D

signal CustomSignalName

func _ready() -> void:
	CustomSignalName.connect.bind(CustomFunction(5)) 
	
func CustomFunction(Variable: int):
	print(Variable)
	return Variable

You can also Stack Multiple .binds and let them Intermingle without Worrying about Errors.
Issue about this method right now: Signals Connections do not get Free when not in use.

Consider using my_signal.emit(...) instead of emit_signal("my_signal", ...) as it is way more robust than using a StringName.

You did great by using my_signal.connect(f) instead of connect("my_signal", f), which is exactly the same situation !


You can also declare parameters to signals !

signal my_signal(health: int)

...
 my_signal.emit(10)
...
... 
  obj.my_signal.connect(_on_emission)
...

func _on_emissions(health: int):
  print("Health %d" % health)
2 Likes

ok thank you for Clarifying hehe…

For people Wondering:
in the Singleton This is how you where write it

extends Node3D

func Emit_Signal():
	SignalBus.Hello_World.emit()

which is “Bus”.“Name_of_Signal”.emit()

Without Singleton:

extends Node3D

signal My_Signal

func Emit_Signal():
	My_Signal.emit()

“Name_of_Signal”.emit()
not sure to edit it… to add it or leave it as is…

and its true you can pass Variables inside of the Signal.
Decide to Edit it to make it easier to Follow

Need to do more Research on Passing Numbers.

I would also suggest you to see Callable.bind ! The example is fairly simple. You want to connect to a signal, but the parameters passed to the signal aren’t sufficient in your context. In that case, binding is useful.

Basics of binding

You have this function (which is considered a callable)

func multiply(a: int, b: int) -> int:
  return a * b

This func takes two integers and returns the multiplication of those. It’s not useful per se but it’s a simple example.
Let’s say you want to create a callable whose entire goal is to multiply a number by specifically 2. Normally, you’d do

multiply(2, my_num)

# or, since multiplication is commutative
multiply(my_num, 2)

You could also do

var by_two: Callable = multiply.bind(2) # It will append the 2 at the end of each call, as if the parameter was passed

From there, you have one callable with one bound param. That means you can do

by_two.call(4) # is equivalent to multiply(4, 2)

Basically, a will become 4 in this call. Since the function has bound arguments, they are passed in order after the last argument written in call, meaning b is equal to 2 when the function is called.

With signals

This is useful with signals because one signal may not hint enough elements for you. Let’s say you want to subscribe to the pressed event of a button and run some code when that button is pressed.

For whatever reason, imagine you have a series of buttons and a script attached on a node containing all of them does this

for button: Button in get_buttons():
  button.pressed.connect(_on_pressed)

Your goal is to randomly change the color of the button whenever it’s pressed. However, you realize that unfortunately, your callback doesn’t have the ref to the button. How would you write it?

func _on_pressed() -> void:
  # How can I access the button that was clicked? 

The key is binding! You can introduce additional parameters in the callback, and bind them when connecting to the event:

for button: Button in get_buttons():
  button.pressed.connect(_on_pressed.bind(button)) # Binding : I add the additional "button" object as received parameter

... 
func _on_pressed(button: Button) -> void:
  # The button param refers to the button I just pressed thanks to binding
  # You can alter its color here, or do whatever you want

A thing to keep in mind : always manage your memory with signals, subscriptions etc. You need to think when you should disconnect from a signal if you’re in a situation where the engine won’t do it for you and you don’t need that connection to be persistent during runtime.

1 Like

(was sick a couple of days).

Trying to understand it. lets see…

Callable Example: (Most Basic one).
(Using your Multiply as Example)

extends Node3D

func _ready() -> void:
	var Call_Function: Callable = test
	print(Call_Function.call(4, 2))
	
func test(a: int, b: int) -> int:
	return a*b

This is the Example that the User Show me
Lets see… Teq is var by_two = test.bind(2) but close enough

extends Node3D

func _ready() -> void:
	var by_two: Callable = test.bind(2)
	print(by_two.call(6)) 

func test(a: int, b: int) -> int:
	return a*b

such a pain to think in lambda.



Missing Last Example but ill be back once i learn more

after spending some time i can’t Figure it out.
This is the only example i got… (im trying to avoid Build in functions for now and looks like this need a Build in Function to create a Sudo Infinite Loop or i like to call it a Permission Slip hehe).


func _ready() -> void:
	SignalBus.ABC.connect(InvokeFunction.bind("A")) #Goes PremadeFunction

func Check_Connection():
	var a: String
	SignalBus.ABC.emit(a) #EmitsTheSignal

func InvokeFunction(a: int) -> void:
	return print(a) #Area Where you put Code.

You can use the if Function to Stop it from Re-Connecting to itself.

func _ready() -> void:
	if SignalBus.ABC.is_connected(InvokeFunction):
		SignalBus.ABC.connect(InvokeFunction.bind("A"))
	else:
		print("IM CONNECTED")
		
func Check_Connection():
	var a: String
	SignalBus.ABC.emit(a)

func InvokeFunction(a: int) -> void:
	return print(a)

but the stuff i can’t figure out is… Why use .bind? you can’t call it in other places.
if you need a Reference of the node it makes more sense to just use the Function directly…
great to have Unique ID for each node. but isn’t Signals already doing that… guess i have to experiment a bit more before using this method.

Since i like Learning i Decided to do a Quick Test try on a quick Health Script

extends Node3D

@export var Number:New_Resource
@export var RefNode: Node3D

func _input(event: InputEvent) -> void:
	if Input.is_action_just_pressed("ui_accept"):
		SignalBus.Hello_World.connect.bind(HitPoints(10))
		SignalBus.Hello_World.connect.bind(HitPoints(10))
		
func HitPoints(value: int):
		Number.Health -= value
		if Number.Health <= 0:
			Number.Health = 0
		SignalBus.Hello_World.emit(Number.Health)

in this example it makes a bit more sense on why to use .bind



This is a method that you can use to set it up.

extends Node3D

signal CustomSignalName

func _ready() -> void:
	CustomSignalName.connect.bind(CustomFunction(5)) 
	
func CustomFunction(Variable: int):
	print(Variable) 
	return Variable 
	

you can also Stack Signals to Unlimited Potential with it instead of waiting for them to despawn.

func _ready() -> void:
	CustomSignalName.connect.bind(CustomFunction(5)) #prints 5
	CustomSignalName.connect.bind(CustomFunction(4)) #prints 4
	CustomSignalName.connect.bind(CustomFunction(3)) #prints 3
	CustomSignalName.connect.bind(CustomFunction(2)) #prints 2
	CustomSignalName.connect.bind(CustomFunction(1)) #prints 1
	
func CustomFunction(Variable: int):
	print(Variable)
	return Variable

This is the most Basic Example i can give… for now… now time to check on emit and see if i can do something similar like this.

bind is most useful with run-time known values. Your example here (is wrong and) can be simplified using a for loop

func _ready() -> void:
    for i in 5:
        CustomSignal.connect(CustomFunction.bind(i + 1))

Another very close example would be a level select screen. Imagine a row of buttons each numbered for a level to load. You can use bind instead of connecting a unique function to every button

func load_level(level_number: int) -> void:
    get_tree().change_scene_to_file("res://Levels/level_%d.tscn" % level_number)

func _ready() -> void:
    for button in $LevelSelectContainer.get_children():
        var level_num: int = button.name.to_int()
        button.pressed.connect(load_level.bind(level_num))
2 Likes

Good Example:

Using the level_number as both the % level_number and var level_num (Which gets the Value from the Button) is quite creative. i haven’t reach that far in my study yet…

But in this Example whould that mean that the button name is what dictates the “var”
unless… if by using .bind set its automagicly… not sure hmm research time haha thank you so much!

You are correct, using button.name means the node name dictates which level they change to. Here’s what that sample’s scene tree might look like

1 Like

Binding isn’t the solution of the year ! It’s mostly useful whenever you manipulate signals that don’t give enough context for your code to run. It can happen and I had to once do that back when i was in Roblox.

It’s not necessarily that your conception is bad in essence, it’s just that you lack a parameter that is useful during runtime in a more specific context. Don’t get me wrong, when making the param list for a signal, you tend to see what would be necessary in most of the cases so it’s not something you’d use frequently.

You mentioned instance IDs, that’s right you can use IDs instead, but how would you transfer the ID of your instance to your callable?

I know it’s not the question you asked but I realized that you are mixing naming conventions on functions and variable names. Godot has suggested naming convention for everything so I really advise you to follow that..

For example

#wrong
func CustomFunction(Variable: int):
	print(Variable)
	return Variable

#correct - snake_case function and variable name

func custom_function(variable: int):
	print(variable)
	return variable

Here is more info on that:

https://docs.godotengine.org/en/stable/tutorials/scripting/gdscript/gdscript_styleguide.html#naming-conventions

4 Likes