Skip to content

音频系统

给你的世界加上耳朵能听到的东西——背景音乐、音效、环境音,让玩家不只是在"看"故事,而是"身处"故事里。


简单版

音频是沉浸感的放大器。想象一下:玩家走进一座古堡,文字再好,如果耳边没有风声和远处的钟鸣,总感觉差点意思。Yumina 的音频系统就是来补上这块拼图的。

三种音频类型:

  • BGM(背景音乐)——持续播放的音乐,比如探索时的悠扬旋律、战斗时的紧张节奏
  • SFX(音效)——一次性的短音频,比如开门声、爆炸声、获得物品的叮咚声
  • Ambient(环境音)——循环的氛围音,比如雨声、森林鸟叫、酒馆里的嘈杂人声

最快上手: 上传一首 BGM 音频文件,把 loop 设成 true,把播放列表的 autoPlay 打开——搞定。玩家一进入你的世界,音乐就自动响起来了。

想更进一步? 你可以:

  • 设置"条件 BGM"——当某个变量满足条件时自动切歌(比如进入战斗区域自动换战斗曲)
  • 让 AI 在叙述中触发音效——AI 写"门被猛地撞开"的时候,顺手带一条音频指令,玩家就能听到撞门声

音频系统的设计哲学是:简单的事情一步到位,复杂的事情分层解锁。你不需要一开始就搞懂所有字段,用到再学完全来得及。


详细版

AudioTrack(音频轨道)

每一条音频资源在系统里就是一个 AudioTrack,你可以把它理解为"一首歌"或"一段音效"的登记卡。

字段类型必填说明
idstring唯一标识符,AI 指令和规则里都用这个来引用它。比如 battle_bgmdoor_slam
namestring显示名称,给你自己看的。比如"战斗主题曲"
type"bgm" / "sfx" / "ambient"音频类型。决定了它在播放时的行为逻辑
urlstring音频文件的 URL 地址
loopboolean是否循环播放。BGM 和 Ambient 通常设为 true,SFX 通常不需要
volumenumber (0-1)音量大小。1 是最大声,0.5 是一半,0 是静音。不填默认满音量
fadeInnumber (秒)淡入时长,单位秒。比如 2 就是 2 秒渐渐响起来
fadeOutnumber (秒)淡出时长,单位秒。停止播放时不会突然断掉,而是慢慢消失
maxDurationnumber (秒)最大播放时长。到时间后自动停止(带淡出)。适合那种"播一小段就够了"的场景

一个世界可以注册多条 AudioTrack,它们都存在世界定义的 audioTracks 数组里。


BGMPlaylist(BGM 播放列表)

如果你有好几首 BGM,不想只循环一首,可以用播放列表把它们串起来。把它想象成一个"音乐盒"——你放几张碟进去,告诉它怎么播就行。

字段类型默认值说明
tracksstring[][]曲目 ID 数组,引用 audioTracks 里的 ID。播放顺序就是数组顺序
playMode"loop" / "shuffle" / "sequential""loop"loop: 列表播完从头再来;shuffle: 随机打乱顺序播;sequential: 播完一遍就停
autoPlaybooleantrue进入世界后是否自动开始播放
waitForFirstMessagebooleanfalse设为 true 的话,不会一进来就播,而是等玩家发出第一条消息后才开始。适合有开场白或角色创建流程的世界
gapSecondsnumber (0-30)0两首曲子之间的间隔秒数。设个 2 就会有短暂的静默过渡,更有"换碟"的感觉

每个世界只有一个 bgmPlaylist(可选)。如果你只有一首 BGM 并且设了 loop: true,其实不设播放列表也行,直接用条件 BGM 或 AI 指令来控制也可以。


ConditionalBGM(条件 BGM)

这是音频系统里最有趣的部分——让音乐跟着故事走。想象你在玩一个 RPG,走进酒馆是欢快的手风琴,走进地牢变成阴森的低音提琴,遇到 Boss 突然切成激昂的管弦乐。条件 BGM 就是实现这个效果的。

字段类型默认值说明
idstring唯一标识符
namestring显示名称,方便你自己管理
triggerType见下方"variable"触发方式
conditionsCondition[][]变量条件数组(triggerType 为 variable 时使用)
conditionLogic"all" / "any""all"条件之间的逻辑关系。all = 全部满足才触发,any = 任一满足就触发
keywordsstring[]关键词列表(triggerType 为 keywordai-keyword 时使用)
matchWholeWordsboolean是否全词匹配
atTurnnumber在第几回合触发(triggerType 为 turn-count 时使用)
everyNTurnsnumber每隔几回合触发一次
targetTrackIdstring触发后要播放的目标曲目 ID
prioritynumber0优先级。数字越大优先级越高。当多个条件同时满足时,优先级高的赢
fadeInDurationnumber (秒)1新曲目淡入时长
fadeOutDurationnumber (秒)1旧曲目淡出时长
stopPreviousBGMbooleantrue是否停掉正在播放的 BGM。通常保持 true,除非你想叠加多层音乐(比如叠加一层紧张的弦乐)
fallbackstring"default"条件不再满足时的行为:"default" 回到播放列表默认曲目,"previous" 回到之前那首,也可以填具体的 trackId

triggerType 的五种触发方式:

什么时候触发典型用法
variable游戏变量满足指定条件时location == "battle_arena" 时切战斗曲
ai-keywordAI 回复中出现指定关键词时AI 提到"战斗开始"时切歌
keyword玩家消息中出现指定关键词时玩家说"演奏"时触发音乐
turn-count到达指定回合数时第 10 回合切换到紧张的倒计时音乐
session-start会话开始时每次进入世界播放固定的开场曲

