How do I connect a signal in code?
Connect a built-in signal
Section titled “Connect a built-in signal”The script sits on Main, and the button is its child:
MainNode2D
the script goes here
ButtonButton
In _ready(), take the signal, call .connect() on it, and hand it the function that should run. You pass the function itself, not a call to it, so there are no parentheses after its name:
extends Node2D
func _ready() -> void: $Button.pressed.connect(_on_button_pressed)
func _on_button_pressed() -> void: print("Button pressed")using Godot;
public partial class Main : Node2D{ public override void _Ready() { GetNode<Button>("Button").Pressed += OnButtonPressed; }
private void OnButtonPressed() { GD.Print("Button pressed"); }}Run the scene and click the button. Every click prints a line, with nothing set up in the editor.
Signals that pass arguments
Section titled “Signals that pass arguments”Some signals send data along. Area2D.body_entered tells you which body came in, so the handler takes that body as a parameter:
func _ready() -> void: $Area2D.body_entered.connect(_on_body_entered)
func _on_body_entered(body: Node2D) -> void: print(body.name, " entered")The handler must take the same parameters the signal sends, in the same order. To see what a signal sends, select the node, open the Node → Signals dock and read the signature next to its name.
Pass extra values with bind()
Section titled “Pass extra values with bind()”Sometimes the handler needs something the signal does not send, like which button was pressed when several share one function. Call .bind() on the function and pass the extra values. They arrive after the signal’s own arguments:
MainNode2D
HBoxContainerHBoxContainer
RedButtonButton
GreenButtonButton
extends Node2D
func _ready() -> void: $HBoxContainer/RedButton.pressed.connect(_on_color_pressed.bind(Color.RED)) $HBoxContainer/GreenButton.pressed.connect(_on_color_pressed.bind(Color.GREEN))
func _on_color_pressed(color: Color) -> void: print("Picked ", color)using Godot;
public partial class Main : Node2D{ public override void _Ready() { GetNode<Button>("HBoxContainer/RedButton").Pressed += () => OnColorPressed(Colors.Red); GetNode<Button>("HBoxContainer/GreenButton").Pressed += () => OnColorPressed(Colors.Green); }
private void OnColorPressed(Color color) { GD.Print($"Picked {color}"); }}pressed sends nothing, so color is the only parameter. With a signal that does send data, the signal’s arguments come first and the bound ones after. Binding to body_entered gives a handler of (body: Node2D, side: String):
func _ready() -> void: $LeftZone.body_entered.connect(_on_zone_entered.bind("left")) $RightZone.body_entered.connect(_on_zone_entered.bind("right"))
func _on_zone_entered(body: Node2D, side: String) -> void: print(body.name, " entered the ", side, " zone")In C# there is no Bind. A lambda that calls your method with the extra value does the same job.
Make your own signal
Section titled “Make your own signal”Declare the signal at the top of the script with the parameters it will send, then call .emit() where the value changes:
MainNode2D
connects to the signal
PlayerCharacterBody2D
owns and emits the signal
extends CharacterBody2D
signal health_changed(new_health: int)
var health := 100
func take_damage(amount: int) -> void: health -= amount health_changed.emit(health)extends Node2D
func _ready() -> void: $Player.health_changed.connect(_on_player_health_changed)
func _on_player_health_changed(new_health: int) -> void: print("Health: ", new_health)using Godot;
public partial class Player : CharacterBody2D{ [Signal] public delegate void HealthChangedEventHandler(int newHealth);
private int _health = 100;
public void TakeDamage(int amount) { _health -= amount; EmitSignal(SignalName.HealthChanged, _health); }}using Godot;
public partial class Main : Node2D{ public override void _Ready() { GetNode<Player>("Player").HealthChanged += OnPlayerHealthChanged; }
private void OnPlayerHealthChanged(int newHealth) { GD.Print($"Health: {newHealth}"); }}Notice the direction. Player owns the signal and emits it without knowing who is listening. Main, the node above, connects to it. The child never reaches up to its parent, so you can drop Player into any scene and it still works.