Menu
CoddyTech

声明与发出

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

使用关键字 signal 和一个名称来声明信号,放在类顶部、变量旁边。信号名称用 snake_case 描述已经发生的事件,例如:died、door_opened、leveled_up。类可以根据需要声明任意数量的信号:

# player.gd
class_name Player
extends RefCounted

signal leveled_up
signal max_level_reached

const MAX_LEVEL := 3
var level := 1
var xp := 0

func add_xp(amount: int) -> void:
	xp += amount
	while xp >= 100 and level < MAX_LEVEL:
		xp -= 100
		level += 1
		leveled_up.emit()
		if level == MAX_LEVEL:
			max_level_reached.emit()

仅在事件发生的那一刻发出信号。对新玩家调用 add_xp(50) 不会发出任何信号。之后调用 add_xp(250),使 xp 达到 300:leveled_up 会发出两次,每升一级发出一次;max_level_reached 会发出一次,在升到 3 级时发出。

属性设置器是发出信号的好地方,因为这样无论哪段代码更改了值,信号都会触发:

signal died

var hp: int = 10:
	set(value):
		var was_alive := hp > 0
		hp = maxi(value, 0)
		if was_alive and hp == 0:
			died.emit()

当生命值为 0 的英雄再次受到攻击时,was_alive 会防止再次发出 died。

也可以通过信号名称的文本形式访问信号:emit_signal("died") 会发出该信号,hero.connect("died", _on_hero_died) 会连接到该信号,而 hero.has_signal("died") 会返回 true。如果连接到对象未声明的信号名称,那么这行代码运行时会出错。

challenge icon

挑战

简单

编写一个 Bomb,让它倒计时,并通过信号播报每一步。

  • 声明两个信号:beeped 和 exploded。
  • tick() 将 fuse 减少 1。如果引信仍大于 0,就发出 beeped;如果引信已经达到 0,就发出 exploded。
  • 引信变为 0 后,炸弹就已爆炸:后续的计时不会执行任何操作,也不会发出任何信号。

提供的代码会读取引信的初始值和要运行的计时次数,按名称连接两个信号,并从连接的函数中打印 Beep 和 BOOM!,然后打印剩余引信值。引信值为 3、计时 5 次时,打印结果为:

Beep
Beep
BOOM!
Fuse: 0

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

亲手试试

extends Node

func _ready():
	# 提供的输入输出代码:保持原样
	var bomb := Bomb.new(int(OS.read_string_from_stdin()))
	# 用信号名称作为文本调用 connect()
	bomb.connect("beeped", _on_beeped)
	bomb.connect("exploded", _on_exploded)
	var ticks := int(OS.read_string_from_stdin())
	for i in ticks:
		bomb.tick()
	print("Fuse: ", bomb.fuse)

func _on_beeped() -> void:
	print("Beep")

func _on_exploded() -> void:
	print("BOOM!")
quiz icon自我测试

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

面向对象编程中的所有课

自己练习: GDScript 在线编译器