UFL6-3——音效管理模块优化

优化频繁创建删除音效组件

目前我们音效管理器中的音效组件会频繁的创建和删除
这样会产生大量的内存垃圾,并且频繁创建对象也会带来性能消耗
因此我们可以利用缓存池对其进行优化

按照之前实现的缓存池要求,在场景上创建一个挂载音效源的对象,然后挂载一个poolObj​脚本,然后设置最大存在数量maxNum

image

因此,我们不再需要一个专门挂载音效源的对象,而是转为使用缓存池创建挂载音效源的对象

播放音效时,从缓存池内取出音效对象,停止播放音效(因为该音效可能正在播放其他音效),设置音效切片
停止播放或者帧更新检测播放停止时,清空关联的音效切片,从管理列表内移除,将音效对象压回对象池内,

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
44
45
46
47
48
49
50
51
52
53
54
55
56
57
58
//管理正在播放的音效
private List<AudioSource> soundList = new List<AudioSource>();

private MusicManager()
{
//固定每一小段时间检测是否有音效播放完毕
MonoManager.Instance.AddFixedUpdateListener(Update);
}

private void Update()
{
if (!soundIsPlay)
return;
//逆向遍历容器,检测是否有音效播放完毕了,如果播放完毕就销毁该组件并移除
for (int i = soundList.Count - 1; i >= 0; i--)
{
if (!soundList[i].isPlaying)
{
//音效播放完毕,不再使用,将该音效切片置空
soundList[i].clip = null;
PoolManager.Instance.PushObj(soundList[i].gameObject);
soundList.RemoveAt(i);
}
}
}

public void PlaySound(string name, bool isLoop = false, bool isSync = false, UnityAction<AudioSource> callBack = null)
{
//加载音效资源,进行播放
ABResManager.Instance.LoadResAsync<AudioClip>("sound", name, (clip) =>
{
//从缓存池中,取出音效对象,得到对应组件
AudioSource source = PoolManager.Instance.GetObj("Sound/soundObj").GetComponent<AudioSource>();
//由于取出的音效有可能是之前正在使用的,因此不管什么情况先停止播放
source.Stop();
source.clip = clip;
source.loop = isLoop;
source.volume = soundVolume;
source.Play();
//由于从缓存池内取出的容器是已经存在于列表内的,因此需要判断其是否存在与列表
if (!soundList.Contains(source))
soundList.Add(source);
//传递给外部使用
callBack?.Invoke(source);
}, isSync);
}

public void StopSound(AudioSource source)
{
if (soundList.Contains(source))
{
source.Stop();
soundList.Remove(source);
//音效播放完毕,不再使用,将该音效切片置空,将其依附的对象压入缓存池中
source.clip = null;
PoolManager.Instance.PushObj(source.gameObject);
}
}

优化3D音效问题

由于我们使用了缓存池,因此AudioSource​依附的将会是一个个独立的游戏对象,而3D音效主要考虑的问题是:

  1. 音效依附在对象上跟随对象移动
  2. 音效中3D相关参数的设置

如果你想要使用3D音效,那么只需要获取到音效组件和它依附的对象就可以迎刃而解了

  1. 获取音效组件依附的对象,改变它的父对象,设置它的位置,音效便可以跟随对象移动
  2. 获取音效组件改变其中的3D音效相关参数即可完成相关设置
1
2
3
4
5
6
7
8
9
10
11
if (GUILayout.Button("播放音效"))
{
MusicManager.Instance.PlaySound("sound_1", false, false, (source) =>
{
GameObject soundObj = source.gameObject;
source.gameObject.transform.SetParent(soundObj.transform);
source.spatialBlend = 1f; //变为3D音效
//设置3D音效相关参数
//...
});
}

提供清除所有音效的方法

目前过场景时,音效相关对象会被自动的删除,但是音效管理器中我们的容器还占着引用,我们应该提供方法清空容器
值得一提的是,该方法必须要在清空缓存池前去调用

1
2
3
4
5
6
7
8
9
10
11
12
13
14
/// <summary>
/// 清空音效相关记录,必须在清空缓存池之前去调用!!!
/// </summary>
public void ClearSound()
{
for (int i = 0; i < soundList.Count; i++)
{
soundList[i].Stop();
soundList[i].clip = null;
PoolManager.Instance.PushObj(soundList[i].gameObject);
}
//清空音效列表
soundList.Clear();
}

具体代码