Smarter way to connect the same Signal of multiple buttons?

Godot Version

Godot 4.4

Question

Hello,

i am currently making a main menu and connected the “pressed” signal of all the buttons like this

@onready var start_game_button: EnhancedButton = $StartGameButton
@onready var tutorial_button: EnhancedButton = $TutorialButton
@onready var options_button: EnhancedButton = $OptionsButton
@onready var credits_button: EnhancedButton = $CreditsButton
@onready var quit_game_button: EnhancedButton = $QuitGameButton

func _ready() -> void:
	start_game_button.connect("pressed", _on_start_button_pressed)
	tutorial_button.connect("pressed", _on_tutorial_button_pressed)
	options_button.connect("pressed", _on_options_button_pressed)
	credits_button.connect("pressed", _on_credits_button_pressed)
	quit_game_button.connect("pressed", _on_quit_game_button_pressed)
func _on_start_button_pressed() -> void:
	print("start button pressed")


func _on_tutorial_button_pressed() -> void:
	print("tutorial button pressed")
	
	
func _on_options_button_pressed() -> void:
	print("options button pressed")
	
	
func _on_credits_button_pressed() -> void:
	print("credits button pressed")
	
	
func _on_quit_game_button_pressed() -> void:
	print("quit game button pressed")

I just asked myself if it would be possible to do this in a “smarter” way? What I tried is getting all the children of the container, who holds the buttons but I could not figure out then how to differentiate between the buttons.

func _ready() -> void:
	for button in button_container.get_children():
		button.connect("pressed", _on_button_pressed)
		
		
func _on_button_pressed() -> void:
	print("button xyz pressed")

You can pass arguments to a _on_button_pressed function, and check the argument to determine which button was pressed. This can be done in the editor or via script.

myButton.emit("start")

func _on_button_pressed(button_name):
    if button_name == "start":
        do_stuff()

If you don’t emit the signal yourself or you don’t want to change the signal signature, you can bind extra parameters to the callable: Callable — Godot Engine (stable) documentation in English

E.g.,

func _ready() -> void:
  start_game_button.pressed.connect(_on_button_pressed.bind("start"))
  tutorial_button.pressed.connect(_on_button_pressed.bind("tutorial"))
  # ...

func _on_button_pressed(name: String) -> void:
	print(name)

Edit: Oops, I accidentally deleted my answer :stuck_out_tongue:

4 Likes

1 - if you are only going to use the nodes once, you don’t need a reference, you can just do it on ready.
also, that is not how you are supposed to connect a signal, you are using godot 3x syntax, 4x is cleaner. so instead you take the signal and call connect on it, it’s cleaner and doesn’t use a string.


$StartGameButton.pressed.connect(_on_start_button_pressed)

2 -

yes. but. in your code you have a different function for every button. using a loop you either still need to define a function for each of the nodes, you can put the functions in an array or dictionary as callables. or you only run the same function on all buttons.

3 - as @GameDevGeezer said, you can also pass an argument. here’s an example:

func _ready() -> void:
	var w : int = 0
	for button in button_container.get_children():
		button.pressed.connect(_on_button_pressed.bind(w))
		w += 1

func _on_button_pressed(id : int) -> void:
	match w:
		0:
			#do something for 0 etc
			pass

we are binding a different number to each button, and that will be passed when the specific button is clicked.

there are many ways to tell what a node is.
in godot, I recommend using meta_data. it’s a field at the bottom of the inspector and every single node has it. you can add a variable of any kind and test it from a script.

so for example we set a variable button_id that is an int

then, when obtaining the node, we check for this variable:

for button in button_container.get_children():
	var val : int = button.get_meta("button_id", 0)
	if val != 0:
		button.pressed.connect(_on_button_pressed.bind(val))
3 Likes

Add this to your EnhancedButton script:

func _ready() -> void:
	self.connect("pressed", _onButtonPressed)


func _onButtonPressed() -> void:
	print("%s button pressed" % self.name)

Very cool. I did not know that. Thanks!

that’s very nice, did not know you could do that. thank you. looks way cleaner like that