表现与音频
客户端音频
管理音频资产、程序化声音、会话生命周期和浏览器播放限制。
表现与音频
先决定声音何时结束,再决定播放什么
点击音、环境循环和背景音乐有不同生命周期。音频只表达结果,不修改玩家状态;浏览器拒绝播放时,规则仍必须完成。
# 文件资产还是程序化音频
| 需求 | 推荐 |
|---|---|
| 已有音乐、配音或样本 | 项目音乐资产库与场景/事件/控件音频字段 |
| 短提示音、合成器、节奏和程序化音乐 | tools.runClientAudio |
项目音乐资产统一上传、试听和复用 URL。不要为同一文件重复上传或在脚本中散落手写地址。
# 两层脚本与生命周期 API
外层 Worker 脚本读取状态并调用:
| 方法 | 作用 |
|---|---|
tools.runClientAudio(script, options?) |
启动音频会话 |
tools.disposeClientAudio(label) |
停止指定会话 |
tools.clearClientAudio() |
停止当前页面所有客户端音频 |
内层字符串注入 Tone、audio、payload、runtime、session 和 pointer。脚本按原生 JavaScript 执行,不做浏览器全局遮蔽;不要借此绕过宿主资源管理或请求平台私有接口。运行时数据通过 payload 传入。
tools.runClientAudio(`
await audio.resume();
const synth = audio.createSynth({
oscillator: { type: "triangle" },
envelope: { attack: .005, decay: .08, sustain: .02, release: .12 }
});
synth.setVolume(-12);
synth.triggerAttackRelease("C5", "16n");
`, { label: "ui-confirm" });
短音通常播放后自动销毁。循环和背景音乐在内层 await audio.keepAlive(),外层用稳定 label 停止或替换。不要假设场景切换会自动清理所有长音。
# 内层 API
| 类别 | 方法 |
|---|---|
| 基础 | resume、now、toSeconds、wait、keepAlive、dispose、cleanup、log |
| 乐器 | createSynth、createPolySynth、createNoiseSynth、createMembraneSynth |
| 调度 | createLoop、createSequence、createPattern、scheduleOnce |
| 音乐工具 | Tone.Frequency(...) 等 Tone 转换能力 |
宿主包装的乐器和调度器会跟随会话清理。裸 new Tone.Synth() 或 new Tone.Pattern() 可能脱离宿主跟踪,不作为默认做法。
# 完整乐器与调度句柄
所有 create*Synth 方法都返回受跟踪乐器句柄:
| 方法/属性 | 用途 |
|---|---|
kind、id、type |
句柄身份和乐器类型 |
set(options?) |
修改受支持的 Tone 乐器参数 |
setVolume(db) |
设置分贝音量 |
triggerAttackRelease(note, duration, time?, velocity?) |
播放完整音符/和弦 |
triggerAttack(note, time?, velocity?) |
开始持续音符/和弦 |
triggerRelease(time?) / releaseAll(time?) |
释放正在播放的声部 |
start(time?, offset?, duration?) |
在类型支持时启动音源 |
stop(time?) |
在类型支持时停止 |
seek(offset?, time?) |
在类型支持时跳转位置 |
loaded() |
可加载音源是否准备完成 |
dispose() |
释放该受跟踪资源 |
createLoop、createSequence、createPattern 和 scheduleOnce 返回调度句柄:
| 方法/属性 | 用途 |
|---|---|
kind、id、type |
句柄身份及 loop/sequence/pattern/schedule 类型 |
start(time?) |
开始或安排资源 |
stop(time?) |
停止后续迭代 |
cancel(time?) |
从指定时间取消已调度事件 |
set(options?) |
修改受支持调度参数 |
dispose() |
停止并释放资源 |
audio.dispose(handle) 是通用安全释放入口,audio.cleanup(reason?) 会销毁整个当前音频会话。runtime 提供 sessionId、label、now()、toneNow() 和 getContextState();注入的 session 是前端脚本共享 session,不是玩家存档。
# 音高与并发规则
音符必须包含八度,例如 C4、F#4、Bb3。同一单音 synth 的开始时间必须严格递增;同一拍多个音使用 createPolySynth()。
tools.runClientAudio(`
await audio.resume();
const poly = audio.createPolySynth();
poly.setVolume(-14);
poly.triggerAttackRelease(["C4", "E4", "G4"], "8n");
`, { label: "success-chord" });
程序化背景音乐使用稳定 label:
tools.runClientAudio(`
await audio.resume();
const synth = audio.createSynth();
audio.createSequence(payload.notes, "8n", (time, note) => {
if (note) synth.triggerAttackRelease(note, "8n", time);
});
await audio.keepAlive();
`, {
label: "scene-theme",
payload: { notes: ["C4", "E4", "G4", "B4"] }
});
停止时调用 tools.disposeClientAudio("scene-theme")。
# 高频交互
js_enter 可以播放一次短音,js_leave 清理持续音,js_long 播放确认或蓄力反馈。js_move 不要每个事件创建新会话;复用稳定 label,或完全避免连续发声。
# 自动播放、音量和可访问性
第一次发声前通常需要 await audio.resume(),仍可能受浏览器用户手势策略限制。玩家可从悬浮设置调节或静音客户端音效。重要信息不能只靠声音表达,必须有视觉或文本反馈。
# 纯 AI 提示词与验收
说明资产、一次性/循环类型、label、开始、停止、淡出、场景切换、静音和失败行为。测试首次无手势、重复触发、同 label 替换、场景切换、后台恢复、静音、非法音符、单音并发错误和低性能设备。
返回:客户端视觉特效。