自定义Unity组件——AudioManager(音频管理器)

这篇具有很好参考价值的文章主要介绍了自定义Unity组件——AudioManager(音频管理器)。希望对大家有所帮助。如果存在错误或未考虑完全的地方,请大家不吝赐教,您也可以点击"举报违法"按钮提交疑问。

需求描述

        在游戏开发中,音频资源是不可或缺的,通常情况下音频资源随机分布,各个音频的操作和管理都是各自负责,同时对于音频的很多操作逻辑都是大同小异的,这就造成了许多冗余代码的堆叠,除此之外在获取各类音频资源的时候也会造成不必要的开销。所以解决资源分散的问题最直接的方式就是集中管理和分配,通过统一的渠道和特有标识即可获取或操作对应的音频资源。所以本篇文章将围绕这个方案进行尝试。

功能描述

        在Unity中我们导入的音频资源都会转换为AudioClip,音频的设置和管理则由AudioSource负责,AudioListener负责监听音频。我们可以在此基础上去封装,从而打造一个音频管理器。

        音频管理器负责管理音频信息以及操作音频,比如音频信息的增加和删除,音频的播放和暂停等;

        音频信息可以使用一个单独的实体类来记录,用于记录AudioSource组件中的信息,之所以单独用一个实体类来记录音频信息而不直接采用AudioSource,主要是可以通过业务需求去动态调整所要记录的音频信息,我们的实际开发中并非需要AudioSource中所有的信息,有时候仅仅需要其中的一部分,同时音频信息可能涉及存储,直接采用AudioSource可能无法与已经开发好的存储系统相互兼容,而实体类可以为其添加接口或继承来兼容存储系统;

        AudioSource组件的管理可以通过一个组件池进行管理,我们知道AudioSource也是等同于一个音频实体类,但是其同时也是一种组件,组件同样作为一种资源,通常情况下相比简单的实体类而言会带来更大的开销,例如一个场景中有十个游戏对象有播放音频的需求,那么按照传统情况就需要每个游戏对象挂载一个AudioSource组件,但是实际运行中十个游戏对象并不一定需要同时播放音频,它们或许都存在各自触发音频播放的条件,我们只需要在游戏对象需要播放音频时为其分配一个AudioSource组件即可,组件池则负责维护AudioSource组件的生产、获取和归还,进而减少资源开销。

        本质上,AudioSource组件承担的工作就是记录音频信息和操作音频,我们现在将记录工作分担给音频信息实体类,而操作音频的工作则分担给音频管理器,例如音频管理器的播放依旧是调用AudioSource的播放方法,在播放之前由音频管理器去获取AudioSource组件并且为之配置音频信息,其它的音频操作逻辑同理。

