Skip to content

音频设计指南

这不是一个单独的食谱 -- 它是一系列使用 Yumina 音频系统打造完整音景的技巧合集:跟随场景变化的 BGM、关键词触发的音效、循环环境音、丝滑的 crossfade,以及从自定义 UI 和 AI 叙述中控制音乐的各种方式。


概览

Yumina 的音频系统有多个入口点,可以在不同层级控制声音:

控制方式在哪里设置特点
播放列表自动播放音频 选项卡最简单 -- 背景音乐在玩家进入世界时自动开始
条件 BGM音频 选项卡当变量/关键词/轮次条件满足时自动切换音轨,不需要行为
行为 + 播放音频动作行为 选项卡场景切换时 crossfade,与变量更改和条目切换组合使用
AI 音频指令AI 在回复中写 [audio: ...]AI 决定播放什么和何时播放 -- 最灵活,也最不可预测
Root Component 音频 API自定义 UI 部分从自定义按钮触发,适合点唱机风格的交互 UI

本指南逐一介绍 7 种模式。挑选你需要的,或者组合使用。


模式 1:基础 BGM 设置

你将构建什么

玩家进入你的世界后立即开始播放的背景音乐 -- 循环播放,不需要额外设置。

逐步操作

第 1 步:上传音频轨道

编辑器 → 音频 选项卡 → 点击「添加音轨」

字段原因
显示名称Main Theme供你自己参考
IDmain_theme所有引用使用此 ID
类型BGM背景音乐
音频文件上传你的 .mp3.ogg 文件支持常见音频格式
循环开启BGM 通常需要循环
音量0.7不要太大声 -- 给音效和环境音留出空间
淡入2渐进淡入,不突兀

想要多于一首音轨?重复以上步骤创建更多音轨 -- 例如 explore_bgm(探索)、battle_bgm(战斗)、town_bgm(城镇)。

第 2 步:设置播放列表

仍在 音频 选项卡中,找到「BGM 播放列表」部分:

