音频
音效用 sfx(),音乐用 music(),和贴图一样在场景的 static assets 里声明。格式统一用 mp3。
ts
import { AudioStreamPlayer, music, Scene, sfx } from 'sapling2d'
export class Stage extends Scene {
// 音效预解码(可叠加),音乐流式播放;格式统一用 mp3
static override assets = { pop: sfx('pop.mp3'), bgm: music('bgm.mp3') }
override ready() {
this.add(new AudioStreamPlayer({ stream: Stage.assets.bgm, loop: true, volume: 0.5, autoplay: true }))
}
pop() {
this.tree.audio.play(Stage.assets.pop) // 一次性音效
}
hit() {
// 一帧里可能命中几十次:同一个音效最多同时播 3 个,多的直接不播
this.tree.audio.play(Stage.assets.pop, { maxVoices: 3, volume: 0.6 })
}
toggleMusic() {
const audio = this.tree.audio
audio.setBusMute('Music', !audio.isBusMuted('Music')) // 总线:Master / Music / SFX
}
}
// 浏览器在第一次触摸 / 点击 / 按键时自动解锁声音;切到后台自动挂起播放
- 一次性音效:
this.tree.audio.play(stream, { volume, loop, bus, maxVoices })。 - 背景音乐这类需要控制的声音:
AudioStreamPlayer节点,属性stream、volume、loop、bus、autoplay,方法play()/stop(),信号finished。 maxVoices限制同一个声音同时播放的数量,超过时这次不播。命中、爆炸这类一帧可能触发几十次的音效都应该设。- 音效预解码,可以叠加播放;音乐流式播放。
总线与音量
- 三条总线:
Master、Music、SFX。setBusMute/isBusMuted做静音开关。 - 音量是 0–1,不是 dB。
平台行为
- 浏览器在第一次触摸、点击或按键时自动解锁声音;切到后台时自动挂起。
tree.paused不会暂停声音。
测试
无头模式不发声,只记录:g.audio.log 是播放记录,g.audio.playing 是正在播放的声音,g.audio.finishAll() 模拟全部播放结束。
ts
const g = await createTestGame({ main: Stage })
g.scene.pop()
g.scene.toggleMusic()
// 无头模式不发声,只记录:g.audio.log / g.audio.playing;g.audio.finishAll() 模拟播放结束
expect(g.audio.log.map((s) => `${s.kind}:${s.path}:${s.volume}`)).toEqual(['music:bgm.mp3:0', 'sfx:pop.mp3:1'])
for (let i = 0; i < 10; i++) g.scene.hit()
expect(g.audio.playing.filter((s) => s.path === 'pop.mp3').length).toBe(3) // 先前的 pop 也算在内