代码展示(C#)

AudioManager.cs

using System.Linq;
using System;
using System.Collections.Generic;
using UnityEngine;

namespace Tools.AudioManagerAPI
{
    /// <summary>
    /// 音频管理器
    /// </summary>
    [DisallowMultipleComponent]
    public class AudioManager : MonoBehaviour
    {
        [Header("必要属性")]
        [Tooltip("音频总音量,默认为1(值属于[0,1]),该值将影响所有音频的音量,例如该值为0则所有音频音量变为原有音量的0%,若为1则所有音频音量保持不变,即该值将基于所有音频的当前音量进行影响,而不是直接统一所有音频的音量为该值")]
        [Range(0, 1), SerializeField] private float TotalVolume = 1;

        /// <summary>
        /// 音频总音量,默认为1(值属于[0,1])
        /// <para>声明:该值将影响所有音频的音量,例如该值为0则所有音频音量变为原有音量的0%,若为1则所有音频音量保持不变,即该值将基于所有音频的当前音量进行影响,而不是直接统一所有音频的音量为该值</para>
        /// </summary>
        public float mTotalVolume
        {
            get { return TotalVolume; }
            set
            {
                if (value >= 0 && value <= 1 && TotalVolume != value)
                {
                    TotalVolume = value;
                    mTotalVolumeChangedEvents?.Invoke(value);
                }
            }
        }

        /// <summary>
        /// 是否启用音频信息覆盖,默认为true
        /// </summary>
        public bool mIsOverWrite { get => isOverWrite; set => isOverWrite = value; }

        /// <summary>
        /// 音频管理器中所存储的音频数量
        /// </summary>
        public int mCount { get => audioInfos.Count; }

        /// <summary>
        /// 总音量更改事件回调
        /// </summary>
        public event Action<float> mTotalVolumeChangedEvents;

        /// <summary>
        /// 音频信息名称合集
        /// </summary>
        public string[] mAudioInfoNames { get => audioInfos.Keys.ToArray(); }

        private Dictionary<string, AudioInfo> audioInfos;//音频信息集合
        private bool isInit;//是否完成了初始化
        private bool isOverWrite;//是否启用音频信息覆盖
        private static AudioSourcePool audioSourcePool = AudioSourcePool.GetInstance();//AudioSource组件池

        /// <summary>
        /// 播放指定名称的音频
        /// <para>p_audioName:音频名称</para>
        /// </summary>
        public void Play(string p_audioName)
        {
            if (isInit)
            {
                if (audioInfos.ContainsKey(p_audioName))
                {
                    AudioInfo ai = audioInfos[p_audioName];
                    AudioSource v_audioSource = audioSourcePool.Get(ai);
                    ai.mAudioSource = v_audioSource;
                    ai.Play();
                }
            }
        }

        /// <summary>
        /// 播放指定名称的音频并开启立体声过渡
        /// <para>p_audioName:音频名称</para>
        /// <para>声明:该方法要求已启用立体声过渡且已设置好立体声过渡的相关属性</para>
        /// </summary>
        public void PlayWithStereoTransition(string p_audioName)
        {
            if (isInit)
            {
                if (audioInfos.ContainsKey(p_audioName))
                {
                    AudioInfo ai = audioInfos[p_audioName];
                    AudioSource v_audioSource = audioSourcePool.Get(ai);
                    ai.mAudioSource = v_audioSource;
                    StartCoroutine(ai.mStereoPanTransitionCoroutine);
                    ai.Play();
                }
            }
        }

        /// <summary>
        /// 播放指定名称的音频并开启立体声过渡
        /// <para>p_audioName:音频名称</para>
        /// <para>p_stereoTransitionValues:立体声过渡值集合</para>
        /// <para>p_stereoTimeSpan:立体声过渡每帧时间间隔</para>
        /// </summary>
        public void PlayWithStereoTransition(string p_audioName, float[] p_stereoTransitionValues, float p_stereoTimeSpan)
        {
            if (isInit)
            {
                if (audioInfos.ContainsKey(p_audioName))
                {
                    AudioInfo ai = audioInfos[p_audioName];
                    AudioSource v_audioSource = audioSourcePool.Get(ai);
                    ai.mAudioSource = v_audioSource;
                    ai.mStereoTransition = true;
                    ai.mStereoTransitionValues = p_stereoTransitionValues;
                    ai.mStereoTransitionTimeSpan = p_stereoTimeSpan;
                    StartCoroutine(ai.mStereoPanTransitionCoroutine);
                    ai.Play();
                }
            }
        }

        /// <summary>
        /// 暂停播放指定名称的音频
        /// <para>p_audioName:音频名称</para>
        /// </summary>
        public void Pause(string p_audioName)
        {
            if (isInit)
            {
                if (audioInfos.ContainsKey(p_audioName))
                {
                    AudioInfo ai = audioInfos[p_audioName];
                    StopCoroutine(ai.mStereoPanTransitionCoroutine);
                    ai.Pause();
                    audioSourcePool.Return(ai.mAudioSource);
                }
            }
        }

        /// <summary>
        /// 添加音频信息
        /// <para>p_audioInfo:音频信息</para>
        /// <para>声明1:若启用了音频信息覆盖,当存在相同名称的音频时,新的音频信息将覆盖旧的音频信息</para>
        /// <para>声明2:默认启用了音频信息覆盖,可通过mIsOverWrite属性设置禁用</para>
        /// </summary>
        public void AddAudioInfo(AudioInfo p_audioInfo)
        {
            if (isInit) DoAddAudioInfo(p_audioInfo);
        }

        /// <summary>
        /// 删除音频信息
        /// <para>p_audioName:音频名称</para>
        /// <para>返回值:若删除成功则返回true,否则返回false</para>
        /// </summary>
        public bool DeleteAudioInfo(string p_audioName)
        {
            if (isInit) return DoDeleteAudioInfo(p_audioName);
            return false;
        }

        private void Awake()
        {
            isInit = false;
            if (InitParameters()) isInit = true;
        }

        //添加音频信息的执行逻辑
        private void DoAddAudioInfo(AudioInfo p_audioInfo)
        {
            if (p_audioInfo != null && !String.IsNullOrEmpty(p_audioInfo.mAudioName))
            {
                string v_audioName = p_audioInfo.mAudioName;
                p_audioInfo.BindAudioManager(this);
                if (isOverWrite) audioInfos[v_audioName] = p_audioInfo;
                else if (!audioInfos.ContainsKey(v_audioName)) audioInfos.Add(v_audioName, p_audioInfo);
            }
        }

        //删除音频信息的执行逻辑
        private bool DoDeleteAudioInfo(string p_audioName)
        {
            if (!String.IsNullOrEmpty(p_audioName) && audioInfos.ContainsKey(p_audioName))
            {
                audioInfos[p_audioName].BindAudioManager(null);
                return audioInfos.Remove(p_audioName);
            }
            return false;
        }

        //对音频管理器相关参数进行初始化
        private bool InitParameters()
        {
            audioInfos = new Dictionary<string, AudioInfo>();
            isOverWrite = true;
            audioSourcePool.BindAudioManager(this);
#if UNITY_EDITOR
            foreach (AudioInfo ai in AudioInfos)
                DoAddAudioInfo(ai);
#endif
            return true;
        }

#if UNITY_EDITOR
        /// <summary>
        /// 在当前Inspector面板中的AudioInfos中的元素数量
        /// </summary>
        public int mAudioInfoCount { get => AudioInfos?.Length > 0 ? AudioInfos.Length : 0; }

        [SerializeField] private AudioInfo[] AudioInfos;//存储Inspector面板中

        /// <summary>
        /// 在当前Inspector面板中的AudioInfos中添加一个元素
        /// </summary>
        public void Add()
        {
            AudioInfo v_audioInfo = new AudioInfo();
            if (AudioInfos?.Length > 0)
            {
                AudioInfo[] v_audioInfos = new AudioInfo[AudioInfos.Length + 1];
                AudioInfos.CopyTo(v_audioInfos, 0);
                v_audioInfos[v_audioInfos.Length - 1] = v_audioInfo;
                AudioInfos = new AudioInfo[v_audioInfos.Length];
                v_audioInfos.CopyTo(AudioInfos, 0);
            }
            else AudioInfos = new AudioInfo[] { v_audioInfo };
            v_audioInfo.ValidateCheck();
        }

        /// <summary>
        /// 在当前Inspector面板中的AudioInfos中删除一个元素
        /// </summary>
        public void Delete(int p_index)
        {
            if (AudioInfos?.Length == 1) AudioInfos = Array.Empty<AudioInfo>();
            else if (AudioInfos?.Length > 1)
            {
                AudioInfo[] v_audioInfos = new AudioInfo[AudioInfos.Length - 1];
                int v_index = 0;
                for (int i = 0; i < AudioInfos.Length; i++)
                {
                    if (i != p_index) v_audioInfos[v_index++] = AudioInfos[i];
                }
                AudioInfos = new AudioInfo[v_audioInfos.Length];
                v_audioInfos.CopyTo(AudioInfos, 0);
            }
        }

        private void OnValidate()
        {
            foreach (AudioInfo audioInfo in AudioInfos)
            {
                audioInfo?.ValidateCheck();
            }
        }
#endif
    }
}

AudioInfo.cs

using UnityEngine;
using System.Collections;
using System.Linq;
using System;

namespace Tools.AudioManagerAPI
{
    /// <summary>
    /// 音频信息
    /// </summary>
    [System.Serializable]
    public class AudioInfo
    {
        [Header("必要组件")]
        [Tooltip("AudioClip组件"), SerializeField] private AudioClip TheAudioClip;

        [Header("必要属性")]
        [Tooltip("音频名称"), SerializeField] private string AudioName;
        [Tooltip("音频音量,默认为1(值属于[0,1])"), Range(0, 1), SerializeField] private float Volume = 1;
        [Tooltip("音频播放速度,默认为1(值属于[-3,3])"), Range(-3, 3), SerializeField] private float Pitch = 1;
        [Tooltip("立体声位置,默认为0(值属于[-1,1]),若为-1则完全为左声道,若为1则完全为右声道"), Range(-1, 1), SerializeField] private float StereoPan = 0;
        [Tooltip("音频优先级,默认为128(值属于[0,256])"), Range(0, 256), SerializeField] private int Priority = 128;
        [Tooltip("是否在场景启动时进行播放,默认为true"), SerializeField] private bool PlayOnAwake = true;
        [Tooltip("是否循环播放,默认为false"), SerializeField] private bool Loop;
        [Tooltip("是否忽略总音量影响,默认为false"), SerializeField] private bool IgnoreTotalVolume;

        [Header("立体声过渡属性")]
        [Tooltip("是否启用立体声过渡,默认为false"), SerializeField] private bool StereoTransition;
        [Tooltip("立体声过渡的每帧时间间隔,默认为0.5(值属于[0.1,5])"), Range(.1f, 5), SerializeField] private float StereoTransitionTimeSpan = 0.5f;
        [Tooltip("立体声过渡值集合"), SerializeField] private float[] StereoTransitionValues;

        /// <summary>
        /// 音频名称
        /// </summary>
        public string mAudioName { get => AudioName; set => AudioName = value; }

        /// <summary>
        /// 音频音量,默认为1(值属于[0,1])
        /// </summary>
        public float mVolume
        {
            get { return Volume; }
            set { if (value >= 0 && value <= 1) Volume = value; }
        }

        /// <summary>
        /// 音频播放速度,默认为1(值属于[-3,3])
        /// </summary>
        public float mPitch
        {
            get { return Pitch; }
            set { if (value >= -3 && value <= 3) Pitch = value; }
        }

        /// <summary>
        /// 立体声位置,默认为0(值属于[-1,1]),若为-1则完全为左声道,若为1则完全为右声道
        /// </summary>
        public float mStereoPan
        {
            get { return StereoPan; }
            set { if (value >= -1 && value <= 1) StereoPan = value; }
        }

        /// <summary>
        /// 音频优先级,默认为128(值属于[0,256])
        /// </summary>
        public int mPriority
        {
            get { return Priority; }
            set { if (value >= 0 && value <= 256) Priority = value; }
        }

        /// <summary>
        /// 是否在场景启动时进行播放,默认为true
        /// </summary>
        public bool mPlayOnAwake { get => PlayOnAwake; set => PlayOnAwake = value; }

        /// <summary>
        /// 是否循环播放,默认为false
        /// </summary>
        public bool mLoop { get => Loop; set => Loop = value; }

        /// <summary>
        /// 是否启用立体声过渡,默认为false
        /// </summary>
        public bool mStereoTransition { get => StereoTransition; set => StereoTransition = value; }

        /// <summary>
        /// 立体声过渡的每帧时间间隔,默认为0.5(值属于[0.1,5])
        /// </summary>
        public float mStereoTransitionTimeSpan
        {
            get { return StereoTransitionTimeSpan; }
            set { if (value >= 0.1f && value <= 5) StereoTransitionTimeSpan = value; }
        }

        /// <summary>
        /// 立体声过渡值集合
        /// </summary>
        public float[] mStereoTransitionValues
        {
            get => StereoTransitionValues;
            set => StereoTransitionValues = value;
        }

        /// <summary>
        /// AudioSource组件
        /// </summary>
        public AudioSource mAudioSource { get => audioSource; set => audioSource = value; }

        /// <summary>
        /// 立体声过渡协程
        /// </summary>
        public IEnumerator mStereoPanTransitionCoroutine { get => stereoPanTransitionCoroutine; }

        /// <summary>
        /// 是否忽略总音量影响,默认为false
        /// </summary>
        public bool mIgnoreTotalVolume { get => IgnoreTotalVolume; set => IgnoreTotalVolume = value; }

        private AudioSource audioSource;//AudioSource组件
        private AudioManager audioManager;//音频管理器
        private bool isInit;//是否完成初始化
        private IEnumerator stereoPanTransitionCoroutine;//立体声过渡协程
        private float actualVolume;//实际音量
        private Action<float> totalVolumeChangedEvent;//总音量更改事件对象

        /// <summary>
        /// 将指定的AudioSource组件信息记录在新的AudioInfo实例中并返回它
        /// <para>p_audioSource:指定的AudioSource组件</para>
        /// <para>返回值:新的AudioInfo实例</para>
        /// </summary>
        public static AudioInfo Record(AudioSource p_audioSource)
        {
            AudioInfo v_audioInfo = new AudioInfo();
            if (p_audioSource != null)
            {
                v_audioInfo.TheAudioClip = p_audioSource.clip;
                v_audioInfo.Volume = p_audioSource.volume;
                v_audioInfo.Pitch = p_audioSource.pitch;
                v_audioInfo.StereoPan = p_audioSource.panStereo;
                v_audioInfo.Priority = p_audioSource.priority;
                v_audioInfo.PlayOnAwake = p_audioSource.playOnAwake;
                v_audioInfo.Loop = p_audioSource.loop;
                v_audioInfo.audioSource = p_audioSource;
            }
            return v_audioInfo;
        }

        public AudioInfo()
        {
            isInit = false;
            InitToDefault();
        }

        /// <summary>
        /// 播放音频
        /// </summary>
        public void Play()
        {
            if (audioSource != null && !audioSource.isPlaying)
            {
                if (!IgnoreTotalVolume)
                {
                    if (actualVolume < 0 || actualVolume > 1) actualVolume = Volume;
                    audioSource.volume = actualVolume;
                }
                else actualVolume = -1;
                audioSource.Play();
            }
        }

        /// <summary>
        /// 暂停音频播放
        /// </summary>
        public void Pause()
        {
            if (audioSource != null && audioSource.isPlaying)
            {
                audioSource.Pause();
                audioSource.volume = Volume;
            }
        }

        /// <summary>
        /// 绑定音频管理器
        /// <para>p_audioManager:音频管理器</para>
        /// <para>声明1:若有需要可通过该方法将当前的AudioInfo与指定的音频管理器进行绑定</para>
        /// <para>声明2:绑定后将自动向指定的音频管理器添加当前的AudioInfo</para>
        /// </summary>
        public void BindAudioManager(AudioManager p_audioManager)
        {
            audioManager = p_audioManager;
            if (audioManager != null)
            {
                audioManager.mTotalVolumeChangedEvents -= totalVolumeChangedEvent;
                audioManager.mTotalVolumeChangedEvents += totalVolumeChangedEvent;
            }
        }

        /// <summary>
        /// 初始化为默认值
        /// </summary>
        public void InitToDefault()
        {
            if (!isInit)
            {
                TheAudioClip = null;
                AudioName = "Audio";
                Volume = 1;
                Pitch = 1;
                StereoPan = 0;
                StereoTransitionValues = null;
                StereoTransitionTimeSpan = 0.5f;
                Priority = 128;
                StereoTransition = false;
                PlayOnAwake = true;
                Loop = false;
                audioSource = null;
                stereoPanTransitionCoroutine = StereoPanTransition();
                totalVolumeChangedEvent = (val) => TotalVolumeChangedEvent(val);
                actualVolume = -1;
            }
        }

        /// <summary>
        /// 将当前AudioInfo实例中的信息配置给指定的AudioSource组件
        /// <para>p_audioSource:指定的AudioSource组件</para>
        /// </summary>
        public void ShareTo(AudioSource p_audioSource)
        {
            if (p_audioSource != null)
            {
                p_audioSource.clip = TheAudioClip;
                p_audioSource.volume = Volume;
                p_audioSource.pitch = Pitch;
                p_audioSource.panStereo = StereoPan;
                p_audioSource.priority = Priority;
                p_audioSource.playOnAwake = PlayOnAwake;
                p_audioSource.loop = Loop;
            }
        }

        /// <summary>
        /// 将指定的AudioSource组件信息存储在当前AudioInfo实例中
        /// <para>p_audioSource:指定的AudioSource组件</para>
        /// </summary>
        public void SelfRecord(AudioSource p_audioSource)
        {
            if (p_audioSource != null)
            {
                TheAudioClip = p_audioSource.clip;
                Volume = p_audioSource.volume;
                Pitch = p_audioSource.pitch;
                StereoPan = p_audioSource.panStereo;
                Priority = p_audioSource.priority;
                PlayOnAwake = p_audioSource.playOnAwake;
                Loop = p_audioSource.loop;
                audioSource = p_audioSource;
            }
        }

        // 总音量更改事件
        // p_totalVolume:总音量
        // 若不忽略总音量影响,通过调用该事件将基于总音量和当前音量换算实际音量数值
        // 当TotleVolume为0时,实际音量为0;
        // 当TotleVolume为1或不属于[0,1)时,实际音量为Volume;
        // 当TotleVolume属于(0,1)时,实际音量为Volume * TotalVolume
        private void TotalVolumeChangedEvent(float p_totalVolume)
        {
            if (!IgnoreTotalVolume)
            {
                if (p_totalVolume == 0) actualVolume = 0;
                else if (p_totalVolume > 0 && p_totalVolume < 1) actualVolume = Volume * p_totalVolume;
                else actualVolume = Volume;
                //运行时修改AudioSource音量
                if (audioSource != null) audioSource.volume = actualVolume;
            }
            else actualVolume = -1;
        }

        //立体声过渡协程
        private IEnumerator StereoPanTransition()
        {
            int currentIndex = 0;
            while (true)
            {
                if (audioSource == null || !StereoTransition || StereoTransitionValues == null || StereoTransitionValues.Length == 0)
                    yield break;
                audioSource.panStereo = StereoTransitionValues[currentIndex];
                yield return new WaitForSeconds(StereoTransitionTimeSpan);
                currentIndex = (currentIndex + 1) % StereoTransitionValues.Length;
                if (currentIndex == 0) StereoTransitionValues = StereoTransitionValues.Reverse().ToArray<float>();
            }
        }

#if UNITY_EDITOR
        [NonSerialized] private bool isAudioClipLog;
        private bool isAudioNameLog;

        /// <summary>
        /// Inspector面板的数据更改检测
        /// </summary>
        public void ValidateCheck()
        {
            AudioClipCheck();
            AudioNameCheck();
        }

        //AudioClip检测
        private void AudioClipCheck()
        {
            if (TheAudioClip == null)
            {
                if (!isAudioClipLog)
                {
                    Debug.LogWarning("Component: <b><color=orange>TheAudioClip</color></b> is null.");
                    isAudioClipLog = true;
                }
            }
            else isAudioClipLog = false;
        }

        //AudioName检测
        private void AudioNameCheck()
        {
            if (String.IsNullOrEmpty(AudioName))
            {
                if (!isAudioNameLog)
                {
                    Debug.LogWarning("Property: <b><color=orange>AudioName</color></b> is empty.");
                    isAudioNameLog = true;
                }
            }
            else isAudioNameLog = false;
        }
#endif
    }
}

AudioSourcePool.cs 

using System.Collections.Generic;
using UnityEngine;

namespace Tools.AudioManagerAPI
{
    /// <summary>
    /// AudioSource组件池
    /// </summary>
    public class AudioSourcePool
    {
        /// <summary>
        /// 空闲的AudioSource数量
        /// </summary>
        public int mFreeCount { get => audioSources.Count; }
        private Stack<AudioSource> audioSources;//AudioSource组件集合
        private AudioManager audioManager;//AudioManager组件
        private AudioInfo defaultAudioInfo;//默认的AudioInfo

        /// <summary>
        /// 获取实例(单例模式)
        /// </summary>
        public static AudioSourcePool GetInstance()
        {
            return Handler.instance;
        }

        /// <summary>
        /// 绑定音频管理器
        /// <para>p_audioManager:音频管理器</para>
        /// </summary>
        public void BindAudioManager(AudioManager p_audioManager)
        {
            if (p_audioManager != null) audioManager = p_audioManager;
        }

        /// <summary>
        /// 获取AudioSource组件
        /// <para>返回值:AudioSource组件</para>
        /// </summary>
        public AudioSource Get()
        {
            return DoGet();
        }

        /// <summary>
        /// 获取AudioSource组件并按照指定的AudioInfo为之配置属性
        /// <para>p_audioInfo:指定的AudioInfo</para>
        /// <para>返回值:AudioSource组件</para>
        /// </summary>
        public AudioSource Get(AudioInfo p_audioInfo)
        {
            AudioSource v_audioSource = DoGet();
            p_audioInfo?.ShareTo(v_audioSource);
            return v_audioSource;
        }

        /// <summary>
        /// 归还指定的AudioSource组件
        /// <para>p_audioSource:指定的AudioSource组件</para>
        /// </summary>
        public void Return(AudioSource p_audioSource)
        {
            if (p_audioSource != null)
            {
                CleanAudioSource(p_audioSource);
                audioSources.Push(p_audioSource);
            }
        }

        class Handler
        {
            public static AudioSourcePool instance = new AudioSourcePool();
        }

        private AudioSourcePool()
        {
            audioSources = new Stack<AudioSource>();
            defaultAudioInfo = new AudioInfo();
        }

        //获取AudioSource组件的执行逻辑
        private AudioSource DoGet()
        {
            if (audioManager == null) return null;
            AudioSource v_audioSource = null;
            while (v_audioSource == null)
            {
                if (audioSources.Count == 0) GenerateAudioSource();
                v_audioSource = audioSources.Pop();
            }
            return v_audioSource;
        }

        //生成AudioSource组件
        private void GenerateAudioSource()
        {
            if (audioManager?.gameObject != null)
            {
                AudioSource v_audioSource = audioManager.gameObject.AddComponent<AudioSource>();
                audioSources.Push(v_audioSource);
            }
        }

        //清洗AudioSource组件
        private void CleanAudioSource(AudioSource p_audioSource)
        {
            defaultAudioInfo.ShareTo(p_audioSource);
        }
    }
}

NumberRange.cs

using System.Collections.Generic;
using System.Linq;

namespace Tools.AudioManagerAPI
{
    /// <summary>
    /// 数值范围数组工具类
    /// </summary>
    public static class NumberRange
    {
        /// <summary>
        /// 获取指定范围内指定步长的Float数值数组
        /// <para>p_start:起始值</para>
        /// <para>p_end:终点值</para>
        /// <para>p_step:步长值</para>
        /// <para>[ContainsEnd]:是否包括终点值,默认为false</para>
        /// <para>返回值:Float[]</para>
        /// </summary>
        public static float[] FloatRange(float p_start, float p_end, float p_step, bool ContainsEnd = false)
        {
            if (!ContainsEnd) return DoFloatRange(p_start, p_end, p_step).ToArray();
            else
            {
                List<float> result = DoFloatRange(p_start, p_end, p_step).ToList();
                result.Add(p_end);
                return result.ToArray();
            }
        }

        //获取指定范围内指定步长的Float数值数组的执行逻辑
        static IEnumerable<float> DoFloatRange(float p_start, float p_end, float p_step)
        {
            for (float i = p_start; i <= p_end; i += p_step)
            {
                yield return i;
            }
        }
    }
}

界面展示

audiomanager,Unity开发,unity,音频

演示效果

自定义Unity组件AudioManager

资源下载

GitHub_AudioManager    百度网盘

如果这篇文章对你有帮助,请给作者点个赞吧! 文章来源地址https://www.toymoban.com/news/detail-767013.html

到了这里,关于自定义Unity组件——AudioManager(音频管理器)的文章就介绍完了。如果您还想了解更多内容,请在右上角搜索TOY模板网以前的文章或继续浏览下面的相关文章,希望大家以后多多支持TOY模板网!

本文来自互联网用户投稿,该文观点仅代表作者本人,不代表本站立场。本站仅提供信息存储空间服务,不拥有所有权,不承担相关法律责任。如若转载,请注明出处: 如若内容造成侵权/违法违规/事实不符,请点击违法举报进行投诉反馈,一经查实,立即删除!

领支付宝红包 赞助服务器费用

相关文章

  • Unity基础 音频组件以及音频播放

    在游戏开发中,声音是一个重要的环节。Unity中的声音组件可以帮助开发者轻松地控制游戏中音频的播放、音量、循环等属性,从而实现更好的游戏体验。本文将详细介绍Unity声音组件的相关概念和技术,以及其在游戏、影视等领域的广泛应用和发展前景。       在Unity中想

    2024年02月11日
    浏览(44)
  • Vue实现自定义音频播放器组件

    template javascript less 文档参考 关于 Audio 自定义样式 H5 audio 音频标签自定义样式修改以及添加播放控制事件

    2024年02月12日
    浏览(33)
  • 【uniapp】小程序开发:2 安装uni-ui组件库、使用pinia状态管理、自定义http请求

    1、安装 2、配置组件自动导入 使用 npm 安装好 uni-ui 之后,需要配置 easycom 规则,让 npm 安装的组件支持 easycom 打开项目根目录下的 pages.json 并添加 easycom 节点: 3、安装插件,实现uni-ui组件的类型提示 安装完成后,在 tsconfig.json 中增加配置项 4、测试使用 随便复制一个组件在

    2024年02月08日
    浏览(58)
  • Unity 之利用Audio Source(音频源)组件用于播放声音

    Unity中的Audio Source(音频源)是一个用于播放声音的组件,通常附加到游戏对象上,以便在游戏中播放音频效果、音乐或对话。以下是Audio Source的详细介绍: 添加Audio Source : 要在Unity中使用Audio Source,首先需要将其附加到一个游戏对象上。通常,您可以选择场景中的一个空游

    2024年02月09日
    浏览(50)
  • Unity 音频插件 - MasterAudio 实现音频管理系统

    插件介绍: Master Audio的是一个整体解决方案,所有的丰富的游戏音频需求。内置的音乐闪避,手动和自动的声音触发真正的随机声音变化,音频汇集全3D声音功能。支持所有出口的手机游戏平台,具有一流的性能。 主音频在线帮助网站可在此处找到: Table of Contents 完整的 主

    2023年04月10日
    浏览(44)
  • Unity音频管理器

    在Unity中,为了管理并播放多个音效,可以创建一个音频管理器类来负责加载、缓存和播放音频剪辑。以下是一个简单的示例,展示如何构建一个基本的音效管理播放器脚本: 要使用这个音频管理器,你可以在场景中挂载该脚本的实例,并在Inspector面板中关联所需的AudioClip

    2024年01月20日
    浏览(38)
  • 【unity每日一记】 三大金星之(音频Audio + 碰撞和触发+光源组件)

    👨‍💻个人主页 :@元宇宙-秩沅 👨‍💻 hallo 欢迎 点赞👍 收藏⭐ 留言📝 加关注✅! 👨‍💻 本文由 秩沅 原创 👨‍💻 收录于专栏 : unity每日一记 ⭐【Unityc#专题篇】之c#系统化大礼包】 ⭐【unity数据持久化】数据管理类_PlayerPrfs ⭐【unity本站最全系列】unity常用API大全

    2024年02月14日
    浏览(41)
  • Unity零基础到入门 ☀️| 音频源Audio Source组件 详解

    🎬 博客主页:https://xiaoy.blog.csdn.net 🎥 本文由 呆呆敲代码的小Y 原创,首发于 CSDN 🙉 🎄 学习专栏推荐:

    2024年02月12日
    浏览(42)
  • Unity零基础到进阶 ☀️| 音频源Audio Source组件 详解

    🎬 博客主页:https://xiaoy.blog.csdn.net 🎥 本文由 呆呆敲代码的小Y 原创,首发于 CSDN 🙉 🎄 学习专栏推荐:

    2024年02月13日
    浏览(36)
  • 微信小程序开发(第五期):npm包的使用、Vant Weapp 组件库的使用 && API Promise优化 、全局状态管理 && store 、分包、分包预下载 && 自定义配置tabBar

    LiuJInTao:2024年4月6日 一、小程序对 npm 包的限制 二、Vant Weapp 组件库 1. 什么是 Vant Weapp 官网文档地址 2. Vant Weapp 组件库的安装 注意点: 我们最新版本的微信开发者工具是不是没有勾选npm的,默认系统内部自动勾选。 步骤一 通过 npm 安装 使用npm构建前,请先阅读微信官方的

    2024年04月27日
    浏览(34)

觉得文章有用就打赏一下文章作者

支付宝扫一扫打赏

博客赞助

微信扫一扫打赏

请作者喝杯咖啡吧~博客赞助

支付宝扫一扫领取红包,优惠每天领

二维码1

领取红包

二维码2

领红包