在游戏开发中,音频资源是不可或缺的,通常情况下音频资源随机分布,各个音频的操作和管理都是各自负责,同时对于音频的很多操作逻辑都是大同小异的,这就造成了许多冗余代码的堆叠,除此之外在获取各类音频资源的时候也会造成不必要的开销。所以解决资源分散的问题最直接的方式就是集中管理和分配,通过统一的渠道和特有标识即可获取或操作对应的音频资源。所以本篇文章将围绕这个方案进行尝试。
在Unity中我们导入的音频资源都会转换为AudioClip,音频的设置和管理则由AudioSource负责,AudioListener负责监听音频。我们可以在此基础上去封装,从而打造一个音频管理器。
音频管理器负责管理音频信息以及操作音频,比如音频信息的增加和删除,音频的播放和暂停等;
音频信息可以使用一个单独的实体类来记录,用于记录AudioSource组件中的信息,之所以单独用一个实体类来记录音频信息而不直接采用AudioSource,主要是可以通过业务需求去动态调整所要记录的音频信息,我们的实际开发中并非需要AudioSource中所有的信息,有时候仅仅需要其中的一部分,同时音频信息可能涉及存储,直接采用AudioSource可能无法与已经开发好的存储系统相互兼容,而实体类可以为其添加接口或继承来兼容存储系统;
AudioSource组件的管理可以通过一个组件池进行管理,我们知道AudioSource也是等同于一个音频实体类,但是其同时也是一种组件,组件同样作为一种资源,通常情况下相比简单的实体类而言会带来更大的开销,例如一个场景中有十个游戏对象有播放音频的需求,那么按照传统情况就需要每个游戏对象挂载一个AudioSource组件,但是实际运行中十个游戏对象并不一定需要同时播放音频,它们或许都存在各自触发音频播放的条件,我们只需要在游戏对象需要播放音频时为其分配一个AudioSource组件即可,组件池则负责维护AudioSource组件的生产、获取和归还,进而减少资源开销。
本质上,AudioSource组件承担的工作就是记录音频信息和操作音频,我们现在将记录工作分担给音频信息实体类,而操作音频的工作则分担给音频管理器,例如音频管理器的播放依旧是调用AudioSource的播放方法,在播放之前由音频管理器去获取AudioSource组件并且为之配置音频信息,其它的音频操作逻辑同理。
AudioManager.cs
- using System.Linq;
- using System;
- using System.Collections.Generic;
- using UnityEngine;
-
- namespace Tools.AudioManagerAPI
- {
- ///
- /// 音频管理器
- ///
- [DisallowMultipleComponent]
- public class AudioManager : MonoBehaviour
- {
- [Header("必要属性")]
- [Tooltip("音频总音量,默认为1(值属于[0,1]),该值将影响所有音频的音量,例如该值为0则所有音频音量变为原有音量的0%,若为1则所有音频音量保持不变,即该值将基于所有音频的当前音量进行影响,而不是直接统一所有音频的音量为该值")]
- [Range(0, 1), SerializeField] private float TotalVolume = 1;
-
- ///
- /// 音频总音量,默认为1(值属于[0,1])
- ///
声明:该值将影响所有音频的音量,例如该值为0则所有音频音量变为原有音量的0%,若为1则所有音频音量保持不变,即该值将基于所有音频的当前音量进行影响,而不是直接统一所有音频的音量为该值 - ///
- public float mTotalVolume
- {
- get { return TotalVolume; }
- set
- {
- if (value >= 0 && value <= 1 && TotalVolume != value)
- {
- TotalVolume = value;
- mTotalVolumeChangedEvents?.Invoke(value);
- }
- }
- }
-
- ///
- /// 是否启用音频信息覆盖,默认为true
- ///
- public bool mIsOverWrite { get => isOverWrite; set => isOverWrite = value; }
-
- ///
- /// 音频管理器中所存储的音频数量
- ///
- public int mCount { get => audioInfos.Count; }
-
- ///
- /// 总音量更改事件回调
- ///
- public event Action<float> mTotalVolumeChangedEvents;
-
- ///
- /// 音频信息名称合集
- ///
- 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组件池
-
- ///
- /// 播放指定名称的音频
- ///
p_audioName:音频名称 - ///
- 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();
- }
- }
- }
-
- ///
- /// 播放指定名称的音频并开启立体声过渡
- ///
p_audioName:音频名称 - ///
声明:该方法要求已启用立体声过渡且已设置好立体声过渡的相关属性 - ///
- 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();
- }
- }
- }
-
- ///
- /// 播放指定名称的音频并开启立体声过渡
- ///
p_audioName:音频名称 - ///
p_stereoTransitionValues:立体声过渡值集合 - ///
p_stereoTimeSpan:立体声过渡每帧时间间隔 - ///
- 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();
- }
- }
- }
-
- ///
- /// 暂停播放指定名称的音频
- ///
p_audioName:音频名称 - ///
- 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);
- }
- }
- }
-
- ///
- /// 添加音频信息
- ///
p_audioInfo:音频信息 - ///
声明1:若启用了音频信息覆盖,当存在相同名称的音频时,新的音频信息将覆盖旧的音频信息 - ///
声明2:默认启用了音频信息覆盖,可通过mIsOverWrite属性设置禁用 - ///
- public void AddAudioInfo(AudioInfo p_audioInfo)
- {
- if (isInit) DoAddAudioInfo(p_audioInfo);
- }
-
- ///
- /// 删除音频信息
- ///
p_audioName:音频名称 - ///
返回值:若删除成功则返回true,否则返回false - ///
- 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
- ///
- /// 在当前Inspector面板中的AudioInfos中的元素数量
- ///
- public int mAudioInfoCount { get => AudioInfos?.Length > 0 ? AudioInfos.Length : 0; }
-
- [SerializeField] private AudioInfo[] AudioInfos;//存储Inspector面板中
-
- ///
- /// 在当前Inspector面板中的AudioInfos中添加一个元素
- ///
- 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();
- }
-
- ///
- /// 在当前Inspector面板中的AudioInfos中删除一个元素
- ///
- public void Delete(int p_index)
- {
- if (AudioInfos?.Length == 1) AudioInfos = Array.Empty
(); - 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
- {
- ///
- /// 音频信息
- ///
- [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;
-
- ///
- /// 音频名称
- ///
- public string mAudioName { get => AudioName; set => AudioName = value; }
-
- ///
- /// 音频音量,默认为1(值属于[0,1])
- ///
- public float mVolume
- {
- get { return Volume; }
- set { if (value >= 0 && value <= 1) Volume = value; }
- }
-
- ///
- /// 音频播放速度,默认为1(值属于[-3,3])
- ///
- public float mPitch
- {
- get { return Pitch; }
- set { if (value >= -3 && value <= 3) Pitch = value; }
- }
-
- ///
- /// 立体声位置,默认为0(值属于[-1,1]),若为-1则完全为左声道,若为1则完全为右声道
- ///
- public float mStereoPan
- {
- get { return StereoPan; }
- set { if (value >= -1 && value <= 1) StereoPan = value; }
- }
-
- ///
- /// 音频优先级,默认为128(值属于[0,256])
- ///
- public int mPriority
- {
- get { return Priority; }
- set { if (value >= 0 && value <= 256) Priority = value; }
- }
-
- ///
- /// 是否在场景启动时进行播放,默认为true
- ///
- public bool mPlayOnAwake { get => PlayOnAwake; set => PlayOnAwake = value; }
-
- ///
- /// 是否循环播放,默认为false
- ///
- public bool mLoop { get => Loop; set => Loop = value; }
-
- ///
- /// 是否启用立体声过渡,默认为false
- ///
- public bool mStereoTransition { get => StereoTransition; set => StereoTransition = value; }
-
- ///
- /// 立体声过渡的每帧时间间隔,默认为0.5(值属于[0.1,5])
- ///
- public float mStereoTransitionTimeSpan
- {
- get { return StereoTransitionTimeSpan; }
- set { if (value >= 0.1f && value <= 5) StereoTransitionTimeSpan = value; }
- }
-
- ///
- /// 立体声过渡值集合
- ///
- public float[] mStereoTransitionValues
- {
- get => StereoTransitionValues;
- set => StereoTransitionValues = value;
- }
-
- ///
- /// AudioSource组件
- ///
- public AudioSource mAudioSource { get => audioSource; set => audioSource = value; }
-
- ///
- /// 立体声过渡协程
- ///
- public IEnumerator mStereoPanTransitionCoroutine { get => stereoPanTransitionCoroutine; }
-
- ///
- /// 是否忽略总音量影响,默认为false
- ///
- 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;//总音量更改事件对象
-
- ///
- /// 将指定的AudioSource组件信息记录在新的AudioInfo实例中并返回它
- ///
p_audioSource:指定的AudioSource组件 - ///
返回值:新的AudioInfo实例 - ///
- 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();
- }
-
- ///
- /// 播放音频
- ///
- 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();
- }
- }
-
- ///
- /// 暂停音频播放
- ///
- public void Pause()
- {
- if (audioSource != null && audioSource.isPlaying)
- {
- audioSource.Pause();
- audioSource.volume = Volume;
- }
- }
-
- ///
- /// 绑定音频管理器
- ///
p_audioManager:音频管理器 - ///
声明1:若有需要可通过该方法将当前的AudioInfo与指定的音频管理器进行绑定 - ///
声明2:绑定后将自动向指定的音频管理器添加当前的AudioInfo - ///
- public void BindAudioManager(AudioManager p_audioManager)
- {
- audioManager = p_audioManager;
- if (audioManager != null)
- {
- audioManager.mTotalVolumeChangedEvents -= totalVolumeChangedEvent;
- audioManager.mTotalVolumeChangedEvents += totalVolumeChangedEvent;
- }
- }
-
- ///
- /// 初始化为默认值
- ///
- 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;
- }
- }
-
- ///
- /// 将当前AudioInfo实例中的信息配置给指定的AudioSource组件
- ///
p_audioSource:指定的AudioSource组件 - ///
- 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;
- }
- }
-
- ///
- /// 将指定的AudioSource组件信息存储在当前AudioInfo实例中
- ///
p_audioSource:指定的AudioSource组件 - ///
- 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;
-
- ///
- /// Inspector面板的数据更改检测
- ///
- public void ValidateCheck()
- {
- AudioClipCheck();
- AudioNameCheck();
- }
-
- //AudioClip检测
- private void AudioClipCheck()
- {
- if (TheAudioClip == null)
- {
- if (!isAudioClipLog)
- {
- Debug.LogWarning("Component:
TheAudioClip is null."); - isAudioClipLog = true;
- }
- }
- else isAudioClipLog = false;
- }
-
- //AudioName检测
- private void AudioNameCheck()
- {
- if (String.IsNullOrEmpty(AudioName))
- {
- if (!isAudioNameLog)
- {
- Debug.LogWarning("Property:
AudioName is empty."); - isAudioNameLog = true;
- }
- }
- else isAudioNameLog = false;
- }
- #endif
- }
- }
AudioSourcePool.cs
- using System.Collections.Generic;
- using UnityEngine;
-
- namespace Tools.AudioManagerAPI
- {
- ///
- /// AudioSource组件池
- ///
- public class AudioSourcePool
- {
- ///
- /// 空闲的AudioSource数量
- ///
- public int mFreeCount { get => audioSources.Count; }
- private Stack
audioSources;//AudioSource组件集合 - private AudioManager audioManager;//AudioManager组件
- private AudioInfo defaultAudioInfo;//默认的AudioInfo
-
- ///
- /// 获取实例(单例模式)
- ///
- public static AudioSourcePool GetInstance()
- {
- return Handler.instance;
- }
-
- ///
- /// 绑定音频管理器
- ///
p_audioManager:音频管理器 - ///
- public void BindAudioManager(AudioManager p_audioManager)
- {
- if (p_audioManager != null) audioManager = p_audioManager;
- }
-
- ///
- /// 获取AudioSource组件
- ///
返回值:AudioSource组件 - ///
- public AudioSource Get()
- {
- return DoGet();
- }
-
- ///
- /// 获取AudioSource组件并按照指定的AudioInfo为之配置属性
- ///
p_audioInfo:指定的AudioInfo - ///
返回值:AudioSource组件 - ///
- public AudioSource Get(AudioInfo p_audioInfo)
- {
- AudioSource v_audioSource = DoGet();
- p_audioInfo?.ShareTo(v_audioSource);
- return v_audioSource;
- }
-
- ///
- /// 归还指定的AudioSource组件
- ///
p_audioSource:指定的AudioSource组件 - ///
- 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
(); - 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
(); - 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
- {
- ///
- /// 数值范围数组工具类
- ///
- public static class NumberRange
- {
- ///
- /// 获取指定范围内指定步长的Float数值数组
- ///
p_start:起始值 - ///
p_end:终点值 - ///
p_step:步长值 - ///
[ContainsEnd]:是否包括终点值,默认为false - ///
返回值:Float[] - ///
- 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;
- }
- }
- }
- }
-

自定义Unity组件AudioManager
如果这篇文章对你有帮助,请给作者点个赞吧!