Menu
CoddyTech

什么是信号?

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

游戏运行时,对象会发生各种变化:金币被收集,门打开,英雄的 hp 降到 0。程序的其他部分会对这些事件做出反应:分数增加,播放声音,出现游戏结束画面。

信号是对象在发生某件事时发送的消息。对象会发出信号,所有连接到该信号的函数都会运行:

# coin.gd
class_name Coin
extends RefCounted

signal collected

func collect() -> void:
	print("Coin picked up")
	collected.emit()

signal collected 声明了信号,collected.emit() 会发送信号。硬币不知道谁在监听。关心硬币的代码会将自己的一个函数连接到这个信号:

# main.gd
func _ready():
	var coin := Coin.new()
	coin.collected.connect(_on_coin_collected)
	coin.collect()
	print("Done")

func _on_coin_collected() -> void:
	print("Score +1")

输出为 Coin picked up、Score +1、Done。发出信号会立即运行已连接的函数,在 emit() 后的那一行之前执行。没有连接任何函数的信号发出时不会执行任何操作。

如果没有信号,collect() 就必须自己调用计分代码,因此每个金币都需要引用计分板。有了信号后,金币只需发出事件通知,任意数量的监听器都可以响应该事件,也可以没有监听器响应。

Godot 自带的节点也是以同样的方式工作的:点击 Button 时会发出 pressed,计时结束时 Timer 会发出 timeout。

challenge icon

挑战

简单

邮箱通过信号通知有新邮件。Mailbox 已声明 signal mail_arrived,并且有一个 letters 计数器。编写 deliver(kind):

  • 当 kind 是 "letter" 时,将 1 加到 letters,并发出 mail_arrived。
  • 其他类型(广告、传单)都会被丢弃:计数不变,也不会发出信号。

提供的代码会连接一个打印 You've got mail! 的函数,然后读取后续行数并逐行投递。away 这一行会断开该函数的连接,home 则会重新连接它,因此你不在家时收到的信件会计数,但不会发出通知。输入以下几行:letter、ad、away、letter,程序会打印:

Delivering: letter
You've got mail!
Delivering: ad
Delivering: letter
Letters: 2

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

亲手试试

extends Node

func _ready():
	# 提供的输入输出代码:保持原样。
	var mailbox := Mailbox.new()
	mailbox.mail_arrived.connect(_on_mail_arrived)
	var count := int(OS.read_string_from_stdin())
	for i in count:
		var line := OS.read_string_from_stdin()
		if line == "away":
			mailbox.mail_arrived.disconnect(_on_mail_arrived)
		elif line == "home":
			mailbox.mail_arrived.connect(_on_mail_arrived)
		else:
			print("Delivering: ", line)
			mailbox.deliver(line)
	print("Letters: ", mailbox.letters)

func _on_mail_arrived() -> void:
	print("You've got mail!")
quiz icon自我测试

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

面向对象编程中的所有课

自己练习: GDScript 在线编译器