0%

Unity音频优化实践

本文整理自10 Unity Audio Optimisation Tips - Game Dev Beginner

Unity的音频设置中有好多平时不会去了解的属性,本文只从How to而不是Why的角度整理一下可用的优化方式。

3D音效使用Force to Mono

立体声有左右两个省道,大小和内存占用都会翻倍。但是Unity中两个声道都是从同一个点发出,双声道没有意义。

最佳实践:
如果使用了双声道的音源,可以通过勾选Force to Mono轻松将双声道音源变成普通音源,节省内存开支。

使用正确的加载类型和压缩格式

关于压缩类型

  • Decompress on Load – 解压完整的数据进内存
  • Compressed in Memory – 加载进内存,使用的时候解压。用CPU性能换一部分内存。
  • Streaming – 完全不加载进内存,使用时从存储介质中串流。最省内存,消耗最多CPU。

下图为同一声音文件在默认压缩格式下使用3种不同加载类型的CPU和内存消耗:

关于压缩格式

在使用Decompress on Load时,压缩格式对内存没有影响(因为无论如何都是会解压后进内存的),但是对包体大小会有影响。使用PCM会增加包体大小,而使用Ogg Vorbis包体会更小,但是在加载进内存时需要解压缩,不过解压缩的消耗可以忽略不计。

在使用Compressed in Memory时,如果你想要通过Quality选项进一步压缩声音文件的质量和大小,使用Ogg Vorbis会更好。但是在同样100% Quality的情况下,ADPCM占用的内存比Ogg Vorbis更小,而且解压缩消耗的CPU资源比Ogg Vorbis小很多。不过,无论如何不要使用PCM,因为PCM是不压缩的格式,完全没有利用上Compressed in Memory的优势。

在使用Streaming时,和Compressed in Memory类似,使用ADPCM可以节省很多CPU资源。不同的是,可以使用PCM进一步节省CPU资源,因为PCM没有压缩。PCM唯一的缺点是会占用更多的存储资源。

MP3格式相较上述格式没有显著优势,因此不用考虑。

另外,需要注意的是,在移动端短时间内Stream多个声音文件可能会造成CPU的高负载。

最佳实践:

  • 时间短的音效使用Decompress on Load,压缩格式使用Ogg Vorbis
  • 时间较长的音效使用Compressed in Memory,压缩格式使用ADPCM
  • 音乐(如背景音)使用Streaming,压缩格式使用PCM

声音数量限制和优先级

Unity默认的声音数量限制是32个,但可以通过设置更改

Real Voice是指真正能听到的声音数量。假如限制为1,则无论什么时候都只能听到1个声音,其他声音会根据优先级依次变为Virtual Voice。

Virtual Voice会在后台继续播放,但实际听不到。当Real Voice小于设定的上限时,Virtual Voice会根据优先级变成Real Voice继续播放。如果Virtual Voice数量大于设置的上限时,会根据优先级被停止。

每个声音都可以设置优先级,0为最高,256为最低:

最佳实践:

  • 尽量保持默认设置。如果需要提高上限,手机上Max Real Voices最好低于40,高端手机低于60;主机、桌面端低于80。
  • Max Virtual Voices大于Max Real Voices
  • 大部分声音都使用一样的优先级,少数重要的声音设置高优先级

暂停不使用的声音

如果Max Real Voices设置的不高,这个方法没有必要。如果设置了较高的声音数量上限,则有必要手动暂停一些在AudioListener以外的声音。只需要给所有带声音的物体挂上如下脚本即可:

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
using System.Collections;
using System.Collections.Generic;
using UnityEngine;

public class CheckIfAudible : MonoBehaviour
{
AudioListener audioListener;
AudioSource audioSource;
float distanceFromPlayer;

void Start()
{
// Finds the Audio Listener and the Audio Source on the object
audioListener = Camera.main.GetComponent<AudioListener>();
audioSource = gameObject.GetComponent<AudioSource>();
}

void Update()
{
distanceFromPlayer = Vector3.Distance(transform.position, audioListener.transform.position);

if (distanceFromPlayer <= audioSource.maxDistance)
{
ToggleAudioSource(true);
}
else
{
ToggleAudioSource(false);
}
}

void ToggleAudioSource(bool isAudible)
{
if (!isAudible && audioSource.isPlaying)
{
audioSource.Pause();
}
else if (isAudible && !audioSource.isPlaying)
{
audioSource.Play();
}
}
}

非必须的音效勾选Load in Background

Unity Load in Background

勾选此选项后,Unity在加载场景时不会等待该声音完全加载好,可以减少加载场景的时间。下图是在加载场景时加载约90个音频和大量Prefab时,勾选和不勾选Load in Background的对比。不是很精确的实验,但是有参考作用:

Difference in load time when using Load in Background in Unity

最佳实践:
在加载一些场景初始阶段非必需的声音(如怪物死亡音效、胜利音效等)时,可以勾选此选项。

合理加载音频数据

Preload Audio Data in Unity

Unity导入音频时有个默认勾选的选项为Preload Audio Data,即在加载声音文件时,同时将声音的信息(如时长)和音频数据加载进内存。如果不勾选,则只有声音信息会被加载进内存。

因此,如果不勾选此选项,可以节省一些内存,但是需要在使用该声音时手动加载:

1
audioClip.LoadAudioData();

为了节省内存,也可以手动卸载该声音:

1
audioClip.UnloadAudioData();

最佳实践:
手动卸载不需要使用的音频数据,在需要时重新加载

禁用音频组建而不是使用静音

当音频组件还被挂在物体上时,就算使用了静音,也依然存在相关的性能开销(如计算声音和Audio Listener)之间的距离。因此,如果不是真的有“静音”这个需求,尽量禁用音频组件,搭配上一段落的卸载音频数据效果更佳。