第 12 章 Unity 里的音频:听到”位置”的声音
前面第 9、10、11 章,咱们把”声音怎么变成数字""数字怎么装进文件""视频又是怎么一回事”彻底拆明白了。你现在已经知道:一个 .ogg 文件里,本质就是第 9 章那串被采样量化出来的 PCM 数字,再按第 10 章讲的感知压缩挤掉你不敏感的部分。
但——地下有个坑,很多人学到这步会卡住:
那一串数字,是怎么”从电脑里跑出来、钻进你耳朵、而且还能让你听出’声音在左边、在身后、在远处‘“的?
注意,第 9、10 章只解决了”声音被存成数字”。它可没解决”数字怎么变成有空间感的声音”。就像你有一张高清地图(数据),但地图不会自己告诉你”我现在站在哪、北边那座山离我多远”。从”存着的数字”到”耳朵听到的位置感”,中间还隔着一整个引擎子系统。
这一章,我就带你把这层补上。你学完会发现:Unity 处理 3D 声音的思路,和它处理雾(第 8 章)、处理坐标(第 2 章)的思路,是一母所生的——都是”先把现实现象吃透,再拿几个旋钮去近似”。
12.1 三个核心组件:唱片、唱机、人耳
想象一个最朴素的”放音乐”场景。你要让一段音乐响起来,物理上缺不了三样东西:
- 一张唱片(上面刻着音轨,是”声音本身”)。
- 一台唱机(把唱片转起来、把沟槽变成声音,是”播放的机器”)。
- 一双耳朵(真正去听,是”接收的器官”)。
缺一不可。唱片不转,没声;唱机不接喇叭,没声;你没耳朵,那更没声。
Unity 把这三样,做成了三个明确定义的组件。我一个一个拆,你必须分清楚”谁是谁”。
AudioClip:那张唱片,也就是 PCM 数据本身
AudioClip(音频片段)就是”唱片”。它对应的是第 9 章你算过的那串 PCM 数字——某个声音被采样量化后,落进文件里的那一长串整数。
你在 Project 窗口里看到的 gunshot.ogg、bgm_battle.ogg,导入 Unity 后,每一个就是一个 AudioClip。它只描述”声音长什么样”,不负责播放、不负责挂在哪、不负责从哪只耳朵进。它就像一张静态的”音频照片”:采样率多少、位深多少、几声道、总共多长,这些元数据(第 10 章讲的”说明书”)都挂在它身上。
关键认知:AudioClip 是”数据”,不是”正在响的声音”。 这点特别容易混。一个 AudioClip 躺在项目里,世界静悄悄;它必须被人”播放”,才会出声。这和数据与实例的区别,和你写程序时”类的定义”和”类的实例”几乎一模一样——AudioClip 是类,播放出来的声音 是那个正在跑的实例。
另外连回第 10 章的现实账本:每个 AudioClip 都有一个 Load Type(Decompress On Load / Compressed In Memory / Streaming)。短音效选”载入即解压”,因为要零延迟随机触发;长 BGM 选”流式”,因为全塞内存太亏。所以 AudioClip 不是简单”装了段录音”,它还带着”我该怎么被读进内存”的策略。你回忆一下第 10 章那张表,这里就接上了。
AudioSource:那台唱机,挂在物体上
AudioSource(音频源)就是”唱机”。它是 Unity 里一个可以挂在游戏物体(GameObject)上的组件。它的职责是:拿一个 AudioClip,按你设的方式,把它”播出来”。
为什么强调”挂在物体上”?这是和普通音乐播放器最不一样的地方。你电脑上的播放器,声音从”扬声器”出来,没有位置概念。但游戏里,声音通常属于某个东西:枪声属于那把枪,脚步声属于那个角色,瀑布声属于那道瀑布。所以 AudioSource 是物体的一个属性——物体在世界坐标里挪到哪(第 2 章),声音就从哪发出来。
AudioSource 上你要设的核心就两件事:
- 它播哪个
AudioClip(把唱片放上去)。 - 怎么播(音量多大、循环不循环、是 2D 还是 3D,这些下面 12.4 细讲)。
打个比方:AudioClip 是 MP3 文件,AudioSource 是”正在用某台设备放这个 MP3 的那个动作”。一台唱机同时只能放一张唱片,但一个物体上也常只挂一个 AudioSource;而同一个 AudioClip(比如脚步声)可以被无数个 AudioSource 同时播——就像一张唱片可以被多台唱机各放各的。
AudioListener:那双耳朵,通常在摄像机
AudioSource 负责”出声”,那”谁在听”?AudioListener(音频监听器)就是”耳朵”。场景里必须至少有一双耳朵,声音才被送出去。Unity 默认把这双耳朵,放在**主摄像机(Main Camera)**上——这非常合理:你看到的画面,就是”你”站的位置;那”你”听到的,自然也从摄像机位置出发。你眼睛和耳朵长在同一个脑袋上,游戏里摄像机就同时是”眼+耳”。
于是整个链路清楚了:
AudioClip(唱片:PCM 数据)
↓ 被指定给
AudioSource(唱机:挂在某物体上,在世界里有个位置)
↓ 发出声音,经过"距离/方向"的计算
AudioListener(耳朵:在摄像机上,在世界里也有个位置)
↓ 最终混成你耳机/喇叭里的声音
你注意中间那道”距离/方向的计算”——这正是普通播放器没有、而游戏必须有的一步。下面两节就专讲它。
一个小陷阱先提醒:场景里可以有很多个
AudioSource(很多声源),但AudioListener通常只有一个(你只有一双耳朵)。如果你不小心把两双耳朵放进场景(比如给主角也挂一个、摄像机又有一个),声音会”打架”、定位错乱。所以记住:耳朵一般只在摄像机上有一双。
12.2 3D 音效:为什么近处响、远处轻
先回到现实,问一个你绝不会怀疑的现象:
你站边上听人敲鼓,“咚”一声震耳朵;你退到五十米外,同样一声鼓,“咚”变成闷闷的、小小的。再退到五百米,几乎听不见。为什么越远越轻?
这不是录音的问题,是物理。声音是空气中的压强波(第 9 章说的”疏密变化”),它从声源出发,像水波一样向四面荡开。波的能量摊在一圈越来越大的”球面”上——离声源越远,单位面积分到的能量越少。粗略说,强度按”距离的平方”反比衰减(点声源的理想情况)。你不用记公式,记住一句大白话就行:声音散出去,越远越稀,所以越远越弱。
问题:游戏怎么”模拟”这种远弱近强
游戏里,声源(AudioSource)和耳朵(AudioListener)都在世界坐标里有确定的位置(第 2 章)。那么播放时,引擎就该回答一个问题:
声源离耳朵多远?根据这个距离,把声音”压”到多小?
如果不做这事,会出什么洋相?你退到地图另一头,敌人的枪声照样”震耳朵”——你根本听不出敌人在远处还是贴脸,所有声音一样响。这违反直觉到离谱,游戏的”空间感”瞬间破产。
所以工程师的方案很直接:用一个”距离 → 音量”的函数,把声音按距离衰减。 离得近,原样放;离得远,乘个小于 1 的系数;远到一定程度,干脆静音。
Unity 里的旋钮:Min Distance / Max Distance / Rolloff
Unity 把这套衰减做成了 AudioSource 上的几个参数,我逐个翻译成人话:
- Min Distance(最小距离):在这个距离之内,声音按”最大音量”放,不衰减。意思是”太近了,响度封顶”。直觉:你贴着喇叭站,再近也不会更响(喇叭功率就那么大)。比如设成 1 米,那 0~1 米内声音一样响。
- Max Distance(最大距离):在这个距离之外,声音彻底静音(衰减到 0)。直觉:“远到听不见就算了,别白算”。比如设成 50 米,那 50 米外这个声源不贡献声音。
- Rolloff(衰减曲线 / 滚降):这是
Min到Max之间,音量”怎么降”的形状。它回答”从满响到静音,是匀速降,还是先慢后快,还是先快后慢”。Unity 提供Logarithmic(对数,最贴近真实空气衰减)、Linear(线性)、Custom(你手画一条曲线)。
我重点说”曲线”这件事,因为它最能体现”工程师用旋钮近似现实”。真实的空气衰减不是匀速的,是对数式的——前面几米掉得快,后面慢慢平缓。如果你选 Linear,那会是”每米均匀降一点”,听起来”远处的声音消失得太突兀”;选 Logarithmic 更自然。而 Custom 让你自己画图,比如你想做”只有 5 米内听得清、之后断崖式消失”的恐怖效果,就手动画一条陡降曲线。
你立刻能和第 8 章的雾连起来:雾是”按距离把颜色混进雾色”,音频衰减是”按距离把音量乘个系数”。两者都是’距离越大,存在感越弱’的同一类思想——只不过一个作用在你眼睛(颜色),一个作用在你耳朵(响度)。这就是”同源”的真正含义:游戏里很多效果,底层是同一个”用距离换存在感”的招。
连回第 9/10 章的一个细节:衰减是在播放时实时算的,它改的是”这串 PCM 数字被播多响”,而不是去改 PCM 数据本身。所以同一个 AudioClip,放在耳边和放在远处,文件完全没变,变的只是 AudioSource 播放它时乘的系数。这正好印证第 10 章”文件是数据、播放是另一回事”。
12.3 空间化:声音从哪边来,以及”房间有多大”
光有”远近”还不够。你闭眼也能分辨”左后方有车、右前方有人说话”。这靠的是两件事:左右方位和空间反射(混响)。Unity 用一组功能来对付,统称”空间化(Spatialization)“。
左右声道:声音从左边来,左喇叭就响
你还记得第 9 章讲的”声道”——立体声是左(L)、右(R)两路独立声音。空间化第一步,就是根据”声源在耳朵的左边还是右边”,决定左声道和右声道各放多少。
怎么判断左右?靠 AudioSource 和 AudioListener 的相对位置(又是世界坐标,第 2 章)。引擎算出”声源在耳朵的左边 30 度”,就调高左声道、调低右声道——你的大脑据此判断”声源在左”。这叫声像定位(Panning)。
注意一个巧妙点:真实世界里你判断左右,靠的是”声音到两只耳朵的时间差和音量差”(头挡住了右边来的声音,右耳略弱)。Unity 不需要真的模拟头,它直接用”相对方位 → 左右声道配比”近似,效果够骗过大脑。又是”实时约束下的聪明近似”(第 1 章那句话)。
混响:模拟”这个空间有多大、多空”
你有没有注意:同样一声拍手,在浴室里”嗡——“回荡很久,在户外旷野”啪”一下就散了。区别在反射——声音撞墙弹回来,多次叠加,形成”混响(Reverb)”。
游戏里,你希望”山洞里脚步带回音、空旷原野没有回音”。Unity 的做法是两件套:
- AudioSource 上的
Reverb Zone Mix:声源可以被”混响区”影响。 - Reverb Zone(混响区):你在场景里放一个”区域”,进入这个区域的声音会被加上特定混响。比如做一个
Cave混响区,参数设成”长尾巴、多反射”,角色一进山洞,所有声音自动带上洞穴的回响。
这背后的理念是”空间大小决定声音质感”。你想想,这和雾(第 8 章)是不是又一对兄弟?雾是”用颜色告诉你空间有多远多浑”,混响是”用回响告诉你空间有多大多空”。两者都是’用听觉/视觉线索暗示空间属性’。
Doppler:声音的”火车进站变尖、出站变闷”
最后提一个思想实验级的参数:Doppler(多普勒)。现实里,一辆鸣笛的火车朝你开过来,笛声变尖(频率升高);开过去远离你,变闷(频率降低)。原因是声源和听者相对运动,压缩/拉长了声波的”到达间隔”,等效于改变了你听到的频率(第 9 章讲的”采样/频率”反过来想)。
游戏里如果一辆赛车从你身边飙过,开着 Doppler,你会听到”嗖——“一声由尖变闷的划过感,临场感暴增。Unity 用 AudioSource 上的 Doppler Level 控制强度(0 就是关掉,声音不因运动变调)。
我得诚实告诉你:Doppler 在游戏里默认常常调得很低甚至关掉,因为一旦参数没配对,赛车声音会”哇”地一声鬼叫,反而出戏。但理解它的”为什么”很重要——它再次证明:声音不是孤立的”录音”,它是和”运动""位置”绑在一起的实时演算(回到第 1 章”游戏是演算不是回放”)。
12.4 参数直觉:四个旋钮各对付什么听感
到这,你已经把”3D 声音”的机制摸清了。最后我给你一张”旋钮 → 听感”的直觉表,都是 AudioSource 上的常用参数。你调音效时,先问”我要解决什么听感问题”,再去找对应的旋钮,别瞎拧。
- Volume(音量):最直白,这声源整体多响,0~1。它和”距离衰减”是两层——衰减是引擎按距离自动乘的系数,Volume 是你手动设的”基准响度”。比如两把枪,一把小手枪 Volume 0.3,一把大炮 Volume 1.0,即便离你一样远,大炮也响。
- Pitch(音高):播放速度/频率的缩放,1 是正常。Pitch 调高,声音变尖、变快(像把录音加快);调低,变闷、变慢。实用场景:角色受伤时把环境音 Pitch 调低,瞬间”时间变慢”的电影感;或者一个
AudioClip复用在多种怪物上,用不同 Pitch 区分大小。注意:Pitch 改的是频率,和第 9 章”频率=音高”完全呼应——你调的正是那串 PCM 被还原时的快慢。 - Spatial Blend(空间混合):这是本章最该懂的一个,取值 0(纯 2D)到 1(纯 3D)。
- 0(2D):声音没有位置,左右声道固定,远近不影响响度。适合”背景音乐 BGM”——你希望 BGM 永远一样响,不管角色跑哪去。也适合 UI 音效(按钮”叮”)。
- 1(3D):声音完全按位置算,远近、左右全生效。适合”世界里的声源”——枪声、脚步、瀑布。
- 中间值:半 3D,比如你想让一段旁白”大致从某方向来,但不随距离衰减太多”。
- 一句话判断你该设多少:这个声音属于”世界”还是属于”界面”?属于世界→往 1 靠;属于界面/音乐→往 0 靠。
- Loop(循环):播完是否从头再来。BGM、流水声、风声要开;枪声、爆炸一次性,关掉。简单但关键——忘了关 Loop,你的爆炸声会”轰轰轰”循环到天荒地老。
还有几个顺手记的:Mute(静音这路,调试用)、Play On Awake(物体一出现就播,还是等代码调用 Play())、Priority(声音太多时谁先被砍,省算力,呼应第 1 章”餐厅只做点的菜”)。
把 12.1–12.4 串成一句话:Unity 的声音系统,就是用
AudioClip(唱片/PCM 数据,第 9/10 章)+AudioSource(唱机/带位置)+AudioListener(耳朵/在摄像机)三件套,再按”距离衰减、左右声像、混响、多普勒”把那串数字实时加工成”有位置的听感”。每一个旋钮,都对准一个你现实里能体会的听感问题。
12.5 收个尾,连回整条主线
你回头看,这一章其实干了一件和第 8 章雾完全对称的事:
- 雾:现实有”空气透视” → 问题”远处看不清还要省钱” → 方案”按距离混颜色” → 落进
Fog Color / Density。 - 音频:现实有”声音随距离减弱、有方位、有空间反射” → 问题”游戏得让声音有位置感” → 方案”按距离衰减、按方位分配声道、加混响” → 落进
Min/Max Distance / Spatial Blend / Reverb。
而这一切的”原材料”,是第 9、10 章你学的:声音先被采样量化成 PCM(第 9 章),再被压缩装进 OGG/WAV 并决定载入方式(第 10 章),最后在 Unity 里由 AudioClip 引用、由 AudioSource 播放、由 AudioListener 接收。你现在可以用一句话回答第 9 章开头那个疑问”音视频怎么进计算机”的”音频半边”了:进计算机靠采样量化,进游戏靠三组件 + 空间化,全程都是’连续变离散、再用感知弱点和位置近似换实时’。
12.6 一个你马上能用的调试心法:先听”裸声”,再听”空间声”
讲了这么多旋钮,我得给你一个实战里最不容易踩坑的习惯。新手调 3D 音效,最常干的一件傻事是:把 AudioSource 一堆参数(距离、声像、混响)全设上,一播放发现”声音怪怪的”,却分不清怪在哪——是距离衰减错了,还是声像反了,还是混响太重?所有变量搅在一起,你根本定位不了病根。
我的办法朴素:拆开调,像做实验控制变量。具体说,第一步,先把声音当”裸声”听——也就是 Spatial Blend 设成 0(纯 2D),关掉所有衰减和混响,只放 AudioClip 本身,确认”这段录音本身对不对、音量基准合不合适”。这就好比你修音响,先把音源直连喇叭,确定碟没坏,再去调均衡器。很多”音效怪”的锅,其实在录音本身(第 9 章采样率太低发闷、第 10 章压缩太狠全是底噪),不在空间化。
第二步,再一步步加空间:先把 Spatial Blend 拉到 1,只开距离衰减(Min/Max Distance),听”远近对不对”;确认后再开声像(左右方位),听”左右对不对”;最后才加混响区,听”空间感对不对”。每一层单独验证,出问题立刻知道是哪层。这套”分层验证”的思路,和你写程序时”每次只改一个变量看结果”完全一致——把音频系统当成一组可独立测试的模块,而不是一团神秘黑箱。
我再补一个你一定会遇到的真实坑:同一个 AudioClip 被很多个 AudioSource 同时播(比如十个敌人同时开枪,都播同一个 gunshot),如果 Max Distance 设太大、音量都拉满,十声叠一起会”轰”地爆音,震得耳朵疼。这叫”同屏声源过多导致的响度叠加”。解决思路有两种:一是用第 1 章的”优先级”思想,给最重要的声源高优先级,远的干脆不播(可配合剔除思想,第 14 章);二是给群体音效设个总音量上限(音频混合里的限制器)。你看,连”很多声音一起响”这种问题,落回的还是你学过的”实时约束下做取舍”。
还有一个常被忽略的点:移动平台(手机)的扬声器是单声道或假立体声,你精心调的左右声像,在手机外放时玩家基本听不出。所以做手游,3D 音频的”左右”可以弱化,“前后远近”靠衰减来传达更靠谱。这说明:参数怎么设,还得看你”最终声音从什么设备出去”——这又是把知识落到现实约束的例子,和第 1 章”看运行环境定方案”一脉相承。
把这一节收进你的工具箱:调声音,先裸声后空间、分层验证、警惕叠加爆音、别忘了输出设备。这些都不是某个新概念,而是把你第 9、10、12 章学的东西,“用工程师的纪律”串起来用。
顺带连回你第 11 章学的视频,把”音频”这半边彻底收口。游戏里的过场动画,常常”画面是视频、声音是音频”两套管子一起跑——视频流(第 11 章讲的 H.264 装进 MP4 容器)负责画面,旁边的音频流就是本章的 AudioClip 在播。你学完第 11 章和第 12 章就会明白,所谓”一段过场动画”在你眼里不再是黑箱,而是”一帧帧画面 + 一串 PCM + 两者共用同一个帧时钟去同步”(这正是 13.6 要讲的视听同步)。容器里”视频流和音频流捆在一起”,在游戏过场里就是这么落地的:引擎一边解视频帧送屏幕,一边解音频帧送 AudioSource,谁也不抢谁。这种跨章打通,正是概念地图最大的价值——你不再是”学一段忘一段”,而是把第 9、10、11、12 章缝成了一条完整的话:“声音怎么变数字(9)、怎么压缩装盒(10)、视频怎么同理(11)、数字怎么在引擎里变成有位置的声音(12)”。
再补一句关于”听感是综合的”:玩家评价一个游戏声音”好不好”,很少只看单个参数,而是听”所有声音合起来的空间感对不对”。你的脚步、风、敌人、BGM,各自对的空间化设置,拼成”我真的在这个世界里”的错觉。所以调音频,最后一定要戴上耳机、进游戏、整体听一遍,而不是在 Inspector 里孤立看数值。数值对、整体不对,仍是失败。这和第 13 章”游戏手感来自多系统配比”是同一句话的音频版。
下一章,我把你前面学的”渲染(画面)“和这一章的”音频”放进同一个场景,看看当雾、摄像机、光照、声音同时开动,一台机器一帧里到底忙了些什么。
【思考题】
- 用第 9、10 章的知识说明:AudioClip 里装的”那串数字”到底是什么?它和第 10 章讲的 WAV/OGG 是什么关系?同一个 AudioClip 能被多个 AudioSource 同时播放吗,为什么?
- 第 10 章讲了音频的 Load Type(载入即解压 / 流式)。如果一个 3 分钟的 BGM 和一个 0.2 秒的枪声,分别该选哪种 Load Type?为什么这和第 1 章”餐厅”比喻是同一思路?
- 距离衰减(Min/Max Distance、Rolloff)和作用在第 8 章的雾,底层思想有什么相同?分别作用在你的哪个感官?
- 你的游戏里,BGM 和 敌人脚步声,Spatial Blend 分别该设多少?凭什么这么分?如果误把 BGM 设成 1(纯 3D),会发生什么怪事?
- Doppler(多普勒)现象在现实里对应什么?游戏里为什么常常把它调低甚至关掉?它再次印证了第 1 章的哪句话?
【参考答案】
- AudioClip 里装的就是第 9 章说的 PCM 数字——声音被采样(采样率)、量化(位深)、分声道后那一长串整数;而第 10 章的 WAV/OGG 是”这串数字的包装盒”:WAV 原样装(无损),OGG 是感知压缩后的(有损)。AudioClip 引用这个文件,带着它的元数据。同一个 AudioClip 可以被无数个 AudioSource 同时播放,因为它是”数据”,播放是”实例”——就像一张唱片能被多台唱机各放各的,文件本身不被消耗。
- 3 分钟 BGM 选
Streaming(流式):全解压进内存太亏,边读硬盘边播省内存。0.2 秒枪声选Decompress On Load(载入即解压):短、要零延迟随机触发,常驻内存无妨。这和第 1 章”餐厅”是同一思路——贵的步骤(解压长音频)用硬盘 IO 换内存,便宜又频繁的步骤(短音效)提前备好换实时性。本质都是”提前做掉贵的、运行时拼便宜的”。 - 相同思想是**“距离越大,存在感越弱”**:雾按距离把物体色混进雾色(越远越看不见),音频按距离把音量乘衰减系数(越远越听不清)。都用一个”距离 → 强度”的函数实时近似现实。差别只在一个作用在你眼睛(视觉/颜色),一个作用在你耳朵(听觉/响度)。这就是”同源异感”。
- BGM 的 Spatial Blend 应设 0(纯 2D),因为它是”界面/氛围音乐”,希望永远一样响、不随角色位置变;敌人脚步声设 1(纯 3D),因为它属于”世界里的声源”,要随距离和方位变化让你判断敌人方位。若误把 BGM 设成 1:你走到地图边缘,背景音乐会越变越小甚至消失,像”音乐也住在某个坐标上”,极其出戏。判断标准一句话:属于世界→3D,属于界面/音乐→2D。
- 现实里的多普勒:声源朝你运动,你听到的频率升高(变尖);远离则降低(变闷),如火车鸣笛掠过。游戏里常调低/关掉,是因为参数没配对时赛车声会”鬼叫”般失真出戏。它再次印证第 1 章”游戏是当场实时演算、不是回放”——声音的频率是播放时按相对运动实时算出来的,不是录音里固定好的。