声明与发出
本课属于 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。如果连接到对象未声明的信号名称,那么这行代码运行时会出错。
挑战
简单编写一个 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!")
这节课包含一个小测验。开始学习即可作答,并记录你的进度。
面向对象编程中的所有课
自己练习: GDScript 在线编译器