AI 音频指令

AI 不只是写文字——它还可以在回复中嵌入音频指令。这些指令对玩家不可见(引擎会自动剥离),但会触发对应的音频效果。就像电影剧本里的舞台指示,观众看不到,但工作人员会照着执行。

指令格式是 [audio: trackId action],和变量指令(比如 [health: -10])风格一致。

支持的指令:

指令说明
[audio: trackId play]播放指定曲目
[audio: trackId play 2.0]播放,并用 2 秒淡入
[audio: trackId stop]停止播放
[audio: trackId stop 1.5]停止,并用 1.5 秒淡出
[audio: trackId crossfade 0.8]交叉淡入淡出切换到这首曲目,过渡时长 0.8 秒
[audio: trackId volume 0.5]把音量调到 0.5(不停止播放)
[audio: trackId play chain:nextTrackId]播完这首后自动播下一首。比如先放一段战斗前奏 SFX,播完自动切到战斗 BGM

这些指令可以和状态变更指令混在一起使用。AI 可以写出这样的回复:

地牢深处传来轰鸣声,地面开始震动。[audio: earthquake play] 你的生命值因为落石受到了伤害。[health: -5] 恐惧感在心中蔓延。[fear: +10] 周围的环境音变得更加压抑。[audio: ambient volume 0.3]

玩家看到的只有干净的叙述文字,但会同时听到地震音效、感受到数值变化和氛围音量的降低。

规则系统也能触发音频。 在规则(Rule)的 actions 中,有一种 action 类型叫 play-audio,字段和 AudioEffect 一样(trackIdactionvolumefadeDuration)。这意味着你可以不依赖 AI,纯靠规则引擎来控制音乐——比如"当 hp 降到 20 以下时播放危机音乐"。


实用例子

例 1:最简单的循环 BGM

你有一首背景音乐,想让它从头到尾一直放。

AudioTrack 配置:

json
{
  "id": "main_theme",
  "name": "主题曲",
  "type": "bgm",
  "url": "https://example.com/main-theme.mp3",
  "loop": true,
  "volume": 0.7,
  "fadeIn": 2
}

BGMPlaylist 配置:

json
{
  "tracks": ["main_theme"],
  "playMode": "loop",
  "autoPlay": true
}

就这样,玩家进入世界就会听到音乐了。


例 2:战斗切歌

玩家探索时放轻松的音乐,进入战斗区域自动切成紧张的战斗曲,离开后切回来。

准备两条 AudioTrack:

json
[
  { "id": "explore_bgm", "name": "探索", "type": "bgm", "url": "...", "loop": true },
  { "id": "battle_bgm", "name": "战斗", "type": "bgm", "url": "...", "loop": true }
]

BGMPlaylist(默认播探索曲):

json
{
  "tracks": ["explore_bgm"],
  "playMode": "loop",
  "autoPlay": true
}

ConditionalBGM(进入战斗区域时切歌):

json
{
  "id": "battle_music_trigger",
  "name": "战斗音乐触发",
  "triggerType": "variable",
  "conditions": [
    { "variableId": "location", "operator": "eq", "value": "battle_arena" }
  ],
  "targetTrackId": "battle_bgm",
  "priority": 10,
  "fadeInDuration": 0.5,
  "fadeOutDuration": 0.5,
  "stopPreviousBGM": true,
  "fallback": "default"
}

location 变成 "battle_arena" 时,探索曲在 0.5 秒内淡出,战斗曲在 0.5 秒内淡入。当 location 变成别的值时(条件不再满足),fallback: "default" 让它自动回到播放列表里的探索曲。


例 3:AI 触发音效

让 AI 在叙述中自然地触发音效。比如"门被撞开"的场景。

注册一条 SFX:

json
{
  "id": "door_slam",
  "name": "撞门声",
  "type": "sfx",
  "url": "https://example.com/door-slam.mp3",
  "loop": false,
  "volume": 0.9
}

AI 的回复可能长这样:

走廊尽头传来沉重的脚步声,越来越近。突然——
砰!门被猛地撞开![audio: door_slam play]
一个身披铁甲的身影出现在门口。

玩家看到的是干净的叙述(没有 [audio: ...] 部分),同时耳边响起撞门的音效。


例 4:SFX 接 BGM(chain 用法)

战斗开始时先播一段号角音效,号角结束后自动衔接战斗 BGM。

号角声在山谷间回荡,敌军已经逼近![audio: war_horn play chain:battle_bgm]

chain:battle_bgm 的意思是:等 war_horn 这条 SFX 播完后,自动开始播放 battle_bgm。这比分开写两条指令更丝滑,因为衔接是无缝的。


例 5:用规则引擎控制音乐(不依赖 AI)

如果你不想把音频控制交给 AI(毕竟 AI 有时会忘记写指令),可以直接在规则系统里配置。

Rule 配置:

json
{
  "id": "low_hp_music",
  "name": "低血量危机音乐",
  "trigger": {
    "type": "variable-crossed",
    "variableId": "hp",
    "direction": "drops-below",
    "threshold": 20
  },
  "actions": [
    {
      "type": "play-audio",
      "trackId": "crisis_bgm",
      "action": "crossfade",
      "fadeDuration": 1.5
    }
  ],
  "priority": 20
}

hp 从 20 以上降到 20 以下时,自动用 1.5 秒的交叉淡入淡出切换到危机音乐。纯机械触发,AI 完全不需要操心。