栏目分类:
子分类:
返回
名师互学网用户登录
快速导航关闭
当前搜索
当前分类
子分类
实用工具
热门搜索
名师互学网 > IT > 软件开发 > 游戏开发 > 其他

Unity3d--基于对象池优化思想的音频管理系统

其他 更新时间: 发布时间: IT归档 最新发布 模块sitemap 名妆网 法律咨询 聚返吧 英语巴士网 伯小乐 网商动力

Unity3d--基于对象池优化思想的音频管理系统

提示:文章写完后,目录可以自动生成,如何生成可参考右边的帮助文档

文章目录
  • 前言
  • 一、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 Dictionary soundPathDict = new Dictionary();
    /// 
    /// 音效播放音量大小
    /// 
    private static readonly Dictionary soundVolumeDict = new Dictionary();
    /// 
    /// 音效的静音状态
    /// 
    private static readonly Dictionary soundMuteStateDict = new Dictionary();
    /// 
    /// 记录声音效片段
    /// 
    private static Dictionary soundClipDict = 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);
    }
八、测试场景截图

总结

本文介绍了一个基于对象池优化思想的音频管理系统,采用对象池的优化思想,能够及时对不用的音效组件进行回收利用。

转载请注明:文章转载自 www.mshxw.com
本文地址:https://www.mshxw.com/it/901855.html
我们一直用心在做
关于我们 文章归档 网站地图 联系我们

版权所有 (c)2021-2022 MSHXW.COM

ICP备案号:晋ICP备2021003244-6号