中EN
概念页

游戏音频功能与角色对话

Game-Specific Audio Features & Character Dialog

在 3D 音频渲染管线之上,游戏还要实现分屏、物理驱动音频、动态音乐、角色对话、声音合成和人群声音等专门功能。GEA 重点讲了分屏的多听者问题、顽皮狗《最后生还者》的角色对话系统(逻辑台词、随机不重复、优先级与打断、把对话作为一等实体、排他性、基于规则和事实字典的分支对话、按区域标签的情境台词、对话手势)以及音乐系统(播放列表、紧张度、插入乐、过渡)(GEA4 卷 II 15.6)。

常见的游戏专属音频功能

(卷 II 15.6)

  • 分屏支持:多个听者共享一套扬声器;
  • 物理驱动音频:碎片、可破坏物、布娃娃的撞击、滑动、滚动、破碎声;
  • 动态音乐:随剧情情绪和紧张度实时变化;
  • 角色对话:AI 角色彼此交谈、和玩家交谈时显得更真实;
  • 声音合成:用正弦、方波、锯齿波等合成;更高级的有乐器合成、基于物理的声音合成(利用物理引擎的接触、动量、力、力矩、形变信息和材质、形状合成撞击、滑动、弯折声)、载具引擎合成、发声道模型语音合成(如免费工具 VocalTractLab)。GEA 指出《神秘海域》三部曲的载具追逐用了动态引擎建模,但严格说不是合成器,而是在多段预录声音之间交叉淡化;
  • 人群建模:不能简单叠加大量人声,通常分成背景氛围层加个别发声。

分屏

多个听者在世界里,扬声器只有一套。对每个听者各平移一次再平均混合,结果并不总是合理:A 站在爆炸旁、B 离得很远,玩 B 的人照样听到很响的爆炸。没有完美解,只能混合:部分声音"物理正确",部分声音"糊弄"成最合理的体验(卷 II 15.6.1)。

为什么对话系统复杂

对话传达玩法信息、讲故事、建立情感,还决定玩家对 AI 智能的感知。Bungie 在 GDC 2002 的 Halo 演讲里提到:精英被杀后咕噜兽会逃跑,但测试玩家一直没看懂原因,直到给咕噜兽加了一句类似"头儿死了,快跑!"的台词(卷 II 15.6.2)。系统复杂的原因(卷 II 15.6.2.1):

  • 要编目所有可能的台词并给每条唯一 id;
  • 每个可识别的角色声音要一致,例如《最后生还者》匹兹堡段的猎人分配到 8 种声音之一,同一场战斗里没有两个猎人声音相同;
  • 事先不知道哪个角色会说某句,所以同一句要由多位配音演员录多遍;
  • 需要大量变体,从中随机选;
  • 语音资产时长长、占内存,很多过场台词全游戏只说一次,所以通常流式播放;
  • 角色的"用力声"(搬重物、跳跃、挨打)要和说话声一致,一般也由对话系统处理。

逻辑台词

大多数对话系统在"请求说话"和"选哪个音频片段"之间加一层间接:程序或策划请求逻辑台词,用字符串或更好的哈希字符串 id(见 字符串与唯一标识符)标识;音效设计师给每条逻辑台词填入一个或多个片段,提供不同声音和说法(卷 II 15.6.2.2)。顽皮狗的音效设计师用 Scheme 的自定义语法定义,结构大致如下(GEA 示例的简化):

