Skip to content

信号 ​

信号是强类型的事件。节点把信号声明成只读字段,其他节点用 connect 监听。

ts
import { Node, Node2D, Scene, Signal } from 'sapling2d'

class Enemy extends Node2D {
  readonly died = new Signal<[points: number]>() // 强类型信号:声明成只读字段
  hp = 3

  hit() {
    if (--this.hp > 0) return
    this.died.emit(10)
    this.queueFree() // 帧末销毁;销毁时它声明的信号自动断开
  }
}

class ScoreKeeper extends Node {
  total = 0
}

export class Battle extends Scene {
  enemy!: Enemy
  keeper!: ScoreKeeper

  override ready() {
    this.keeper = this.add(new ScoreKeeper())
    this.enemy = this.add(new Enemy())
    // 第二个参数是 owner:owner 被销毁时连接自动断开。监听函数用箭头函数,不会丢 this
    this.enemy.died.connect((points) => (this.keeper.total += points), this)
  }
}

自动断开 ​

connect(fn, owner) 的第二个参数是 owner:owner 被销毁时,这条连接自动断开。节点被销毁时,它自己声明的信号上的所有连接也会断开。所以一般不需要手动 disconnect。

监听函数用箭头函数,不会丢 this。

等待信号 ​

信号可以 await:

ts
const points = await enemy.died // 等下一次触发

await signal 会错过同步紧接着的 emit。要先注册、再同步触发时,用 wait():

ts
// 等下一次触发:await enemy.died(游戏里 emit 发生在之后的帧);需要先注册再同步触发时用 wait()
const s = new Signal<[n: number]>()
const next = s.wait()
s.emit(7)
expect(await next).toBe(7)

内置信号 ​

很多节点自带信号,例如 Node2D.clicked、RigidBody2D.bodyEntered、Timer.timeout、AnimatedSprite2D.animationFinished、tree.sceneChanged。

MIT 许可证