文章目录提示:文章写完后,目录可以自动生成,如何生成可参考右边的帮助文档
- 前言
- 一、Resource文件夹下的音效片段目录
- 二、采用单例模式作为全局访问方式
- 三、音效类型分类
- 四、播放音效的组件池的数据对象
- 五、播放时的参数封装
- 六、音效播放管理器
- 七、测试功能脚本
- 八、测试场景截图
- 总结
前言
提示:这里可以添加本文要记录的大概内容:
一个基于对象池优化思想的音频管理系统,实现了游戏中各种2d和3d不同音效类型播放的需求,包括音效的播放,暂停,继续,停止,播放完成的回调执行等,同时也实现了对音效的开关控制,音量大小控制,以及静音状态的保存。另外采用对象池的优化思想,能够及时对不用的音效组件进行回收利用。
提示:以下是本篇文章正文内容,下面案例可供参考
一、Resource文件夹下的音效片段目录 二、采用单例模式作为全局访问方式using UnityEngine; ///三、音效类型分类/// 依赖于Unity Mono的单例,其需要和Unity的一些物体做交互。例如对象池模块和音频管理模块。 /// public class MonoSingleton: MonoBehaviour where T : Component { private static T _instance = null; public static T Instance { get { if (_instance == null) { _instance = FindObjectOfType (); if (_instance == null) { GameObject obj = new GameObject(typeof(T).Name, new[] { typeof(T) }); DontDestroyOnLoad(obj); _instance = obj.GetComponent (); (_instance as IInitable)?.Init(); } else { Debug.LogWarning("Instance is already exist!"); } } return _instance; } } /// /// 继承Mono单例的类如果写了Awake方法,需要在Awake方法最开始的地方调用一次base.Awake(),来给_instance赋值 /// public void Awake() { _instance = this as T; DontDestroyOnLoad(this); } }
////// 音效类型 /// public enum SoundType { BGM, //背景音乐 UI, //背景音乐 Effect,//效果音 Talk //剧情对话 }
四、播放音效的组件池的数据对象
///五、播放时的参数封装/// 音效组件池数据 /// [Serializable] public class ASPoolData { [SerializeField] private SoundType _type_; [SerializeField] private AudioSource _audioSource_; [SerializeField] private bool _isUseing; //是否在使用中(暂停中属于使用中,因为考虑到还可以继续播放) [SerializeField] private bool _isInPause;//是否在暂停中 public SoundType Type_ { get => _type_; set => _type_ = value; } public AudioSource AudioSource_ { get => _audioSource_; set => _audioSource_ = value; } public bool IsUseing { get => _isUseing; set => _isUseing = value; } public bool IsInPause { get => _isInPause; set => _isInPause = value; } public ASPoolData(SoundType _type, AudioSource _AS, bool _isUsed, bool _isInPause) { this.Type_ = _type; this.AudioSource_ = _AS; this.IsUseing = _isUsed; this.IsInPause = _isInPause; } }
///六、音效播放管理器/// 播放时的参数封装 /// public class PlayParams { public SoundType type; public string clipName; public bool isLoop; public Action endCallback;//播完的回调 //3d音效 public bool is3d = false; public Vector3 pos = Vector3.zero; //2d音效 public PlayParams(SoundType type, string name, bool isLoop = false, Action cb = null) { this.type = type; this.clipName = name; this.isLoop = isLoop; this.endCallback = cb; } //3d音效 public PlayParams(SoundType type, string name, bool is3d, Vector3 pos, bool isLoop = false, Action cb = null) { this.type = type; this.clipName = name; this.isLoop = isLoop; this.endCallback = cb; this.is3d = is3d; this.pos = pos; } }
using System.Collections.Generic; using UnityEngine; using System; using System.Collections; ///七、测试功能脚本/// 音效播放管理器 /// BGM不会同时播放多个,不需要使用Audiosource对象池 /// public class SoundManager : MonoSingleton{ /// /// 音效资源路径 /// private static readonly DictionarysoundPathDict = new Dictionary (); /// /// 音效播放音量大小 /// private static readonly DictionarysoundVolumeDict = new Dictionary (); /// /// 音效的静音状态 /// private static readonly DictionarysoundMuteStateDict = new Dictionary (); /// /// 记录声音效片段 /// private static DictionarysoundClipDict = new Dictionary (); private AudioSource _BGMAuidoSource; //背景音乐播放组件单独使用(不需要入池) public int poolCount_ = 10; //AudioSource对象池数量 public List _ASPoolList = new List (); //AudioSource对象池 private AudioListener audioListener; public AudioListener AudioListener { get { if (audioListener != null) return audioListener; audioListener = this.GetComponent (); if (audioListener != null) return audioListener; audioListener = this.gameObject.AddComponent (); return audioListener; } } private new void Awake() { base.Awake(); Init(); } /// /// 初始化 /// public void Init() { soundPathDict.Add(SoundType.BGM, "Sounds/BGM"); soundPathDict.Add(SoundType.UI, "Sounds/UI"); soundPathDict.Add(SoundType.Effect, "Sounds/Effect"); soundPathDict.Add(SoundType.Talk, "Sounds/Talk"); soundVolumeDict.Add(SoundType.BGM, 1); soundVolumeDict.Add(SoundType.UI, 1); soundVolumeDict.Add(SoundType.Effect, 1); soundVolumeDict.Add(SoundType.Talk, 1); _BGMAuidoSource = gameObject.AddComponent(); Init_MuteState(); Init_VolumeState(); } /// /// 初始化静音状态 /// private void Init_MuteState() { bool _BGMIsMute = false; bool _EffectIsMute = false; bool _UIIsMute = false; bool _TalkIsMute = false; if (PlayerPrefs.HasKey("BGMIsMute")) { int v = PlayerPrefs.GetInt("BGMIsMute"); _BGMIsMute = v == 1; } if (PlayerPrefs.HasKey("EffectIsMute")) { int v = PlayerPrefs.GetInt("EffectIsMute"); _EffectIsMute = v == 1; } if (PlayerPrefs.HasKey("UIIsMute")) { int v = PlayerPrefs.GetInt("UIIsMute"); _UIIsMute = v == 1; } if (PlayerPrefs.HasKey("TalkIsMute")) { int v = PlayerPrefs.GetInt("TalkIsMute"); _TalkIsMute = v == 1; } soundMuteStateDict[SoundType.BGM] = _BGMIsMute; soundMuteStateDict[SoundType.Effect] = _EffectIsMute; soundMuteStateDict[SoundType.UI] = _UIIsMute; soundMuteStateDict[SoundType.Talk] = _TalkIsMute; if(_BGMAuidoSource != null) _BGMAuidoSource.mute = soundMuteStateDict[SoundType.BGM]; foreach (var data in _ASPoolList) { if (data.Type_ == SoundType.Effect) data.AudioSource_.mute = soundMuteStateDict[SoundType.Effect]; else if(data.Type_ == SoundType.Talk) data.AudioSource_.mute = soundMuteStateDict[SoundType.Talk]; else if (data.Type_ == SoundType.UI) data.AudioSource_.mute = soundMuteStateDict[SoundType.UI]; } } ////// 初始化音效的音量状态 /// private void Init_VolumeState() { float _BGMVolume = 1; float _EffectVolume = 1; float _UIVolume = 1; float _TalkVolume = 1; if (PlayerPrefs.HasKey("BGMVolume")) _BGMVolume = PlayerPrefs.GetFloat("BGMVolume"); if (PlayerPrefs.HasKey("EffectVolume")) _EffectVolume = PlayerPrefs.GetInt("EffectVolume"); if (PlayerPrefs.HasKey("UIVolume")) _UIVolume = PlayerPrefs.GetInt("UIVolume"); if (PlayerPrefs.HasKey("TalkVolume")) _TalkVolume = PlayerPrefs.GetInt("TalkVolume"); soundVolumeDict[SoundType.BGM] = _BGMVolume; soundVolumeDict[SoundType.Effect] = _EffectVolume; soundVolumeDict[SoundType.UI] = _UIVolume; soundVolumeDict[SoundType.Talk] = _TalkVolume; if (_BGMAuidoSource != null) _BGMAuidoSource.volume = soundVolumeDict[SoundType.BGM]; foreach (var data in _ASPoolList) { if (data.Type_ == SoundType.Effect) data.AudioSource_.volume = soundVolumeDict[SoundType.Effect]; else if (data.Type_ == SoundType.Talk) data.AudioSource_.volume = soundVolumeDict[SoundType.Talk]; else if (data.Type_ == SoundType.UI) data.AudioSource_.volume = soundVolumeDict[SoundType.UI]; } } ////// 播放音效 /// public void Play(PlayParams playParams) { AudioClip clip = GetAudioClip(playParams.type, playParams.clipName); if (clip == null) return; AudioSource audioSource = GetAudioSource(playParams.type, out ASPoolData asDate); if (audioSource == null) { Debug.LogError(playParams.type.ToString() + " 类型音频播放组件没找到"); return; } audioSource.clip = clip; audioSource.clip.LoadAudioData(); audioSource.loop = playParams.isLoop; soundMuteStateDict.TryGetValue(playParams.type, out bool isMute); audioSource.mute = isMute; soundVolumeDict.TryGetValue(playParams.type, out float volume); audioSource.volume = volume; audioSource.Play(); if (playParams.type != SoundType.BGM) StartCoroutine(WaitPlayEnd(asDate, playParams.endCallback)); if (!playParams.is3d) return; AudioSource.PlayClipAtPoint(clip, playParams.pos); } ////// 切换音效静音开关 /// public void SetSwitch(SoundType type) { bool isMute = false; int value = 1; if (type == SoundType.BGM) { isMute = soundMuteStateDict[SoundType.BGM]; soundMuteStateDict[SoundType.BGM] = !isMute; _BGMAuidoSource.mute = soundMuteStateDict[SoundType.BGM]; value = soundMuteStateDict[SoundType.BGM] ? 1 : 0; PlayerPrefs.SetInt(type.ToString() + "IsMute", value); return; } isMute = soundMuteStateDict[type]; soundMuteStateDict[type] = !isMute; value = soundMuteStateDict[type] ? 1 : 0; foreach (var data in _ASPoolList) { if (data.Type_ == type) data.AudioSource_.mute = soundMuteStateDict[type]; } PlayerPrefs.SetInt(type.ToString() + "IsMute", value); } ////// 更改音效音量 /// public void SetVolume(SoundType type, float volume) { if (type == SoundType.BGM) { _BGMAuidoSource.volume = volume; PlayerPrefs.SetFloat(type.ToString() + "Volume", volume); return; } else { foreach (var data in _ASPoolList) { if (data.Type_ == type) data.AudioSource_.volume = volume; } } soundVolumeDict[type] = volume; PlayerPrefs.SetFloat(type.ToString() + "Volume", volume); } ////// 停止播放某个音效(会回收), /// 所有同名的音效都会被停止 /// /// public void Stop(SoundType type, string clipName = "") { if (type == SoundType.BGM) { if (!_BGMAuidoSource.isPlaying) return; _BGMAuidoSource.Stop(); return; } foreach (var data in _ASPoolList) { if (data.Type_ != type) break; if (!data.IsUseing) break; if (data.AudioSource_.clip.name != clipName) break; data.AudioSource_.Stop(); if (_ASPoolList.Contains(data)) _ASPoolList.Find((date) => date == data).IsUseing = false; } } ////// 暂停播放某个音效 /// 所有同名的音效都会被暂停 /// /// public void Pause(SoundType type, string clipName = "") { if (type == SoundType.BGM) { if (!_BGMAuidoSource.isPlaying) return; _BGMAuidoSource.Pause(); return; } foreach (var data in _ASPoolList) { if (data.Type_ != type) break; if (!data.IsUseing) break; if (data.AudioSource_.clip.name != clipName) break; data.IsInPause = true; data.AudioSource_.Pause(); } } ////// 继续播放某个音效, /// 所有同名的音效都会被继续播放 /// /// public void Resume(SoundType type, string clipName = "") { if (type == SoundType.BGM) { _BGMAuidoSource.UnPause(); return; } foreach (var data in _ASPoolList) { if (data.Type_ != type) break; if (!data.IsUseing) break; if (data.AudioSource_.clip.name != clipName) break; data.IsInPause = false; data.AudioSource_.UnPause(); } } ////// 获取音频文件 /// private AudioClip GetAudioClip(SoundType type, string clipName) { if (!soundClipDict.ContainsKey(clipName)) { soundPathDict.TryGetValue(type, out string path); if (path == null) { Debug.LogError(type.ToString() + " 音频资源目录不存在,请检查方法参数type"); return null; } AudioClip ac = Resources.Load(path + "/" + clipName) as AudioClip; if (ac == null) { Debug.LogError("Resource中不存在该资源,请检查音频资源---" + path + "/" + clipName); return null; } soundClipDict.Add(clipName, ac); } return soundClipDict[clipName]; } ////// 获取音频组件 /// ///private AudioSource GetAudioSource(SoundType type, out ASPoolData poolData) { if (type == SoundType.BGM) { poolData = null; return _BGMAuidoSource; } poolData = _ASPoolList.Find(data => data.IsUseing == false); if(poolData != null) poolData.IsUseing = true; return poolData != null ? poolData.AudioSource_ : AddAudioSource(type, out poolData); } /// /// 添加音频组件 /// private AudioSource AddAudioSource(SoundType type, out ASPoolData poolData) { AudioSource audioSource = gameObject.AddComponent(); poolData = new ASPoolData(type, audioSource, true, false); _ASPoolList.Add(poolData); return audioSource; } /// /// 回收音频播放组件对象 /// private void RecyclePool(ASPoolData aSPoolData) { if (_ASPoolList.Contains(aSPoolData)) _ASPoolList.Find((date) => date == aSPoolData).IsUseing = false; //控制下对象池的大小 if (_ASPoolList.Count > poolCount_) { _ASPoolList.Remove(aSPoolData); Destroy(aSPoolData.AudioSource_); } } ////// 待音效播放完成后 将其移至未使用集合中, /// 同时执行播放完毕的回调 /// private IEnumerator WaitPlayEnd(ASPoolData aSPoolData, Action action) { if (aSPoolData == null) yield break; //如果没有在使用中的,或者没有正在播放且没有处于暂停状态的就要进行回收 yield return new WaitUntil(() => !aSPoolData.IsUseing || (!aSPoolData.AudioSource_.isPlaying && !aSPoolData.IsInPause)); RecyclePool(aSPoolData); if (action != null) action.Invoke(); yield break; } } ////// 音效类型 /// public enum SoundType { BGM, //背景音乐 UI, //背景音乐 Effect,//效果音 Talk //剧情对话 } ////// 音效组件池数据 /// [Serializable] public class ASPoolData { [SerializeField] private SoundType _type_; [SerializeField] private AudioSource _audioSource_; [SerializeField] private bool _isUseing; //是否在使用中(暂停中属于使用中,因为考虑到还可以继续播放) [SerializeField] private bool _isInPause;//是否在暂停中 public SoundType Type_ { get => _type_; set => _type_ = value; } public AudioSource AudioSource_ { get => _audioSource_; set => _audioSource_ = value; } public bool IsUseing { get => _isUseing; set => _isUseing = value; } public bool IsInPause { get => _isInPause; set => _isInPause = value; } public ASPoolData(SoundType _type, AudioSource _AS, bool _isUsed, bool _isInPause) { this.Type_ = _type; this.AudioSource_ = _AS; this.IsUseing = _isUsed; this.IsInPause = _isInPause; } } ////// 播放时的参数封装 /// public class PlayParams { public SoundType type; public string clipName; public bool isLoop; public Action endCallback;//播完的回调 //3d音效 public bool is3d = false; public Vector3 pos = Vector3.zero; //2d音效 public PlayParams(SoundType type, string name, bool isLoop = false, Action cb = null) { this.type = type; this.clipName = name; this.isLoop = isLoop; this.endCallback = cb; } //3d音效 public PlayParams(SoundType type, string name, bool is3d, Vector3 pos, bool isLoop = false, Action cb = null) { this.type = type; this.clipName = name; this.isLoop = isLoop; this.endCallback = cb; this.is3d = is3d; this.pos = pos; } }
using UnityEngine;
using UnityEngine.UI;
public class SoundMgrTest : MonoBehaviour
{
public Slider volumeSlider_BGM;
public Slider volumeSlider_UI;
public Slider volumeSlider_Talk;
public Slider volumeSlider_Effect;
//测试音效播放,暂停,继续,停止
public void OnPlayBGMBtnClick()
{
SoundManager.Instance.Play(new PlayParams(SoundType.BGM, "BGM01"));
}
public void OnPlayUIBtnClick()
{
SoundManager.Instance.Play(new PlayParams(SoundType.UI, "Bell_High"));
}
public void OnPlayTalkBtnClick()
{
SoundManager.Instance.Play(new PlayParams(SoundType.Talk, "Buzz"));
}
public void OnPlayEffectBtnClick()
{
SoundManager.Instance.Play(new PlayParams(SoundType.Effect, "Bell_Low"));
}
//测试音量调节
public void OnBGMVolumeChange()
{
SoundManager.Instance.SetVolume(SoundType.BGM, volumeSlider_BGM.value);
}
public void OnUIVolumeChange()
{
SoundManager.Instance.SetVolume(SoundType.UI, volumeSlider_UI.value);
}
public void OnTalkVolumeChange()
{
SoundManager.Instance.SetVolume(SoundType.Talk, volumeSlider_Talk.value);
}
public void OnEffectVolumeChange()
{
SoundManager.Instance.SetVolume(SoundType.Effect, volumeSlider_Effect.value);
}
//测试音效开关
public void OnBGMSwitchClick()
{
SoundManager.Instance.SetSwitch(SoundType.BGM);
}
public void OnUISwitchClick()
{
SoundManager.Instance.SetSwitch(SoundType.UI);
}
public void OnTalkSwitchClick()
{
SoundManager.Instance.SetSwitch(SoundType.Talk);
}
public void OnEffectSwitchClick()
{
SoundManager.Instance.SetSwitch(SoundType.Effect);
}
八、测试场景截图
总结
本文介绍了一个基于对象池优化思想的音频管理系统,采用对象池的优化思想,能够及时对不用的音效组件进行回收利用。