字段原因
音轨列表选择 main_theme(如果有多首则全选)列表中的音轨按顺序播放
播放模式loopshuffleloop = 顺序播放,循环;shuffle = 随机
自动播放开启玩家进入世界时音乐开始
等待开场白关闭(或开启,取决于你的偏好)如果开启,音乐等到玩家发送第一条消息后才开始
间隔秒数0(或 2音轨之间的暂停;0 = 无缝衔接

效果

玩家进入世界 → 音乐自动开始(带 2 秒淡入)→ 一首结束后下一首开始 → 最后一首播完后循环回第一首。

只有一首 BGM 音轨?

如果你只有一首歌,只需开启该音轨的 loop 并单独放在播放列表中。你甚至可以完全跳过播放列表,改用条件 BGM 或 AI 指令来控制。


模式 2:场景切换时 BGM 淡入淡出

你将构建什么

当玩家从「村庄」移动到「地牢」时,村庄的柔和音乐逐渐淡出,同时地牢的阴暗音乐逐渐淡入 -- 两条音轨在短暂的重叠期间同时播放,使过渡平滑而富有电影感。

工作原理

玩家点击「前往地牢」按钮
  → 行为触发:设置变量 location = "dungeon"
  → 同一行为的播放音频动作:crossfade 到 dungeon_bgm
  → 旧音轨淡出 + 新音轨淡入,过渡时长 2 秒

逐步操作

第 1 步:准备音频轨道

音频 选项卡中,创建两条(或更多)BGM 音轨:

  • village_bgm -- 村庄音乐,类型 BGM,循环开启
  • dungeon_bgm -- 地牢音乐,类型 BGM,循环开启

village_bgm 放入播放列表作为默认音轨,自动播放开启。

第 2 步:创建变量

编辑器 → 变量 选项卡 → 添加变量

字段
显示名称Current Location
IDlocation
类型String
默认值village

第 3 步:创建行为

编辑器 → 行为 选项卡 → 添加行为

行为名称: Go to Dungeon

触发器: Action → Action ID:go-dungeon

动作(按顺序):

#动作类型设置用途
1设置变量location 设为 dungeon记录玩家去了地牢
2播放音频音轨 dungeon_bgm,操作:crossfade,淡入淡出时长 2丝滑的音轨切换
3启用条目Dungeon Atmosphere开启地牢知识
4禁用条目Village Atmosphere关闭村庄知识

创建一个配套的「返回村庄」行为,action ID 为 go-village,做相反的操作(crossfade 到 village_bgm,交换条目开关)。

第 4 步:从 Root Component 触发

在 Root Component index.tsx 中(在 <Chat renderBubble> 回调内或任何自定义按钮上),调用 executeAction

tsx
<button onClick={() => api.executeAction("go-dungeon")}>
  Go to Dungeon
</button>

行为按顺序执行所有动作 -- 修改变量、切换音乐、切换条目 -- 全部通过一次按钮点击完成。

什么是 crossfade? 交叉淡入淡出 -- 旧音轨逐渐变小声,同时新音轨逐渐变大声。两条音轨在短暂时间内同时播放,听起来像电影场景过渡而不是突然中断。淡入淡出时长 2-3 秒效果最佳。


模式 3:关键词触发音效

你将构建什么

当 AI 写到「explosion」时自动播放爆炸音效。当玩家说「open the door」时播放门吱呀声。不需要行为 -- 你直接在 音频 选项卡的条件 BGM 部分配置。

工作原理

条件 BGM 有名为 ai-keyword(AI 关键词)和 keyword(玩家关键词)的触发类型。引擎扫描每条消息的文本,当关键词匹配时播放对应的音轨。虽然名为「条件 BGM」,但它可以指向任何音轨类型 -- 包括 SFX。

逐步操作

第 1 步:创建 SFX 音轨

音频 选项卡中,创建音效音轨:

爆炸 SFX:

字段
显示名称Explosion
IDexplosion_sfx
类型SFX
循环关闭(音效通常只播放一次)
音量0.9

开门 SFX:

字段
显示名称Door Open
IDdoor_open_sfx
类型SFX
循环关闭
音量0.8

第 2 步:创建条件 BGM 规则

仍在 音频 选项卡中,找到「条件 BGM」部分 → 点击「添加规则」

规则 1:AI 说「explosion」时播放 SFX

字段原因
名称AI Explosion SFX供你自己参考
触发类型AI Keyword(ai-keyword当 AI 回复包含指定关键词时触发
关键词explosionblastdetonate你可以添加多个同义词 -- 匹配任何一个即触发
目标音轨explosion_sfx播放爆炸音效
停止当前 BGM关闭SFX 叠加在 BGM 之上 -- 不要停止音乐

规则 2:玩家说「open the door」时播放 SFX

字段原因
名称Player Door SFX供你自己参考
触发类型Player Keyword(keyword当玩家消息包含指定关键词时触发
关键词open the doorpush the dooropen door多个同义词
目标音轨door_open_sfx播放开门音效
停止当前 BGM关闭同上

效果

AI 写道:"BOOM — 一声震耳欲聋的爆炸震撼了整个山坡,火光照亮了整片天空。"
  → 引擎扫描并匹配「explosion」→ 自动播放 explosion_sfx
  → 玩家在 BGM 继续播放的同时听到爆炸音效

玩家输入:"我走到门前把它打开。"
  → 引擎扫描并匹配「open the door」→ 自动播放 door_open_sfx

SFX 与 BGM

SFX(音效)播放一次即停止。BGM(背景音乐)根据播放列表循环或继续。当条件 BGM 规则指向 SFX 类型的音轨时,它播放一次且不会替换当前背景音乐。但如果 stopPreviousBGM 设为 true,它会在播放前先停止当前 BGM -- SFX 通常不需要这样做。


模式 4:条件 BGM -- 变量驱动的自动切换

你将构建什么

不需要行为 -- 直接在 音频 选项卡中配置规则:当 hp 降到 20 以下时,自动切换到紧张的危机音乐;当 hp 回到 20 以上时,切换回默认音轨。

工作原理

条件 BGM 的 variable 触发类型在每次变量变化后自动检查。条件满足 → 切换到目标音轨;条件不再满足 → 根据 fallback 设置回退(返回播放列表的默认音轨,或返回之前播放的音轨)。

逐步操作

第 1 步:准备音频轨道

确保 音频 选项卡中有:

  • explore_bgm -- 默认探索音乐(在播放列表中)
  • crisis_bgm -- 危机音乐(仅在条件触发时播放;不需要在播放列表中)

第 2 步:创建条件 BGM 规则

音频 选项卡 → 条件 BGM → 添加规则

字段原因
名称Low HP Crisis Music供你自己参考
触发类型Variable(variable基于变量值决定
条件hp < 20当 HP 低于 20 时触发
条件逻辑All(all这里只有一个条件,所以 allany 效果相同
目标音轨crisis_bgm切换到危机音乐
优先级10如果多个规则同时匹配,优先级高的获胜
淡入时长1新音轨逐渐淡入
淡出时长1旧音轨逐渐淡出
停止当前 BGM开启播放危机音乐前停止探索音乐
回退default条件不再满足时(HP 回到 20 以上),自动返回播放列表的默认音轨

效果

玩家正在探索,BGM 是 explore_bgm
  → AI 回复:[hp: -15](hp 从 30 降到 15)
  → 引擎检测到 hp < 20,条件满足
  → explore_bgm 在 1 秒内淡出,crisis_bgm 在 1 秒内淡入
  → 氛围瞬间变得紧张

玩家使用治疗药水
  → AI 回复:[hp: +20](hp 从 15 回到 35)
  → 引擎检测到 hp 不再 < 20,条件不满足
  → fallback: "default" → 自动切换回 explore_bgm

多条件组合

你可以在单个规则中添加多个条件。例如:hp < 20 AND location == "dungeon" → 只有在地牢中且 HP 低时才播放危机音乐。将条件逻辑设为 all(全部匹配)。


模式 5:环境音循环

你将构建什么

在场景背景中持续播放的环境音 -- 雨声、风声、酒馆喧哗 -- 叠加在 BGM 之上,加深沉浸感。

工作原理

Ambient 是第三种音轨类型。它独立于 BGM 播放 -- 你可以同时播放一条 BGM 音轨和一条 Ambient 音轨。Ambient 通常设为低音量循环,作为持续的氛围背景。

逐步操作

第 1 步:创建 Ambient 音轨

音频 选项卡 → 添加音轨

字段
显示名称Rain
IDrain_ambient
类型Ambient
循环开启
音量0.3(环境音应该比 BGM 安静 -- 它是背景)
淡入3 秒(逐渐出现,不突兀)
淡出3

根据需要创建更多:wind_ambient(风声)、tavern_ambient(酒馆喧哗)、forest_ambient(鸟鸣虫叫)。

第 2 步:通过条件 BGM 控制环境音

与模式 4 相同 -- 使用条件 BGM 规则控制环境音何时播放。

规则:在森林中播放森林环境音

字段
名称Forest Ambient
触发类型Variable(variable
条件location == forest
目标音轨forest_ambient
停止当前 BGM关闭
回退default

关键:stopPreviousBGM 必须关闭。 环境音叠加在 BGM 之上 -- 它不应该停止背景音乐。如果你开启它,切换环境音轨也会杀死当前正在播放的 BGM。

你也可以通过行为控制环境音

如果你已经有场景切换行为(如模式 2),只需在行为的动作列表中添加一个「播放音频」动作,指向环境音轨:

#动作类型设置用途
1设置变量location 设为 forest记录位置
2播放音频forest_bgm,操作:crossfade,淡入淡出 2秒切换 BGM
3播放音频forest_ambient,操作:play,淡入 3秒叠加环境音
4播放音频tavern_ambient,操作:stop,淡出 3秒停止旧的环境音

这样,一个行为同时处理 BGM 切换和环境音替换。

音量建议

BGM:通常 0.5-0.7。Ambient:0.2-0.4。SFX:0.7-1.0。三个层级使用不同音量,它们就不会互相打架。


模式 6:从 Root Component 控制音频

你将构建什么

Root Component 中的「点唱机」-- 几个按钮各播放一条不同的音轨,加上一个「停止」按钮。这是纯 UI 控制 -- 不需要行为或条件规则。

工作原理

useYumina() 提供这些音频 API(所有时长单位均为):

  • api.playAudio?.(trackId, opts) -- 播放指定音轨。opts 包含 volumefadeDurationchainTomaxDurationduckBgm,以及 loop(覆盖该音轨的循环设置,仅对本次播放生效)
  • api.stopAudio?.(trackId?) -- 停止指定音轨(省略 ID 则停止所有)。会销毁该元素 -- 想稍后续播请用 pauseAudio
  • api.pauseAudio?.(trackId) / api.resumeAudio?.(trackId) -- 真正的原地暂停/续播
  • api.onAudioEnded?.(cb) -- 当一条非循环音轨播放结束时运行 cb(trackId);返回一个取消订阅的函数。用于让播放列表自动播放下一首:
tsx
React.useEffect(() => api.onAudioEnded?.((endedId) => {
  if (endedId === currentTrackId && mode !== "single") playNext();
}), [currentTrackId, mode]);

这些方法都可以直接在 Root Component index.tsx 中调用(在 <Chat renderBubble> 回调内,或任何你添加的按钮上)。

逐步操作

第 1 步:准备音频轨道

确保 音频 选项卡中有你想播放的音轨(按模式 1 创建)。假设你有:

  • jazz_bgm -- 爵士乐
  • rock_bgm -- 摇滚
  • classical_bgm -- 古典

第 2 步:编写 Root Component 代码

编辑器 → 自定义 UI 部分 → 打开 index.tsx → 粘贴以下代码(替换默认的 return <Chat />):

tsx
export default function MyWorld() {
  const api = useYumina();
  const msgs = api.messages || [];

  const tracks = [
    { id: "jazz_bgm", label: "Jazz", color: "#7c3aed" },
    { id: "rock_bgm", label: "Rock", color: "#dc2626" },
    { id: "classical_bgm", label: "Classical", color: "#0891b2" },
  ];

  return (
    <Chat renderBubble={(msg) => {
      const isLastMsg = msg.messageIndex === msgs.length - 1;
      return (
    <div>
      <div
        style={{ color: "#e2e8f0", lineHeight: 1.7 }}
        dangerouslySetInnerHTML={{ __html: msg.contentHtml }}
      />

      {isLastMsg && (
        <div style={{
          marginTop: "12px",
          padding: "12px",
          background: "rgba(30,41,59,0.5)",
          borderRadius: "8px",
          border: "1px solid #334155",
        }}>
          <div style={{ fontSize: "12px", color: "#94a3b8", marginBottom: "8px" }}>
            Jukebox
          </div>
          <div style={{ display: "flex", gap: "8px", flexWrap: "wrap" }}>
            {tracks.map((t) => (
              <button
                key={t.id}
                onClick={() => api.playAudio?.(t.id, { fadeDuration: 1.5 })}
                style={{
                  padding: "8px 16px",
                  background: t.color,
                  border: "none",
                  borderRadius: "6px",
                  color: "#fff",
                  fontSize: "13px",
                  cursor: "pointer",
                }}
              >
                {t.label}
              </button>
            ))}
            <button
              onClick={() => api.stopAudio?.()}
              style={{
                padding: "8px 16px",
                background: "#475569",
                border: "none",
                borderRadius: "6px",
                color: "#e2e8f0",
                fontSize: "13px",
                cursor: "pointer",
              }}
            >
              Stop
            </button>
          </div>
        </div>
      )}
    </div>
      );
    }} />
  );
}

逐行说明:

  • MyWorld() 是 Root Component -- 世界的 UI 入口。<Chat renderBubble={...} /> 让平台负责消息列表、输入框和滚动;我们只自定义每个气泡的布局
  • api.playAudio?.(t.id, { fadeDuration: 1.5 }) -- 播放指定音轨,带 1.5 秒淡入。如果有其他音轨正在播放,会自动先停止它
  • api.stopAudio?.() -- 不带参数调用 = 停止所有当前播放的音频
  • msg.messageIndex === msgs.length - 1 -- 只在最后一条消息上显示点唱机,不会在每条消息上重复

更高级的用法

你可以读取变量来控制 UI 状态。例如,使用 now_playing 变量跟踪当前音轨 ID,然后在按钮上显示「正在播放」指示:

tsx
const nowPlaying = String(api.variables.now_playing || "");

// 在播放的同时更新变量
onClick={() => {
  api.playAudio?.(t.id, { fadeDuration: 1.5 });
  api.setVariable("now_playing", t.id);
}}

// 在按钮上显示状态
{nowPlaying === t.id ? "♪ " + t.label : t.label}

模式 7:AI 驱动的音频控制

你将构建什么

让 AI 在叙述中自然地控制音乐 -- 描述进入酒馆时播放欢快的手风琴曲,战斗打响时切换到激烈的战斗 BGM,角色受伤时播放疼痛音效。

工作原理

AI 可以在回复中嵌入 [audio: trackId action] 指令。引擎自动识别并执行这些指令,同时将它们从玩家看到的文本中去除 -- 就像剧本中的舞台提示,观众永远看不到,但剧组会照着执行。

逐步操作

第 1 步:注册 AI 可能使用的所有音轨

音频 选项卡中,创建你希望 AI 控制的所有音轨:

  • tavern_bgm -- 酒馆音乐
  • battle_bgm -- 战斗音乐
  • sword_clash_sfx -- 刀剑碰撞音效
  • pain_sfx -- 疼痛/受伤音效
  • rain_ambient -- 雨声环境音

第 2 步:通过系统提示词告诉 AI 有哪些音轨可用

AI 不会自动知道你注册了哪些音轨。你需要在 条目 选项卡中创建一个条目,列出可用音轨和使用规则:

条目名称: Audio Directive Reference

区域: System Presets

内容:

[Audio Control System]
You can use the following audio directives in your replies to control music and sound effects. Directives are automatically executed and stripped from the text the player sees.

Available directive formats:
- [audio: trackId play] — play
- [audio: trackId play 2.0] — play with a 2-second fade-in
- [audio: trackId stop] — stop
- [audio: trackId stop 1.5] — stop with a 1.5-second fade-out
- [audio: trackId crossfade 2.0] — crossfade transition, 2-second overlap
- [audio: trackId volume 0.5] — adjust volume to 0.5
- [audio: trackId play chain:nextTrackId] — after this track finishes, automatically start the next one

Available tracks:
- tavern_bgm — lively tavern accordion music (good for social scenes, shopping)
- battle_bgm — intense battle music (good for combat, chase scenes)
- sword_clash_sfx — sword clash sound effect (good for melee action descriptions)
- pain_sfx — pain/injury sound effect (good for when a character gets hurt)
- rain_ambient — rain ambient sound (good for rainy scenes)

Usage guidelines:
- Insert audio directives at natural narrative points
- Use crossfade for scene transitions, with a duration of 1.5-2.5 seconds
- Pair sound effects with action descriptions, placing them near the corresponding text
- Don't overdo it — 2-3 audio directives per response at most

第 3 步:AI 回复示例

告诉 AI 这些规则后,它的回复可能看起来像这样:

You push open the tavern's heavy wooden door, and a rush of warm air hits your face. [audio: tavern_bgm crossfade 2.0]

The tavern is buzzing — someone's playing accordion in the corner, and a dwarf at the bar is shouting over a dice game. You've barely found a seat when a masked figure suddenly draws a blade and lunges at you!

[audio: battle_bgm crossfade 0.5] [audio: sword_clash_sfx play]

You throw yourself sideways on instinct. The table splits in two behind you.

玩家看到干净的叙述文本,同时听到:酒馆音乐淡入 → 突然切换到战斗音乐 + 刀剑碰撞音效。

chain 指令 -- 特殊用法

chain 让一条音轨在播放完毕后自动启动另一条:

The sound of war horns echoes through the valley — the battle is about to begin! [audio: war_horn_sfx play chain:battle_bgm]

war_horn_sfx 号角声播放完后,battle_bgm 自动开始 -- 先有一段序奏再进入主音轨,比直接切换更有仪式感。

AI 可能会忘记使用指令

AI 不会总是记得插入音频指令,尤其是在长对话中。对于关键场景的 BGM 切换(比如进入战斗区域),设置条件 BGM 规则(模式 4)作为后备。AI 指令是锦上添花;条件 BGM 是安全网。


综合快速参考

音轨类型

类型用途典型设置
BGM背景音乐循环开启,音量 0.5-0.7
SFX一次性音效循环关闭,音量 0.7-1.0
Ambient循环环境音循环开启,音量 0.2-0.4

5 种控制音频的方式

你想做什么用哪种方式在哪里设置
进入世界时自动播放 BGM播放列表 + 自动播放音频 选项卡 → BGM 播放列表
变量条件满足时自动切换音轨条件 BGM(variable 触发)音频 选项卡 → 条件 BGM
AI 回复包含关键词时播放 SFX条件 BGM(ai-keyword 触发)音频 选项卡 → 条件 BGM
玩家消息包含关键词时播放 SFX条件 BGM(keyword 触发)音频 选项卡 → 条件 BGM
在特定轮次切换音轨条件 BGM(turn-count 触发)音频 选项卡 → 条件 BGM
场景切换时 crossfade行为 + 播放音频动作行为 选项卡
通过按钮点击播放/停止Root Component api.playAudio?.() / api.stopAudio?.()自定义 UI 部分
AI 在叙述中触发音频AI 音频指令 [audio: trackId action]条目 选项卡(告诉 AI 规则)

AI 音频指令参考

指令效果
[audio: trackId play]播放
[audio: trackId play 2.0]播放,带 2 秒淡入
[audio: trackId stop]停止
[audio: trackId stop 1.5]停止,带 1.5 秒淡出
[audio: trackId crossfade 2.0]交叉淡入淡出过渡,2 秒重叠
[audio: trackId volume 0.5]调整音量
[audio: trackId play chain:nextId]播放完后自动启动下一条音轨

条件 BGM 触发类型

触发类型何时触发典型用途
variable变量条件满足时hp < 20 播放危机音乐
ai-keywordAI 回复包含关键词时AI 写「explosion」播放爆炸 SFX
keyword玩家消息包含关键词时玩家说「perform」播放音乐
turn-count到达特定轮次时第 10 轮播放倒计时音乐
session-start会话开始时固定的开场音轨

行为播放音频动作参数

参数描述
音轨 ID(trackId匹配音频选项卡中注册的音轨 ID
操作(actionplay(播放)、stop(停止)、crossfade(交叉淡入淡出切换)、volume(调整音量)
音量(volume0-1,可选
淡入淡出时长(fadeDuration秒,可选;crossfade 建议 1.5-3 秒

Root Component 音频 API

方法描述
api.playAudio?.(trackId, opts)播放音轨。opts 可包含 fadeDuration(秒)、loop
api.stopAudio?.(trackId?)停止音轨。省略 ID 则停止所有
api.pauseAudio?.(trackId) / api.resumeAudio?.(trackId)原地暂停/续播一条音轨(保留播放位置)
api.onAudioEnded?.(cb)订阅音轨播放结束事件;返回取消订阅函数。用于自动播放下一首

常见问题

症状可能原因修复方法
完全没有声音浏览器阻止了自动播放现代浏览器要求用户交互(点击、输入)后才允许音频播放。让玩家先发送一条消息,或在播放列表中开启「等待开场白」
BGM 过渡听起来很断断续续没有使用 crossfade确保行为的「播放音频」操作设为 crossfade,淡入淡出时长至少 1.5 秒
SFX 和 BGM 互相打断stopPreviousBGM 设为了 trueSFX 类型的条件 BGM 规则应将「停止当前 BGM」关闭
AI 不使用音频指令条目没有告诉 AI 这些指令创建一个 System Presets 条目,列出所有可用音轨 ID 和指令格式(见模式 7)
环境音太大声音量太高环境音应该 0.2-0.4,BGM 0.5-0.7,保持层次分离
条件 BGM 没有触发变量值类型不匹配确保条件的值类型与变量类型匹配(例如数值变量需要数值比较,而不是字符串比较)
多个规则冲突优先级相同给不同的条件 BGM 规则设置不同的优先级值 -- 数字越大优先级越高

这是食谱 #14:音频设计指南

音频系统的设计理念是:简单的事情开箱即用(播放列表 + 自动播放),复杂性逐层解锁(条件 BGM → 行为控制 → AI 指令 → 自定义 API)。你不需要一次学会所有模式 -- 从模式 1 开始,需要更精细控制时再回来学习更多。