(define-dialog-line 'line-out-of-ammo
  (character 'drake (lines drk-out-of-ammo-01 drk-out-of-ammo-02 drk-out-of-ammo-03))
  (character 'elena (lines eln-out-of-ammo-01 eln-out-of-ammo-02))
  (character 'pirate-a (lines pira-out-of-ammo-01 pira-out-of-ammo-02))
  ;; ... pirate-h
)

更好的做法是按角色(并按关卡)拆分成多个文件:设计师互不冲突,内存也能按需加载,例如某段没有海盗就不加载海盗台词。

播放台词

系统按角色的声音 id 查表,再从候选里随机挑一个(卷 II 15.6.2.3)。避免重复:把候选索引放进数组随机打乱,按顺序依次使用;用完后重新打乱,并确保最近播放的那句不会排到新序列的第一位。GEA 说这样能"杜绝所有重复",准确地说是保证在一轮用完之前不重复、跨轮时也不会连播同一句,同时听起来随机(随机数见 伪随机数生成)。

API 要简单到"发出即忘":如果 AI 程序员或策划要绕很多弯才能让角色说话,角色会出奇地沉默。《神秘海域 3》的 C++ 代码在 AI 决策逻辑里直接调用 Npc::PlayDialog():

void Skill::OnEvent(const Event& evt)
{
    Npc* pNpc = GetSelf();
    switch (evt.GetMessage())
    {
    case SID("player-seen"):
        pNpc->PlayDialog(SID("line-player-seen")); // 说一句台词
        pNpc->MoveTo(GetClosestCover());           // 然后找最近的掩体
        break;
    // ...
    }
}

优先级与打断

角色正在说话又被要求说话,或同一帧收到多条请求,用优先级裁决:比较当前台词和本帧请求中的最高优先级,当前的赢就继续,请求的更高(或角色没在说话)就播放新台词,必要时打断(卷 II 15.6.2.4)。打断本身不好做:对同一个人的语音做交叉淡化听起来很怪;理想是先播一个喉塞音,甚至一句表示惊讶或不满的短语。《最后生还者》并没做这些,只是停掉当前台词立即播放新的,大多数时候效果不错。

对话、对话打断与排他性

对话 (conversation) 由逻辑片段 (segment) 组成,每段对应一个角色说的一条逻辑台词,片段用唯一 id 通过 :next-seg 串起来。《神秘海域 4》和《失落的遗产》里,吉普车上的对话可以被打断(如玩家下车探索),回到车上后从断处继续(卷 II 15.6.2.5)。

逐角色应用优先级会出问题:A 问完话等 B 回答时,A "技术上没在说话",一条无关台词就能插进来("嘿,找到什么了吗?""没有,我找了一个小时……""看,有个亮东西!""……什么也没找到。")。《最后生还者》因此把对话作为一等实体:对话开始时系统知道所有参与者都在对话中,即使没轮到他们说;优先级按整个对话比较。同级或更低的插话被拒绝;更高优先级的("小心,他拿枪对着我们!")则打断整个对话的所有参与者(卷 II 15.6.2.6)。

排他性 (exclusivity):每条台词或对话标记为三种之一(卷 II 15.6.2.7):

级别行为例子
非排他可以与其他台词重叠两个搜索中的猎人各自嘟囔,互不交谈
阵营排他打断本阵营所有台词猎人喊"他在这儿!",其他猎人立刻闭嘴;但 Ellie 对 Joel 的耳语不被打断
全局排他跨阵营打断所有台词所有听得到的角色都应该有反应的情况

规则、分支与事实字典

编剧和音效设计师不仅想控制说什么,还想控制走哪条分支的逻辑条件,而不必经过程序员。顽皮狗的系统部分受 Valve 的启发(Elan Ruskin,GDC 2012《Rule Databases for Contextual Dialog and Game Logic》),两者细节不同但核心相似(卷 II 15.6.2.8)。

  • 每个片段可以有多个备选,每个备选带一条规则;规则为真时选中。
  • 规则由一个或多个判据 (criterion) 组成,多个判据之间是逻辑与;空规则相当于"否则"。例:生命值 < 25 说"流血了……需要医生",< 75 说"麻烦大了",否则说"好险"。
  • 分支对话:Ellie 问"你没事吧?",若 Joel 最近没中弹则回答"没事",Ellie 接"把头低下!";若中弹则回答"(喘气) 不太好",Ellie 接"你在流血!"。
  • 说话者与听者:片段的 :next-speaker 'listener 让当前听者成为下一段的说话者,角色来回切换;判据里 ('speaker 'shot-recently) 总是指当前说话者。对话定义与具体角色无关,同一份定义也能用于 Joel 问 Ellie;对敌人尤其必要,因为战斗闲聊是动态选两个角色触发的,事先不知道是谁。系统最多支持三名听者,但绝大多数对话只有两人。

    原书问题(前后不一致):同一段先说说话者/听者机制"可扩展到两人或三人对话",紧接着又说"最多支持三名听者"(加上说话者就是四人)。两句无法同时成立,原文没有说明以哪句为准(见卷 II 15.6.2.8 "Speaker and Listener" 末段)。

  • 事实字典 (fact dictionary):判据中的 'health、'player-death-count 等符号量存在键值表里,值是变体 (variant):像 C/C++ union 能存多种类型,但还记录当前类型,便于校验和转换(如把整数 42 取成浮点 42.0f)。每个角色、每个阵营各有一个字典,另有一个全局单例字典(游戏时长、当前关卡、某任务重试次数等)。
  • 判据语法:可以显式指定字典,如 (('self 'health) > 5)、(('global 'seconds-playing) <= 23.5);不指定时按"角色 → 所属阵营 → 全局"的顺序查找,写起来简短,代价是清晰度降低。

下图是 GEA 描述的片段选择过程:对当前片段的各备选按顺序求规则,选中第一个为真的(GEA 示例里规则按从严到宽排列,空规则放最后作为"否则";"按顺序取第一个"是从示例推断的)。

flowchart TD
  S["当前片段"] --> A1{"备选 1 规则: 全部判据为真?"}
  A1 -->|"是"| P1["播放备选 1 台词"]
  A1 -->|"否"| A2{"备选 2 规则"}
  A2 -->|"是"| P2["播放备选 2 台词"]
  A2 -->|"否"| E["空规则备选 = 否则"]
  P1 --> N["按 :next-seg / :next-speaker 进入下一段"]
  P2 --> N
  E --> N

情境对话

敌人要能"聪明地"报出玩家位置:"他在商店里!""他在那辆车后面!"。做法:音效设计师在世界里标注区域,每个区域带具体标签("柜台后""收银机旁")或一般标签("商店里""街上")。若玩家和 NPC 在同一个一般区域,用玩家的具体标签选台词("他在窗户边!");不在同一一般区域时退回玩家的一般标签("他在街上!抓住他!")。系统很简单,难在要录和配置的组合数量巨大,但游戏内效果值得(卷 II 15.6.2.9)。

对话动作

没有肢体语言的台词显得怪异。有些台词属于全身动画(如过场),但很多要在走、跑、开枪时说。《最后生还者》用叠加动画实现手势系统(见 动画混合),可以由 C++ 或脚本显式调用;每条台词还可以关联一段与音频时间线同步的脚本,在关键台词的精确时刻触发手势(卷 II 15.6.2.10)。

音乐系统

音乐系统的职责(卷 II 15.6.3):音乐几乎都太大放不进内存,要流式播放;提供变化;匹配游戏事件;在曲目间无缝过渡;与其他声音合理混合;可以被临时闪避以突出特定声音或对话;允许插入乐 (stinger) 临时打断或叠在当前曲目上;可以暂停和重新开始(不需要每一秒都有管弦乐)。

  • 按情绪/紧张度的播放列表:每个列表对应一种紧张度或情绪,含一首或多首曲子,随机或顺序选择;战斗开始结束、动人过场进出时切换列表。有的游戏用一个紧张度递增的"栈":无敌人时平静、接近未察觉的敌人时紧张、首次接触时惊悚、战斗中快节奏。
  • 插入乐:短的音乐或音效片段,临时打断当前曲目,或在压低主曲音量时叠加播放,例如首次看到新敌人时一声不祥的"隆隆",玩家死亡时快速切到一小段"死亡音乐"。
  • 过渡:不能盲目交叉淡化两首无关的曲子,速度和节拍可能对不上。关键是把握过渡时机:速度不匹配时快速淡化,速度接近时可以长淡化;需要反复试验,连让一首曲子循环得好都需要音效工程师调整。

与 UE5 的对应

UE5 的 Quartz 提供与节拍对齐、精确到采样的调度(可用于在小节或拍上切换音乐),见 音频系统与 MetaSounds。"规则 + 事实"的选择思路与 AI 的决策结构可以对照 行为树与 StateTree(这是对比归纳)。

  1. Game_Engine_Architecture_4th_Edtion_Volume_2_-_Jason_Gregory.pdf
  2. Jason Gregory《Game Engine Architecture》第 4 版(2026)卷 II 第 15 章 15.6 节

相关页面

游戏开发音频引擎架构Audio Engine Architecture音频渲染软件和多数系统一样是分层的"栈":硬件 → 驱动 → 底层 API → 3D 音频引擎,功能集通常模仿录音棚的多通道调音台。每个 2D/3D 声音是一条语音 (voice),沿语音总线经过编解码、增益、预发送滤波、辅助发送分出干湿两路、混响、后发送滤波和声像,然后由主混音器合成多声道信号,再经主输出总线(预放、LFE 生成、EQ、压缩、主音量)送到扬声器。资产上分片段、音效提示 (cue)、声音库和流式声音;混音靠分组、闪避、总线预设与混音快照、实例限制和语音抢占(GEA4 卷 II 15.5)。游戏开发三维音频渲染3D Audio Rendering3D 音频渲染引擎要尽可能可信地重现"玩家若真在游戏世界里会听到什么"。它的输入是世界里大量的 3D 声音,输出是少数几路扬声器声道;主要任务是合成、空间化(距离衰减 + 声像定位)、声学建模(混响、遮挡、衍射)、多普勒和混音。GEA 给出了恒功率声像律、聚焦角、混响区域交叉混合、遮挡/阻挡/排除的分析方法,以及顽皮狗《最后生还者》的随机传播建模和声音门户(GEA4 卷 II 15.4)。游戏开发字符串与唯一标识符Strings & Unique Identifiers字符串在 C/C++ 中只是字符数组,变长意味着要么硬编码长度上限、要么动态分配;比较和复制都是 O(n)。引擎内部大量用字符串做资源名和对象 ID,处理不当会严重拖慢游戏。GEA 的建议:运行时代码尽量避免字符串类,若用则了解其成本并按引用传递;路径类是值得专门做的例外;对象与资产的唯一标识用哈希字符串 ID(string id),兼得字符串的可读性与整数的比较速度。游戏开发动画混合Animation Blending动画混合把两个或多个输入姿势合成一个输出姿势。基本工具是对每个关节的局部 SRT 分别做 lerp(旋转用 nlerp 或 slerp);在此之上有时间插值、交叉淡入淡出、方向移动、一维/二维混合空间、部分骨骼混合和叠加混合(差分片段)。前几种都是加权平均,叠加混合则是"加上一段变化"(GEA4 卷 II 13.6)。游戏数学伪随机数生成Pseudorandom Number Generation游戏引擎到处要用随机数,但随机数生成器 (RNG) 并不真正随机:它只是给出一个复杂但完全确定、预先定义的序列,因此严格说应叫伪随机数生成器 (PRNG)。好坏取决于周期(多久重复)和能否通过各种公认的随机性测试。GEA 介绍了线性同余、梅森旋转、Marsaglia 的 Mother-of-All / Xorshift / KISS99,以及 PCG。游戏开发 · UE5行为树与 StateTreeBehavior Tree & StateTreeUE 的 AI 决策有两套工具:行为树 (Behavior Tree) + 黑板 (Blackboard) 是事件驱动的,条件写成装饰器 (Decorator),用 Simple Parallel、Services 和 Observer Aborts 代替传统并行节点;StateTree 是结合了行为树选择器和状态机转换的通用分层状态机,只在转换触发时按需选择状态,激活路径上的任务并发运行,通过属性绑定传递数据,也是 Mass 的行为层。优先级抢占多的战斗 AI 适合行为树,状态显式、分层或大规模的场景适合 StateTree。游戏开发 · UE5音频系统与 MetaSoundsAudio System & MetaSoundsUE5 的音频资产分三层:音源 (Sound Source) 负责产生声音,UE5 主推 MetaSounds,它是一张由设计师搭建的数字信号处理 (DSP) 图,能精确到单个采样;分组与混音用 Sound Class 和 Sound Class Mix 批量调整音量和音高;空间与资源由 Sound Attenuation(随距离衰减和空间化)和 Sound Concurrency(限制同时播放数量)控制。需要和节拍对齐的玩法可以用 Quartz 做精确到采样的调度。游戏策划游戏角色与化身Game Characters & Avatars世界里住着角色。与小说、电影相比,游戏角色呈现三种倾向:心理 → 身体、现实 → 幻想、复杂 → 简单——这是媒介决定的"容易的路",但并非注定。玩家操控的角色称为化身 (avatar),最适合投射的化身是理想形象与白板。塑造其他角色的十条建议:列出角色功能、定义并运用特质、人际环形模型、角色关系网、地位、声音、面孔、角色转变、让角色带来惊喜、避开恐怖谷(第 3 版第 20 章 Worlds Contain Characters;第 1 版为第 18 章,九条建议,无"让角色带来惊喜")。游戏策划游戏音乐与音效设计Game Music & Sound DesignRogers 从设计师的角度讲音乐与音效:尽早规划(迪士尼"星际旅行"去掉 John Williams 的配乐就不像《星球大战》了);选择授权音乐或原创音乐;学会"说音乐人的话"并用临时音轨与作曲沟通;按关卡列出音乐需求,考虑动态配乐与主导动机;音乐永远要比画面上的动作更激动人心;按角色招式列音效清单,区分写实与卡通、局部/远距/优先三类音效;把声音做成玩法时一定要配视觉(Level Up! 第 3 版 Level 17 Some Notes on Music,第 463–476 页;第 1 版为 Level 15)。实现层面见 游戏音频功能与角色对话。游戏策划游戏叙事Game Story故事与玩法曾被认为规则迥异:故事是单线、个人享受的,游戏是多结局、群体享受的;单人电脑游戏打破了这一划分,形成类似"波粒二象性"的故事/游戏二象性。Schell 认为我们关心的既不是故事也不是游戏,而是体验;故事和游戏都是生成体验的机器。现实中 99% 的游戏只用两种方法:珍珠串与故事机器。"完美分支互动叙事"之梦面临五个难题,而解法是把梦从"故事"换成"体验"。本章另有十一条叙事建议,第一条是新增的故事栈 (story stack):幻想 → 动作 → 经济 → 世界 → 故事,从最不灵活到最灵活(第 3 版第 17 章 One Kind of Experience Is the Story

输入关键词开始搜索

滚轮或双指缩放 · 拖动平移 · 双击放大 / 还原