连接信号
本课属于 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 那样,在监听器对象的成员变量中保存计数器。
挑战
简单射击场里有一些目标,被射中时会发出 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)
这节课包含一个小测验。开始学习即可作答,并记录你的进度。
面向对象编程中的所有课
自己练习: GDScript 在线编译器