一个简单的音频管理器,可以播放bgm和音效,声音音量控制
2022.6.10 更新
随机播放,淡入淡出
using System;
using System.Collections;
using System.Collections.Generic;
using UnityEngine;
using XFramework.CoroutineTool;
using XFramework.Singleton;
namespace XFramework.Audio
{
public class AudioPlayHandler
{
/// <summary>
/// handler的Id
/// </summary>
public int id;
/// <summary>
/// 音频名字
/// </summary>
public string clipName;
/// <summary>
/// 音频对象
/// </summary>
public AudioSource audioSource;
}
public class AudioManager : SingletonTemplate<AudioManager>
{
public Func<string, AudioClip> LoadFunc;
private GameObject audioRootGo;
/// <summary>
/// AudioSource缓存池
/// </summary>
private Queue<AudioSource> cacheAudioSource;
/// <summary>
/// 正在播放的所有音频对象
/// </summary>
private Dictionary<int, AudioPlayHandler> dicAudioSource;
/// <summary>
/// 当前的BGM的Id
/// </summary>
private int curPlayBgmId;
#region 音量
private int volumeAudio;
private int volumeBGM;
public int VolumeAudio
{
get => volumeAudio;
set => volumeAudio = value;
}
public int VolumeBGM
{
get => volumeBGM;
set
{
volumeBGM = value;
SetCurPlayBgmAudioSource();
}
}
#endregion
public void OnInit(Func<string, AudioClip> loadFunc)
{
cacheAudioSource = new Queue<AudioSource>(5);
dicAudioSource = new Dictionary<int, AudioPlayHandler>();
volumeAudio = 1;
volumeBGM = 1;
curPlayBgmId = -1;
audioRootGo = new GameObject("AudioRoot");
GameObject.DontDestroyOnLoad(audioRootGo);
this.LoadFunc = loadFunc;
}
public void OnDisposs()
{
cacheAudioSource = null;
dicAudioSource = null;
curPlayBgmId = -1;
GameObject.Destroy(audioRootGo);
audioRootGo = null;
}
public void SetCurPlayBgmAudioSource()
{
if (curPlayBgmId == -1) return;
AudioPlayHandler handler;
if (dicAudioSource.TryGetValue(curPlayBgmId, out handler))
{
handler.audioSource.volume = volumeBGM;
}
}
/// <summary>
/// 随机播放一个音频
/// </summary>
/// <param name="audioNames"></param>
public AudioPlayHandler RandomPlayAudio(string[] audioNames)
{
if (audioNames == null) return null;
if (audioNames.Length == 0) return null;
var randomValue = UnityEngine.Random.Range(0, audioNames.Length);
var audioName = audioNames[randomValue];
return PlayAudio(audioName);
}
/// <summary>
/// 淡入
/// </summary>
public void FadeInAudio(int audioId, float fadeTime = 1, Action onComplete = null)
{
FadeInAudio(audioId, fadeTime).Start();
IEnumerator FadeInAudio(int audioId, float fadeTime)
{
AudioPlayHandler handler;
dicAudioSource.TryGetValue(audioId, out handler);
if (handler == null)
{
onComplete?.Invoke();
yield break;
}
var audioSource = handler.audioSource;
float startTime = Time.time;
while (true)
{
if (Time.time > startTime + fadeTime)
{
break;
}
yield return null;
//音量从 0 - 1
var volume = (Time.time - startTime) / fadeTime;
volume = Mathf.Clamp(volume, 0, 1);
audioSource.volume = volume;
audioSource.mute = false;
}
audioSource.volume = 1;
onComplete?.Invoke();
}
}
/// <summary>
/// 淡出
/// </summary>
public void FadeOutAudio(int lastAudioId, string newClipName, bool isLoop, float fadeTime = 1, Action<string, bool> onComplete = null)
{
FadeOutAudio(lastAudioId, newClipName, fadeTime).Start();
IEnumerator FadeOutAudio(int lastAudioId, string newClipName, float fadeTime)
{
AudioPlayHandler handler;
dicAudioSource.TryGetValue(lastAudioId, out handler);
if (handler == null)
{
onComplete?.Invoke(newClipName, isLoop);
yield break;
}
var audioSource = handler.audioSource;
float startTime = Time.time;
while (true)
{
if (Time.time > startTime + fadeTime)
{
break;
}
yield return null;
//音量从 1 - 0
var volume = (Time.time - startTime) / fadeTime;
volume = Mathf.Clamp(volume, 0, 1);
audioSource.volume = 1 - volume;
}
audioSource.volume = 0;
onComplete?.Invoke(newClipName, isLoop);
}
}
/// <summary>
/// 背景音乐
/// </summary>
/// <param name="clipName"></param>
/// <param name="loop"></param>
public void PlayBGM(string clipName, bool loop = false)
{
//当前没有bgm
if (curPlayBgmId == -1)
{
PlayNewBgm(clipName, loop);
}
else
{
//之前的bgm淡出
FadeOutAudio(curPlayBgmId, clipName, loop, 1, (newClipName, loop) =>
{
PlayNewBgm(newClipName, loop);
});
}
void PlayNewBgm(string clipName, bool loop = false)
{
//之前的bgm停止播放
StopBgm();
//播放新的bgm
var handler = Play(clipName, loop, (source) =>
{
source.volume = VolumeBGM;
source.spatialBlend = 0;
source.mute = true;
});
curPlayBgmId = handler.id;
//新的bgm淡入
FadeInAudio(curPlayBgmId);
}
}
/// <summary>
/// 普通音效
/// </summary>
/// <param name="clipName"></param>
/// <param name="loop"></param>
/// <param name="pitch">音频速度</param>
public AudioPlayHandler PlayAudio(string clipName, bool loop = false, float pitch = 1)
{
return Play(clipName, loop, (source) =>
{
source.volume = VolumeAudio;
source.pitch = pitch;
source.spatialBlend = 0;
});
}
/// <summary>
/// 3D音效
/// </summary>
/// <param name="clipName"></param>
/// <param name="position"></param>
public AudioPlayHandler Play3DAudio(string clipName, Vector3 position)
{
return Play(clipName, false, (source) =>
{
source.volume = VolumeAudio;
source.spatialBlend = 1;
source.transform.position = position;
});
}
/// <summary>
/// 停止所有音效,包括BGM
/// </summary>
public void StopAllAudio()
{
foreach (var audio in dicAudioSource)
{
StopPlayAudio(audio.Key);
}
}
private AudioPlayHandler Play(string clipName, bool isLoop, Action<AudioSource> onSetting = null)
{
AudioSource source = GetAudioSource();
onSetting?.Invoke(source);
var clip = LoadFunc(clipName);
if (clip == null)
{
LogTool.LogError("can not find the clip: " + clipName);
return null;
}
source.clip = clip;
source.loop = isLoop;
source.Play();
source.gameObject.name = clipName;
var audioId = IdGeneraterManager.Ins.GenerateId();
var audioHandler = new AudioPlayHandler()
{
clipName = clipName,
id = audioId,
audioSource = source,
};
dicAudioSource.Add(audioId, audioHandler);
//非循环播放的主动停止
if (isLoop)
{
}
else
{
DelayTool.DelayDo(source.clip.length,
(obj) =>
{
var audioId = (int)obj;
StopPlayAudio(audioId);
}, audioId);
}
LogTool.Log($"play audio, audio name:{clipName}, loop:{source.loop}", Log.LogColor.Green);
return audioHandler;
}
public void StopPlayAudio(int audioId)
{
//正在播放
if (dicAudioSource.ContainsKey(audioId))
{
AudioPlayHandler handler = dicAudioSource[audioId];
var audioSource = handler.audioSource;
//未在缓存池里
if (!cacheAudioSource.Contains(audioSource))
{
audioSource.Stop();
audioSource.gameObject.SetActive(false);
cacheAudioSource.Enqueue(audioSource);
}
dicAudioSource.Remove(audioId);
}
}
private void StopBgm()
{
if (curPlayBgmId != -1)
{
StopPlayAudio(curPlayBgmId);
curPlayBgmId = -1;
}
}
private AudioSource GetAudioSource()
{
AudioSource source;
//优先从缓存池里取
if (cacheAudioSource.Count > 0)
{
source = cacheAudioSource.Dequeue();
source.gameObject.SetActive(true);
}
else
{
GameObject gameObject = new GameObject("audio");
gameObject.transform.parent = audioRootGo.transform;
source = gameObject.AddComponent<AudioSource>();
source.spatialBlend = 1f;
source.loop = false;
}
return source;
}
}
}