Menu
CoddyTech

Connecting to Signals

Part of the Object Oriented Programming section of Coddy's GDScript journey. Lesson 37 of 65.

connect() takes the function to run, as a Callable. Name a method without parentheses, or write a lambda:

chest.opened.connect(_on_chest_opened)          # a method of this script
chest.opened.connect(quest_log.add_entry)       # a method of another object
chest.opened.connect(func(): print("Creak!"))   # a lambda

Godot names a listening method _on_, then the emitter, then the signal: _on_chest_opened. A signal can have many connected functions, and they run in the order they were connected.

An object that listens usually connects itself, in a method that receives the emitter:

# quest_log.gd
class_name QuestLog
extends RefCounted

var chests_opened := 0

func track(chest: Chest) -> void:
	if not chest.opened.is_connected(_on_chest_opened):
		chest.opened.connect(_on_chest_opened)

func untrack(chest: Chest) -> void:
	if chest.opened.is_connected(_on_chest_opened):
		chest.opened.disconnect(_on_chest_opened)

func _on_chest_opened() -> void:
	chests_opened += 1

Connecting the same function to the same signal twice is an error, and so is disconnecting a function that is not connected. is_connected() checks first. disconnect() stops listening, and the next emit no longer runs that function.

Pass CONNECT_ONE_SHOT as the second argument and the connection removes itself after its first call: chest.opened.connect(_on_first_open, CONNECT_ONE_SHOT).

A lambda keeps the value a local variable had when the lambda was created. count += 1 inside a connected lambda changes that copy, and count in _ready() stays 0. Keep a counter in a member variable of a listener object, as QuestLog does.

challenge icon

Challenge

Easy

A shooting range has targets that emit hit when they are shot. Write the ScoreBoard that listens to them:

  • watch(target) connects the target's hit signal to _on_target_hit. Watching a target that is already watched changes nothing.
  • ignore(target) disconnects it. Ignoring a target that is not watched changes nothing.
  • _on_target_hit() adds 10 to points and prints Hit! Score: N.

Watching twice or ignoring an unwatched target must not cause an error, because any error fails the test. target.gd is complete.

The supplied code reads the number of targets, then commands such as watch 0, ignore 0 and shoot 0, and prints the final score. For two targets and the commands watch 0, shoot 0, shoot 1, watch 1, watch 1, shoot 1, ignore 0, shoot 0 it prints:

Shot target 0
Hit! Score: 10
Shot target 1
Shot target 1
Hit! Score: 20
Shot target 0
Final score: 20

Your code goes in score_board.gd and target.gd. main.gd holds the supplied input/output code and cannot be edited.

Try it yourself

extends Node

func _ready():
	# Supplied input/output code: keep it as it is
	var board := ScoreBoard.new()
	var targets: Array[Target] = []
	for i in int(OS.read_string_from_stdin()):
		targets.append(Target.new())
	var count := int(OS.read_string_from_stdin())
	for i in count:
		var parts := OS.read_string_from_stdin().split(" ")
		var target := targets[int(parts[1])]
		if parts[0] == "watch":
			board.watch(target)
		elif parts[0] == "ignore":
			board.ignore(target)
		else:
			print("Shot target ", parts[1])
			target.shoot()
	print("Final score: ", board.points)
quiz iconTest yourself

This lesson includes a short quiz. Start the lesson to answer it and track your progress.

All lessons in Object Oriented Programming

Practice on your own: Online GDScript compiler