Menu
CoddyTech

连接信号

本课属于 Coddy GDScript 学习旅程的“面向对象编程”单元。第 37 课,共 65 课。

connect() 接收要运行的函数,类型为 Callable。写出方法名,不要加括号;也可以写一个 lambda:

chest.opened.connect(_on_chest_opened)          # 此脚本的一个方法
chest.opened.connect(quest_log.add_entry)       # 另一个对象的方法
chest.opened.connect(func(): print("Creak!"))   # 一个 lambda 函数

Godot 会按“_on_”、发出信号的对象、信号的顺序来命名监听方法:_on_chest_opened。一个信号可以连接多个函数,它们会按连接顺序运行。

监听的对象通常会在接收发射者的方法中连接自己:

# 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

将同一个函数两次连接到同一个信号会导致错误,断开未连接的函数也会导致错误。先用 is_connected() 检查。disconnect() 会停止监听,下一次发出信号时不再运行该函数。

将 CONNECT_ONE_SHOT 作为第二个参数传入,连接会在首次调用后自动移除:chest.opened.connect(_on_first_open, CONNECT_ONE_SHOT)。

lambda 会保留创建时局部变量的值。在已连接的 lambda 中,count += 1 会更改这个副本,而 _ready() 中的 count 仍为 0。像 QuestLog 那样,在监听器对象的成员变量中保存计数器。

challenge icon

挑战

简单

射击场里有一些目标,被射中时会发出 hit 信号。编写一个会监听这些信号的 ScoreBoard:

  • watch(target) 会将目标的 hit 信号连接到 _on_target_hit。监听已经在监听的目标不会有任何变化。
  • ignore(target) 会断开连接。忽略未在监听的目标不会有任何变化。
  • _on_target_hit() 会给 points 加上 10,并打印 Hit! Score: N。

重复监听,或忽略未在监听的目标,都不能导致错误,因为任何错误都会导致测试失败。target.gd 已经完成。

提供的代码会读取目标数量,然后读取诸如 watch 0、ignore 0 和 shoot 0 这样的命令,并打印最终得分。对于两个目标和命令 watch 0、shoot 0、shoot 1、watch 1、watch 1、shoot 1、ignore 0、shoot 0,程序会打印:

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

将代码写在 score_board.gd 和 target.gd 中。main.gd 包含提供的输入/输出代码,不能编辑。

亲手试试

extends Node

func _ready():
	# 提供的输入/输出代码:保持原样
	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 icon自我测试

这节课包含一个小测验。开始学习即可作答,并记录你的进度。

面向对象编程中的所有课

自己练习: GDScript 在线编译器