歡迎加入Unity業内qq交流群:956187480
在項目裡做音頻管理的時候,我們往往是跟資源管理結合起來的,但是這裡我們就單音頻管理展開。其實音頻管理無非就是對幾種固定的音效類型進行加載,播放,暫停,調音等一系列操作。我們隻需要把一些正常操作寫出來滿足大部分的需求即可,對于其他一些特殊功能的比如線上剪切片段,動态調整音素的就需要我們另外實作。
我們一般會把音頻分為四類:以農場類項目為例
1.背景音樂:全局隻有一個,不同的場景不同的時間不同的天氣都會有不同的背景音樂,其主要作用是為了烘托氛圍。是以大部分的背景音樂都是2d的且音量是所有音效中最低的,目的為不突兀不刺激為主。
// 播放背景音樂
public void PlayBackgroundMusic(AudioClip clip, bool isLoop = true, float speed = 1)
// 暫停播放背景音樂
public void PauseBackgroundMusic(bool isGradual = true)
2.人聲配音:全局隻有一個,經常會用在人物對話,npc講話等人聲場景下。當播放新的聲音時會把目前的聲音覆寫掉。
/// 播放單通道音效
public void PlaySingleSound(AudioClip clip, bool isLoop = false, float speed = 1)
/// 暫停播放單通道音效
public void PauseSingleSound(bool isGradual = true)
3.技能ui音效:全局可以多個,在觸發ui的時候或者釋放技能的時候我們的配音是可以多個可重疊的。
/// 播放多通道音效
public void PlayMultipleSound(AudioClip clip, bool isLoop = false, float speed = 1)
/// 停止播放指定的多通道音效
public void StopMultipleSound(AudioClip clip)
4.世界3d音效:全局可以多個,世界音效其實就是3d音,我們一般用在固定位置範圍内的音效,通過設定觸發距離聽到音量不一樣的空間聲音,比如cf的敵人腳步聲。
/// 播放世界音效
public void PlayWorldSound(GameObject attachTarget, AudioClip clip, bool isLoop = false, float speed = 1)
/// 暫停播放指定的世界音效
public void PauseWorldSound(GameObject attachTarget, bool isGradual = true)
using DG.Tweening;
using System.Collections.Generic;
using UnityEngine;
public delegate void AudAction();
/// <summary>
/// 音頻管理者
/// </summary>
[DisallowMultipleComponent]
public sealed class AudioManager : MonoBehaviour
{
/// <summary>
/// 是否靜音初始值【請勿在代碼中修改】
/// </summary>
public bool MuteDefault = false;
/// <summary>
/// 背景音樂優先級初始值【請勿在代碼中修改】
/// </summary>
public int BackgroundPriorityDefault = 0;
/// <summary>
/// 單通道音效優先級初始值【請勿在代碼中修改】
/// </summary>
public int SinglePriorityDefault = 10;
/// <summary>
/// 多通道音效優先級初始值【請勿在代碼中修改】
/// </summary>
public int MultiplePriorityDefault = 20;
/// <summary>
/// 世界音效優先級初始值【請勿在代碼中修改】
/// </summary>
public int WorldPriorityDefault = 30;
/// <summary>
/// 背景音樂音量初始值【請勿在代碼中修改】
/// </summary>
public float BackgroundVolumeDefault = 0.6f;
/// <summary>
/// 單通道音效音量初始值【請勿在代碼中修改】
/// </summary>
public float SingleVolumeDefault = 1;
/// <summary>
/// 多通道音效音量初始值【請勿在代碼中修改】
/// </summary>
public float MultipleVolumeDefault = 1;
/// <summary>
/// 世界音效音量初始值【請勿在代碼中修改】
/// </summary>
public float WorldVolumeDefault = 1;
/// <summary>
/// 單通道音效播放結束事件
/// </summary>
public event AudAction SingleSoundEndOfPlayEvent;
private AudioSource _backgroundAudio;
private AudioSource _singleAudio;
private List<AudioSource> _multipleAudios = new List<AudioSource>();
private Dictionary<GameObject, AudioSource> _worldAudios = new Dictionary<GameObject, AudioSource>();
private bool _singleSoundPlayDetector = false;
private bool _isMute = false;
private int _backgroundPriority = 0;
private int _singlePriority = 10;
private int _multiplePriority = 20;
private int _worldPriority = 30;
private float _backgroundVolume = 0.6f;
private float _singleVolume = 1;
private float _multipleVolume = 1;
private float _worldVolume = 1;
public void Awake()
{
_backgroundAudio = CreateAudioSource("BackgroundAudio", BackgroundPriorityDefault, BackgroundVolumeDefault, 1, 0);
_singleAudio = CreateAudioSource("SingleAudio", SinglePriorityDefault, SingleVolumeDefault, 1, 0);
Mute = MuteDefault;
BackgroundPriority = BackgroundPriorityDefault;
SinglePriority = SinglePriorityDefault;
MultiplePriority = MultiplePriorityDefault;
WorldPriority = WorldPriorityDefault;
BackgroundVolume = BackgroundVolumeDefault;
SingleVolume = SingleVolumeDefault;
MultipleVolume = MultipleVolumeDefault;
WorldVolume = WorldVolumeDefault;
}
public void Start()
{
StopBackgroundMusic();
StopSingleSound();
StopAllMultipleSound();
StopAllWorldSound();
}
public void Update()
{
if (_singleSoundPlayDetector)
{
if (!_singleAudio.isPlaying)
{
_singleSoundPlayDetector = false;
SingleSoundEndOfPlayEvent?.Invoke();
}
}
}
/// <summary>
/// 靜音
/// </summary>
public bool Mute
{
get
{
return _isMute;
}
set
{
if (_isMute != value)
{
_isMute = value;
_backgroundAudio.mute = _isMute;
_singleAudio.mute = _isMute;
for (int i = 0; i < _multipleAudios.Count; i++)
{
_multipleAudios[i].mute = _isMute;
}
foreach (var audio in _worldAudios)
{
audio.Value.mute = _isMute;
}
}
}
}
/// <summary>
/// 背景音樂優先級
/// </summary>
public int BackgroundPriority
{
get
{
return _backgroundPriority;
}
set
{
if (_backgroundPriority != value)
{
_backgroundPriority = value;
_backgroundAudio.priority = _backgroundPriority;
}
}
}
/// <summary>
/// 單通道音效優先級
/// </summary>
public int SinglePriority
{
get
{
return _singlePriority;
}
set
{
if (_singlePriority != value)
{
_singlePriority = value;
_singleAudio.priority = _singlePriority;
}
}
}
/// <summary>
/// 多通道音效優先級
/// </summary>
public int MultiplePriority
{
get
{
return _multiplePriority;
}
set
{
if (_multiplePriority != value)
{
_multiplePriority = value;
for (int i = 0; i < _multipleAudios.Count; i++)
{
_multipleAudios[i].priority = _multiplePriority;
}
}
}
}
/// <summary>
/// 世界音效優先級
/// </summary>
public int WorldPriority
{
get
{
return _worldPriority;
}
set
{
if (_worldPriority != value)
{
_worldPriority = value;
foreach (var audio in _worldAudios)
{
audio.Value.priority = _worldPriority;
}
}
}
}
/// <summary>
/// 背景音樂音量
/// </summary>
public float BackgroundVolume
{
get
{
return _backgroundVolume;
}
set
{
if (!Mathf.Approximately(_backgroundVolume, value))
{
_backgroundVolume = value;
_backgroundAudio.volume = _backgroundVolume;
}
}
}
/// <summary>
/// 單通道音效音量
/// </summary>
public float SingleVolume
{
get
{
return _singleVolume;
}
set
{
if (!Mathf.Approximately(_singleVolume, value))
{
_singleVolume = value;
_singleAudio.volume = _singleVolume;
}
}
}
/// <summary>
/// 多通道音效音量
/// </summary>
public float MultipleVolume
{
get
{
return _multipleVolume;
}
set
{
if (!Mathf.Approximately(_multipleVolume, value))
{
_multipleVolume = value;
for (int i = 0; i < _multipleAudios.Count; i++)
{
_multipleAudios[i].volume = _multipleVolume;
}
}
}
}
/// <summary>
/// 世界音效音量
/// </summary>
public float WorldVolume
{
get
{
return _worldVolume;
}
set
{
if (!Mathf.Approximately(_worldVolume, value))
{
_worldVolume = value;
foreach (var audio in _worldAudios)
{
audio.Value.volume = _worldVolume;
}
}
}
}
/// <summary>
/// 播放背景音樂
/// </summary>
/// <param name="clip">音樂剪輯</param>
/// <param name="isLoop">是否循環</param>
/// <param name="speed">播放速度</param>
public void PlayBackgroundMusic(AudioClip clip, bool isLoop = true, float speed = 1)
{
if (_backgroundAudio.isPlaying)
{
_backgroundAudio.Stop();
}
_backgroundAudio.clip = clip;
_backgroundAudio.loop = isLoop;
_backgroundAudio.pitch = speed;
_backgroundAudio.Play();
}
/// <summary>
/// 暫停播放背景音樂
/// </summary>
/// <param name="isGradual">是否漸進式</param>
public void PauseBackgroundMusic(bool isGradual = true)
{
if (isGradual)
{
_backgroundAudio.DOFade(0, 2).OnComplete(() =>
{
_backgroundAudio.volume = BackgroundVolume;
_backgroundAudio.Pause();
});
}
else
{
_backgroundAudio.Pause();
}
}
/// <summary>
/// 恢複播放背景音樂
/// </summary>
/// <param name="isGradual">是否漸進式</param>
public void UnPauseBackgroundMusic(bool isGradual = true)
{
if (isGradual)
{
_backgroundAudio.UnPause();
_backgroundAudio.volume = 0;
_backgroundAudio.DOFade(BackgroundVolume, 2);
}
else
{
_backgroundAudio.UnPause();
}
}
/// <summary>
/// 停止播放背景音樂
/// </summary>
public void StopBackgroundMusic()
{
if (_backgroundAudio.isPlaying)
{
_backgroundAudio.Stop();
}
}
/// <summary>
/// 播放單通道音效
/// </summary>
/// <param name="clip">音樂剪輯</param>
/// <param name="isLoop">是否循環</param>
/// <param name="speed">播放速度</param>
public void PlaySingleSound(AudioClip clip, bool isLoop = false, float speed = 1)
{
if (_singleAudio.isPlaying)
{
_singleAudio.Stop();
}
_singleAudio.clip = clip;
_singleAudio.loop = isLoop;
_singleAudio.pitch = speed;
_singleAudio.Play();
_singleSoundPlayDetector = true;
}
/// <summary>
/// 暫停播放單通道音效
/// </summary>
/// <param name="isGradual">是否漸進式</param>
public void PauseSingleSound(bool isGradual = true)
{
if (isGradual)
{
_singleAudio.DOFade(0, 2).OnComplete(() =>
{
_singleAudio.volume = SingleVolume;
_singleAudio.Pause();
});
}
else
{
_singleAudio.Pause();
}
}
/// <summary>
/// 恢複播放單通道音效
/// </summary>
/// <param name="isGradual">是否漸進式</param>
public void UnPauseSingleSound(bool isGradual = true)
{
if (isGradual)
{
_singleAudio.UnPause();
_singleAudio.volume = 0;
_singleAudio.DOFade(SingleVolume, 2);
}
else
{
_singleAudio.UnPause();
}
}
/// <summary>
/// 停止播放單通道音效
/// </summary>
public void StopSingleSound()
{
if (_singleAudio.isPlaying)
{
_singleAudio.Stop();
}
}
/// <summary>
/// 播放多通道音效
/// </summary>
/// <param name="clip">音樂剪輯</param>
/// <param name="isLoop">是否循環</param>
/// <param name="speed">播放速度</param>
public void PlayMultipleSound(AudioClip clip, bool isLoop = false, float speed = 1)
{
AudioSource audio = ExtractIdleMultipleAudioSource();
audio.clip = clip;
audio.loop = isLoop;
audio.pitch = speed;
audio.Play();
}
/// <summary>
/// 停止播放指定的多通道音效
/// </summary>
/// <param name="clip">音樂剪輯</param>
public void StopMultipleSound(AudioClip clip)
{
for (int i = 0; i < _multipleAudios.Count; i++)
{
if (_multipleAudios[i].isPlaying)
{
if (_multipleAudios[i].clip == clip)
{
_multipleAudios[i].Stop();
}
}
}
}
/// <summary>
/// 停止播放所有多通道音效
/// </summary>
public void StopAllMultipleSound()
{
for (int i = 0; i < _multipleAudios.Count; i++)
{
if (_multipleAudios[i].isPlaying)
{
_multipleAudios[i].Stop();
}
}
}
/// <summary>
/// 銷毀所有閑置中的多通道音效的音源
/// </summary>
public void ClearIdleMultipleAudioSource()
{
for (int i = 0; i < _multipleAudios.Count; i++)
{
if (!_multipleAudios[i].isPlaying)
{
AudioSource audio = _multipleAudios[i];
_multipleAudios.RemoveAt(i);
i -= 1;
Destroy(audio.gameObject);
}
}
}
/// <summary>
/// 播放世界音效
/// </summary>
/// <param name="attachTarget">附加目标</param>
/// <param name="clip">音樂剪輯</param>
/// <param name="isLoop">是否循環</param>
/// <param name="speed">播放速度</param>
public void PlayWorldSound(GameObject attachTarget, AudioClip clip, bool isLoop = false, float speed = 1)
{
if (_worldAudios.ContainsKey(attachTarget))
{
AudioSource audio = _worldAudios[attachTarget];
if (audio.isPlaying)
{
audio.Stop();
}
audio.clip = clip;
audio.loop = isLoop;
audio.pitch = speed;
audio.Play();
}
else
{
AudioSource audio = AttachAudioSource(attachTarget, WorldPriority, WorldVolume, 1, 1);
_worldAudios.Add(attachTarget, audio);
audio.clip = clip;
audio.loop = isLoop;
audio.pitch = speed;
audio.Play();
}
}
/// <summary>
/// 暫停播放指定的世界音效
/// </summary>
/// <param name="attachTarget">附加目标</param>
/// <param name="isGradual">是否漸進式</param>
public void PauseWorldSound(GameObject attachTarget, bool isGradual = true)
{
if (_worldAudios.ContainsKey(attachTarget))
{
AudioSource audio = _worldAudios[attachTarget];
if (isGradual)
{
audio.DOFade(0, 2).OnComplete(() =>
{
audio.volume = WorldVolume;
audio.Pause();
});
}
else
{
audio.Pause();
}
}
}
/// <summary>
/// 恢複播放指定的世界音效
/// </summary>
/// <param name="attachTarget">附加目标</param>
/// <param name="isGradual">是否漸進式</param>
public void UnPauseWorldSound(GameObject attachTarget, bool isGradual = true)
{
if (_worldAudios.ContainsKey(attachTarget))
{
AudioSource audio = _worldAudios[attachTarget];
if (isGradual)
{
audio.UnPause();
audio.volume = 0;
audio.DOFade(WorldVolume, 2);
}
else
{
audio.UnPause();
}
}
}
/// <summary>
/// 停止播放所有的世界音效
/// </summary>
public void StopAllWorldSound()
{
foreach (var audio in _worldAudios)
{
if (audio.Value.isPlaying)
{
audio.Value.Stop();
}
}
}
/// <summary>
/// 銷毀所有閑置中的世界音效的音源
/// </summary>
public void ClearIdleWorldAudioSource()
{
HashSet<GameObject> removeSet = new HashSet<GameObject>();
foreach (var audio in _worldAudios)
{
if (!audio.Value.isPlaying)
{
removeSet.Add(audio.Key);
Destroy(audio.Value);
}
}
foreach (var item in removeSet)
{
_worldAudios.Remove(item);
}
}
//建立一個音源
private AudioSource CreateAudioSource(string name, int priority, float volume, float speed, float spatialBlend)
{
GameObject audioObj = new GameObject(name);
audioObj.transform.SetParent(transform);
audioObj.transform.localPosition = Vector3.zero;
audioObj.transform.localRotation = Quaternion.identity;
audioObj.transform.localScale = Vector3.one;
AudioSource audio = audioObj.AddComponent<AudioSource>();
audio.playOnAwake = false;
audio.priority = priority;
audio.volume = volume;
audio.pitch = speed;
audio.spatialBlend = spatialBlend;
audio.mute = _isMute;
return audio;
}
//附加一個音源
private AudioSource AttachAudioSource(GameObject target, int priority, float volume, float speed, float spatialBlend)
{
AudioSource audio = target.AddComponent<AudioSource>();
audio.playOnAwake = false;
audio.priority = priority;
audio.volume = volume;
audio.pitch = speed;
audio.spatialBlend = spatialBlend;
audio.mute = _isMute;
return audio;
}
//提取閑置中的多通道音源
private AudioSource ExtractIdleMultipleAudioSource()
{
for (int i = 0; i < _multipleAudios.Count; i++)
{
if (!_multipleAudios[i].isPlaying)
{
return _multipleAudios[i];
}
}
AudioSource audio = CreateAudioSource("MultipleAudio", MultiplePriority, MultipleVolume, 1, 0);
_multipleAudios.Add(audio);
return audio;
}
}