1.音频管理工具
1.1简单使用
双击
1.2 实践案例
using System.Collections;
using System.Collections.Generic;
using UnityEngine;
using UnityEngine.Audio;
using UnityEngine.SceneManagement;
using static MoreMountains.Tools.MMSoundManager;
namespace MoreMountains.Tools
{
public class TestSoundManager : MonoBehaviour
{
[Header("MMSoundManagerSoundPlayEvent")]
public AudioClip SoundClipLoop;
public MMSoundManager.MMSoundManagerTracks UseTracks;
public AudioMixerGroup OtherAudioGroup;
[MMInspectorButton("TestSoundManagerSoundPlayEvent")]
public bool TestSoundManagerSoundPlayEventButton;
protected virtual void TestSoundManagerSoundPlayEvent()
{
MMSoundManagerPlayOptions options = MMSoundManagerPlayOptions.Default;
options.Loop = true;
options.Location = Vector3.zero;
options.MmSoundManagerTrack = UseTracks;
MMSoundManagerSoundPlayEvent.Trigger(SoundClipLoop, options);
}
[MMInspectorButton("TestSoundManagerSoundPlayEventSound1")]
public bool TestSoundManagerSoundPlayEventSound1Button;
protected virtual void TestSoundManagerSoundPlayEventSound1()
{
MMSoundManagerPlayOptions options = MMSoundManagerPlayOptions.Default;
options.Loop = true;
options.Location = Vector3.zero;
options.MmSoundManagerTrack = UseTracks;
options.ID = 1;
MMSoundManagerSoundPlayEvent.Trigger(SoundClipLoop, options);
}
[MMInspectorButton("TestSoundManagerSoundPlayPersistent")]
public bool TestSoundManagerSoundPlayPersistentButton;
protected virtual void TestSoundManagerSoundPlayPersistent()
{
MMSoundManagerPlayOptions options = MMSoundManagerPlayOptions.Default;
options.Loop = true;
options.Location = Vector3.zero;
options.MmSoundManagerTrack = UseTracks;
options.Persistent = true;
MMSoundManagerSoundPlayEvent.Trigger(SoundClipLoop, options);
}
[MMInspectorButton("TestUseOtherAudioGrup")]
public bool TestUseOtherAudioGrupButton;
protected virtual void TestUseOtherAudioGrup()
{
MMSoundManagerPlayOptions options = MMSoundManagerPlayOptions.Default;
options.Loop = true;
options.Location = Vector3.zero;
options.MmSoundManagerTrack = MMSoundManager.MMSoundManagerTracks.Music;
options.AudioGroup = OtherAudioGroup;
MMSoundManagerSoundPlayEvent.Trigger(SoundClipLoop, options);
}
[MMInspectorButton("TestSoundManagerSoundPlayNoloop")]
public bool TestSoundManagerSoundPlayNoloopButton;
protected virtual void TestSoundManagerSoundPlayNoloop()
{
MMSoundManagerPlayOptions options = MMSoundManagerPlayOptions.Default;
options.Loop = true;
options.Location = Vector3.zero;
options.MmSoundManagerTrack = UseTracks;
options.Loop = false;
MMSoundManagerSoundPlayEvent.Trigger(SoundClipLoop, options);
}
[MMInspectorButton("TestFaderSound")]
public bool TestFaderSoundButton;
protected virtual void TestFaderSound()
{
MMSoundManagerPlayOptions options = MMSoundManagerPlayOptions.Default;
options.Loop = true;
options.Location = Vector3.zero;
options.MmSoundManagerTrack = UseTracks;
options.Fade = true;
options.FadeInitialVolume = 1;
options.Volume = 0;
options.FadeDuration = 10;
MMSoundManagerSoundPlayEvent.Trigger(SoundClipLoop, options);
}
public AudioClip SoundClipNoLoop;
[MMInspectorButton("TestSoundManagerNoLoop")]
public bool TestSoundManagerNoLoopButton;
protected virtual void TestSoundManagerNoLoop()
{
MMSoundManagerPlayOptions options = MMSoundManagerPlayOptions.Default;
options.Loop = false;
options.Location = Vector3.zero;
options.MmSoundManagerTrack = MMSoundManager.MMSoundManagerTracks.Sfx;
MMSoundManagerSoundPlayEvent.Trigger(SoundClipNoLoop, options);
}
[MMInspectorButton("TestSoloSingleTrack")]
public bool TestSoloSingleTrackButton;
protected virtual void TestSoloSingleTrack()
{
MMSoundManagerPlayOptions options = MMSoundManagerPlayOptions.Default;
options.Loop = false;
options.Location = Vector3.zero;
options.MmSoundManagerTrack = MMSoundManager.MMSoundManagerTracks.Music;
options.SoloSingleTrack = true;
options.AutoUnSoloOnEnd = true;
MMSoundManagerSoundPlayEvent.Trigger(SoundClipNoLoop, options);
}
[MMInspectorButton("TestSoloAllTracks")]
public bool TestSoloAllTracksButton;
protected virtual void TestSoloAllTracks()
{
MMSoundManagerPlayOptions options = MMSoundManagerPlayOptions.Default;
options.Loop = false;
options.Location = Vector3.zero;
options.MmSoundManagerTrack = MMSoundManager.MMSoundManagerTracks.Sfx;
options.SoloAllTracks = true;
options.AutoUnSoloOnEnd = true;
MMSoundManagerSoundPlayEvent.Trigger(SoundClipNoLoop, options);
}
[Header("MMSoundManagerTrackEvent")]
public MMSoundManager.MMSoundManagerTracks MMSoundManagerTrackEventTestTracks = MMSoundManagerTracks.Music;
public float TestSetVolume = 1f;
[MMInspectorButton("MuteTrack")]
public bool MuteTrackButton;
protected virtual void MuteTrack()
{
MMSoundManagerTrackEvent.Trigger(MMSoundManagerTrackEventTypes.MuteTrack, MMSoundManagerTracks.Music);
}
[MMInspectorButton("UnmuteTrack")]
public bool UnmuteTrackButton;
protected virtual void UnmuteTrack()
{
MMSoundManagerTrackEvent.Trigger(MMSoundManagerTrackEventTypes.UnmuteTrack, MMSoundManagerTracks.Music, 0);
}
[MMInspectorButton("SetVolumeTrack")]
public bool SetVolumeTrackButton;
protected virtual void SetVolumeTrack()
{
MMSoundManagerTrackEvent.Trigger(MMSoundManagerTrackEventTypes.SetVolumeTrack, MMSoundManagerTrackEventTestTracks, TestSetVolume);
}
[MMInspectorButton("PauseTrack")]
public bool PauseTrackButton;
protected virtual void PauseTrack()
{
MMSoundManagerTrackEvent.Trigger(MMSoundManagerTrackEventTypes.PauseTrack, MMSoundManagerTracks.Music, 0.5f);
}
[MMInspectorButton("StopTrack")]
public bool StopTrackButton;
protected virtual void StopTrack()
{
MMSoundManagerTrackEvent.Trigger(MMSoundManagerTrackEventTypes.StopTrack, MMSoundManagerTracks.Music, 0.5f);
}
[MMInspectorButton("PlayTrack")]
public bool PlayTrackButton;
protected virtual void PlayTrack()
{
MMSoundManagerTrackEvent.Trigger(MMSoundManagerTrackEventTypes.PlayTrack, MMSoundManagerTracks.Music, 0.5f);
}
[MMInspectorButton("FreeTrack")]
public bool FreeTrackButton;
protected virtual void FreeTrack()
{
MMSoundManagerTrackEvent.Trigger(MMSoundManagerTrackEventTypes.FreeTrack, MMSoundManagerTracks.Music, 0.5f);
}
[Header("MMSoundManagerEvent")]
[MMInspectorButton("SaveSettings")]
public bool SaveSettingsButton;
protected virtual void SaveSettings()
{
MMSoundManagerEvent.Trigger(MMSoundManagerEventTypes.SaveSettings);
}
[MMInspectorButton("LoadSettings")]
public bool LoadSettingsButton;
protected virtual void LoadSettings()
{
MMSoundManagerEvent.Trigger(MMSoundManagerEventTypes.LoadSettings);
}
[MMInspectorButton("ResetSettings")]
public bool ResetSettingsButton;
protected virtual void ResetSettings()
{
MMSoundManagerEvent.Trigger(MMSoundManagerEventTypes.ResetSettings);
}
[Header("MMSoundManagerSoundControlEvent")]
[MMInspectorButton("Pause")]
public bool PauseButton;
protected virtual void Pause()
{
MMSoundManagerSoundControlEvent.Trigger(MMSoundManagerSoundControlEventTypes.Pause, 1);
}
[MMInspectorButton("Resume")]
public bool ResumeButton;
protected virtual void Resume()
{
MMSoundManagerSoundControlEvent.Trigger(MMSoundManagerSoundControlEventTypes.Resume, 1);
}
[MMInspectorButton("Stop")]
public bool StopButton;
protected virtual void Stop()
{
MMSoundManagerSoundControlEvent.Trigger(MMSoundManagerSoundControlEventTypes.Stop, 1);
}
[MMInspectorButton("Free")]
public bool FreeButton;
protected virtual void Free()
{
MMSoundManagerSoundControlEvent.Trigger(MMSoundManagerSoundControlEventTypes.Free,1);
}
[Header("MMSoundManagerSoundFadeEvent")]
public MMTweenType TestSoundFadeEventTweenType;
[MMInspectorButton("TestSoundFadeEvent")]
public bool TestSoundFadeEventButton;
protected virtual void TestSoundFadeEvent()
{
MMSoundManagerSoundFadeEvent.Trigger(0, 10, 0, TestSoundFadeEventTweenType);
}
[Header("MMSoundManagerAllSoundsControlEvent")]
[MMInspectorButton("AllSoundsPause")]
public bool AllSoundsPauseButton;
protected virtual void AllSoundsPause()
{
MMSoundManagerAllSoundsControlEvent.Trigger(MMSoundManagerAllSoundsControlEventTypes.Pause);
}
[MMInspectorButton("AllSoundsPlay")]
public bool AllSoundsPlayButton;
protected virtual void AllSoundsPlay()
{
MMSoundManagerAllSoundsControlEvent.Trigger(MMSoundManagerAllSoundsControlEventTypes.Play);
}
[MMInspectorButton("AllSoundsStop")]
public bool AllSoundsStopButton;
protected virtual void AllSoundsStop()
{
MMSoundManagerAllSoundsControlEvent.Trigger(MMSoundManagerAllSoundsControlEventTypes.Stop);
}
[MMInspectorButton("AllSoundsFree")]
public bool AllSoundsFreeButton;
protected virtual void AllSoundsFree()
{
MMSoundManagerAllSoundsControlEvent.Trigger(MMSoundManagerAllSoundsControlEventTypes.Free);
}
[MMInspectorButton("AllSoundsFreeAllButPersistent")]
public bool AllSoundsFreeAllButPersistentButton;
protected virtual void AllSoundsFreeAllButPersistent()
{
MMSoundManagerAllSoundsControlEvent.Trigger(MMSoundManagerAllSoundsControlEventTypes.FreeAllButPersistent);
}
[MMInspectorButton("AllSoundsFreeAllLooping")]
public bool AllSoundsFreeAllLoopingButton;
protected virtual void AllSoundsFreeAllLooping()
{
MMSoundManagerAllSoundsControlEvent.Trigger(MMSoundManagerAllSoundsControlEventTypes.FreeAllLooping);
}
[Header("MMSoundManagerTrackFadeEvent")]
public MMSoundManager.MMSoundManagerTracks TrackFadeEventTracks = MMSoundManagerTracks.Music;
public MMTweenType TrackFadeEventTweenType;
[MMInspectorButton("TestTrackFadeEvent")]
public bool TestTrackFadeEventButton;
protected virtual void TestTrackFadeEvent()
{
MMSoundManagerTrackFadeEvent.Trigger(TrackFadeEventTracks, 10, 0, TrackFadeEventTweenType);
}
[Header("MMSfxEvent")]
public AudioClip OnMMSfxEventClip;
[MMInspectorButton("TestMMSfxEvent")]
public bool TestMMSfxEventButton;
protected virtual void TestMMSfxEvent()
{
MMSfxEvent.Trigger(OnMMSfxEventClip);
}
[Header("SceneChange")]
public string NextSceneName;
[MMInspectorButton("TestSceneChange")]
public bool TestSceneChangeButton;
protected virtual void TestSceneChange()
{
SceneManager.LoadScene(NextSceneName);
}
public void GoToNextLevel()
{
SceneManager.LoadScene(NextSceneName);
}
}
}
using System;
using System.Collections;
using System.Collections.Generic;
using Unity.VisualScripting;
using UnityEngine;
using UnityEngine.Audio;
using UnityEngine.SceneManagement;
namespace MoreMountains.Tools
{
/// <summary>
/// 一个简单而强大的声音管理器,让您在播放声音时考虑到基于事件的方法和性能。
///
///特点:
///
///-播放/停止/暂停/恢复/自由声音
///-完全控制:环路、音量、音高、摇摄、空间混合、旁路、优先级、混响、多普勒电平、扩展、滚降模式、距离
///-2D和3D空间支持
///-内置池,自动回收一组音频源以获得最佳性能
///-内置混音器和组,带有现成的曲目(Master、Music、SFX、UI),并可根据需要在更多组上播放
///-停止/暂停/恢复/释放整个曲目
///-一次停止/暂停/恢复/释放所有声音
///-将整首曲目静音/设置音量
///-保存和加载设置,内置自动保存/自动加载机制
///-淡入/淡出声音
///-淡入/淡出轨道
///-独奏模式:播放一首或所有曲目静音的声音,然后自动取消静音
///-PlayOptions结构体
///-允许声音在场景加载和场景之间持续存在的选项
///-曲目检查器控件(音量、静音、取消静音、播放、暂停、停止、恢复、自由、声音数量)
///-MMSfx活动
///-MMSoundManagerEvents:静音音轨、控制音轨、保存、加载、重置、停止持续声音
/// </summary>
[AddComponentMenu("More Mountains/Tools/Audio/MMSoundManager")]
public class MMSoundManager : MMPersistentSingleton<MMSoundManager>,
MMEventListener<MMSoundManagerTrackEvent>,//音轨
MMEventListener<MMSoundManagerEvent>,
MMEventListener<MMSoundManagerSoundControlEvent>,//控制
MMEventListener<MMSoundManagerSoundFadeEvent>,//淡入淡出
MMEventListener<MMSoundManagerAllSoundsControlEvent>,//所有音乐管理
MMEventListener<MMSoundManagerTrackFadeEvent>//音频轨道
{
/// 管理音轨的可能方法
public enum MMSoundManagerTracks { Sfx, Music, UI, Master, Other }
[Header("设置")]
[Tooltip("当前声音设置")]
public MMSoundManagerSettingsSO settingsSo;
[Header("池")]
[Tooltip("AudioSource池的大小,这是一个将被回收的现成资源的储备。应该大约等于您希望一次播放的最大声音量")]
public int AudioSourcePoolSize = 10;
[Tooltip("池是否可以扩展(按需创建新的音频源)。在一个完美的世界里,你应该避免这种情况,并拥有一个足够大的池,以避免昂贵的运行时创建")]
public bool PoolCanExpand = true;
protected MMSoundManagerAudioPool _pool;
protected GameObject _tempAudioSourceGameObject;
protected MMSoundManagerSound _sound;
protected List<MMSoundManagerSound> _sounds;
protected AudioSource _tempAudioSource;
#region Initialization
/// <summary>
/// On Awake we initialize our manager
/// </summary>
protected override void Awake()
{
base.Awake();
InitializeSoundManager();
}
/// <summary>
/// On Start we load and apply our saved settings if needed.
/// This is done on Start and not Awake because of a bug in Unity's AudioMixer API
/// </summary>
protected virtual void Start()
{
if ((settingsSo != null) && (settingsSo.Settings.AutoLoad))
{
settingsSo.LoadSoundSettings();
}
}
/// <summary>
/// Initializes the pool, fills it, registers to the scene loaded event
/// </summary>
protected virtual void InitializeSoundManager()
{
if (_pool == null)
{
_pool = new MMSoundManagerAudioPool();
}
_sounds = new List<MMSoundManagerSound>();
_pool.FillAudioSourcePool(AudioSourcePoolSize, this.transform);
}
#endregion
#region PlaySound
/// <summary>
/// Plays a sound, separate options object signature
/// </summary>
/// <param name="audioClip"></param>
/// <param name="options"></param>
/// <returns></returns>
public virtual AudioSource PlaySound(AudioClip audioClip, MMSoundManagerPlayOptions options)
{
return PlaySound(audioClip, options.MmSoundManagerTrack, options.Location,
options.Loop, options.Volume, options.ID,
options.Fade, options.FadeInitialVolume, options.FadeDuration, options.FadeTween,
options.Persistent,
options.RecycleAudioSource, options.AudioGroup,
options.Pitch, options.PanStereo, options.SpatialBlend,
options.SoloSingleTrack, options.SoloAllTracks, options.AutoUnSoloOnEnd,
options.BypassEffects, options.BypassListenerEffects, options.BypassReverbZones, options.Priority,
options.ReverbZoneMix,
options.DopplerLevel, options.Spread, options.RolloffMode, options.MinDistance, options.MaxDistance
);
}
/// <summary>
/// Plays a sound, signature with all options
/// </summary>
/// <param name="audioClip"></param>
/// <param name="mmSoundManagerTrack"></param>
/// <param name="location"></param>
/// <param name="loop"></param>
/// <param name="volume"></param>
/// <param name="ID"></param>
/// <param name="fade"></param>
/// <param name="fadeInitialVolume"></param>
/// <param name="fadeDuration"></param>
/// <param name="fadeTween"></param>
/// <param name="persistent"></param>
/// <param name="recycleAudioSource"></param>
/// <param name="audioGroup"></param>
/// <param name="pitch"></param>
/// <param name="panStereo"></param>
/// <param name="spatialBlend"></param>
/// <param name="soloSingleTrack"></param>
/// <param name="soloAllTracks"></param>
/// <param name="autoUnSoloOnEnd"></param>
/// <param name="bypassEffects"></param>
/// <param name="bypassListenerEffects"></param>
/// <param name="bypassReverbZones"></param>
/// <param name="priority"></param>
/// <param name="reverbZoneMix"></param>
/// <param name="dopplerLevel"></param>
/// <param name="spread"></param>
/// <param name="rolloffMode"></param>
/// <param name="minDistance"></param>
/// <param name="maxDistance"></param>
/// <returns></returns>
public virtual AudioSource PlaySound(AudioClip audioClip, MMSoundManagerTracks mmSoundManagerTrack, Vector3 location,
bool loop = false, float volume = 1.0f, int ID = 0,
bool fade = false, float fadeInitialVolume = 0f, float fadeDuration = 1f, MMTweenType fadeTween = null,
bool persistent = false,
AudioSource recycleAudioSource = null, AudioMixerGroup audioGroup = null,
float pitch = 1f, float panStereo = 0f, float spatialBlend = 0.0f,
bool soloSingleTrack = false, bool soloAllTracks = false, bool autoUnSoloOnEnd = false,
bool bypassEffects = false, bool bypassListenerEffects = false, bool bypassReverbZones = false, int priority = 128, float reverbZoneMix = 1f,
float dopplerLevel = 1f, int spread = 0, AudioRolloffMode rolloffMode = AudioRolloffMode.Logarithmic, float minDistance = 1f, float maxDistance = 500f
)
{
if (!audioClip) { return null; }
// audio source setup ---------------------------------------------------------------------------------
// we reuse an audiosource if one is passed in parameters
AudioSource audioSource = recycleAudioSource;
if (audioSource == null)
{
// we pick an idle audio source from the pool if possible
audioSource = _pool.GetAvailableAudioSource(PoolCanExpand, this.transform);
if ((audioSource != null) && (!loop))
{
recycleAudioSource = audioSource;
// we destroy the host after the clip has played (if it not tag for reusability.
StartCoroutine(_pool.AutoDisableAudioSource(audioClip.length / Mathf.Abs(pitch), audioSource, audioClip));
}
}
// we create an audio source if needed
if (audioSource == null)
{
_tempAudioSourceGameObject = new GameObject("MMAudio_" + audioClip.name);
SceneManager.MoveGameObjectToScene(_tempAudioSourceGameObject, this.gameObject.scene);
audioSource = _tempAudioSourceGameObject.AddComponent<AudioSource>();
}
// audio source settings ---------------------------------------------------------------------------------
audioSource.transform.position = location;
audioSource.time = 0.0f;
audioSource.clip = audioClip;
audioSource.pitch = pitch;
audioSource.spatialBlend = spatialBlend;
audioSource.panStereo = panStereo;
audioSource.loop = loop;
audioSource.bypassEffects = bypassEffects;
audioSource.bypassListenerEffects = bypassListenerEffects;
audioSource.bypassReverbZones = bypassReverbZones;
audioSource.priority = priority;
audioSource.reverbZoneMix = reverbZoneMix;
audioSource.dopplerLevel = dopplerLevel;
audioSource.spread = spread;
audioSource.rolloffMode = rolloffMode;
audioSource.minDistance = minDistance;
audioSource.maxDistance = maxDistance;
// track and volume ---------------------------------------------------------------------------------
if (settingsSo != null)
{
audioSource.outputAudioMixerGroup = settingsSo.MasterAudioMixerGroup;
switch (mmSoundManagerTrack)
{
case MMSoundManagerTracks.Master:
audioSource.outputAudioMixerGroup = settingsSo.MasterAudioMixerGroup;
break;
case MMSoundManagerTracks.Music:
audioSource.outputAudioMixerGroup = settingsSo.MusicAudioMixerGroup;
break;
case MMSoundManagerTracks.Sfx:
audioSource.outputAudioMixerGroup = settingsSo.SfxAudioMixerGroup;
break;
case MMSoundManagerTracks.UI:
audioSource.outputAudioMixerGroup = settingsSo.UIAudioMixerGroup;
break;
}
}
if (audioGroup)
{
audioSource.outputAudioMixerGroup = audioGroup;
}
audioSource.volume = volume;
// we start playing the sound
audioSource.Play();
// we destroy the host after the clip has played if it was a one time AS.
if (!loop && !recycleAudioSource)
{
Destroy(_tempAudioSourceGameObject, audioClip.length);
}
// we fade the sound in if needed
if (fade)
{
FadeSound(audioSource, fadeDuration, fadeInitialVolume, volume, fadeTween);
}
// we handle soloing
if (soloSingleTrack)
{
MuteSoundsOnTrack(mmSoundManagerTrack, true, 0f);
audioSource.mute = false;
if (autoUnSoloOnEnd)
{
MuteSoundsOnTrack(mmSoundManagerTrack, false, audioClip.length);
}
}
else if (soloAllTracks)
{
MuteAllSounds();
audioSource.mute = false;
if (autoUnSoloOnEnd)
{
StartCoroutine(MuteAllSoundsCoroutine(audioClip.length, false));
}
}
// we prepare for storage
_sound.ID = ID;
_sound.Track = mmSoundManagerTrack;
_sound.Source = audioSource;
_sound.Persistent = persistent;
// we check if that audiosource is already being tracked in _sounds
bool alreadyIn = false;
for (int i = 0; i < _sounds.Count; i++)
{
if (_sounds[i].Source == audioSource)
{
_sounds[i] = _sound;
alreadyIn = true;
}
}
if (!alreadyIn)
{
_sounds.Add(_sound);
}
// we return the audiosource reference
return audioSource;
}
#endregion
#region SoundControls
/// <summary>
/// Pauses the specified audiosource
/// </summary>
/// <param name="source"></param>
public virtual void PauseSound(AudioSource source)
{
source.Pause();
}
/// <summary>
/// resumes play on the specified audio source
/// </summary>
/// <param name="source"></param>
public virtual void ResumeSound(AudioSource source)
{
source.Play();
}
/// <summary>
/// Stops the specified audio source
/// </summary>
/// <param name="source"></param>
public virtual void StopSound(AudioSource source)
{
source.Stop();
}
/// <summary>
/// Frees a specific sound, stopping it and returning it to the pool
/// </summary>
/// <param name="source"></param>
public virtual void FreeSound(AudioSource source)
{
source.Stop();
if (!_pool.FreeSound(source))
{
Destroy(source.gameObject);
}
}
#endregion
#region TrackControls
/// <summary>
/// Mutes an entire track
/// </summary>
/// <param name="track"></param>
public virtual void MuteTrack(MMSoundManagerTracks track)
{
ControlTrack(track, ControlTrackModes.Mute, 0f);
}
/// <summary>
/// Unmutes an entire track
/// </summary>
/// <param name="track"></param>
public virtual void UnmuteTrack(MMSoundManagerTracks track)
{
ControlTrack(track, ControlTrackModes.Unmute, 0f);
}
/// <summary>
/// Sets the volume of an entire track
/// </summary>
/// <param name="track"></param>
/// <param name="volume"></param>
public virtual void SetTrackVolume(MMSoundManagerTracks track, float volume)
{
ControlTrack(track, ControlTrackModes.SetVolume, volume);
}
/// <summary>
/// Returns the current volume of a track
/// </summary>
/// <param name="track"></param>
/// <param name="volume"></param>
public virtual float GetTrackVolume(MMSoundManagerTracks track, bool mutedVolume)
{
switch (track)
{
case MMSoundManagerTracks.Master:
if (mutedVolume)
{
return settingsSo.Settings.MutedMasterVolume;
}
else
{
return settingsSo.Settings.MasterVolume;
}
case MMSoundManagerTracks.Music:
if (mutedVolume)
{
return settingsSo.Settings.MutedMusicVolume;
}
else
{
return settingsSo.Settings.MusicVolume;
}
case MMSoundManagerTracks.Sfx:
if (mutedVolume)
{
return settingsSo.Settings.MutedSfxVolume;
}
else
{
return settingsSo.Settings.SfxVolume;
}
case MMSoundManagerTracks.UI:
if (mutedVolume)
{
return settingsSo.Settings.MutedUIVolume;
}
else
{
return settingsSo.Settings.UIVolume;
}
}
return 1f;
}
/// <summary>
/// Pauses all sounds on a track
/// </summary>
/// <param name="track"></param>
public virtual void PauseTrack(MMSoundManagerTracks track)
{
foreach (MMSoundManagerSound sound in _sounds)
{
if (sound.Track == track)
{
sound.Source.Pause();
}
}
}
/// <summary>
/// Plays or resumes all sounds on a track
/// </summary>
/// <param name="track"></param>
public virtual void PlayTrack(MMSoundManagerTracks track)
{
foreach (MMSoundManagerSound sound in _sounds)
{
if (sound.Track == track)
{
sound.Source.Play();
}
}
}
/// <summary>
/// Stops all sounds on a track
/// </summary>
/// <param name="track"></param>
public virtual void StopTrack(MMSoundManagerTracks track)
{
foreach (MMSoundManagerSound sound in _sounds)
{
if (sound.Track == track)
{
sound.Source.Stop();
}
}
}
/// <summary>
/// Stops all sounds on a track, and returns them to the pool
/// </summary>
/// <param name="track"></param>
public virtual void FreeTrack(MMSoundManagerTracks track)
{
foreach (MMSoundManagerSound sound in _sounds)
{
if (sound.Track == track)
{
sound.Source.Stop();
sound.Source.gameObject.SetActive(false);
}
}
}
/// <summary>
/// Mutes the music track, QoL method ready to bind to a UnityEvent
/// </summary>
public virtual void MuteMusic() { MuteTrack(MMSoundManagerTracks.Music); }
/// <summary>
/// Unmutes the music track, QoL method ready to bind to a UnityEvent
/// </summary>
public virtual void UnmuteMusic() { UnmuteTrack(MMSoundManagerTracks.Music); }
/// <summary>
/// Mutes the sfx track, QoL method ready to bind to a UnityEvent
/// </summary>
public virtual void MuteSfx() { MuteTrack(MMSoundManagerTracks.Sfx); }
/// <summary>
/// Unmutes the sfx track, QoL method ready to bind to a UnityEvent
/// </summary>
public virtual void UnmuteSfx() { UnmuteTrack(MMSoundManagerTracks.Sfx); }
/// <summary>
/// Mutes the UI track, QoL method ready to bind to a UnityEvent
/// </summary>
public virtual void MuteUI() { MuteTrack(MMSoundManagerTracks.UI); }
/// <summary>
/// Unmutes the UI track, QoL method ready to bind to a UnityEvent
/// </summary>
public virtual void UnmuteUI() { UnmuteTrack(MMSoundManagerTracks.UI); }
/// <summary>
/// Mutes the master track, QoL method ready to bind to a UnityEvent
/// </summary>
public virtual void MuteMaster() { MuteTrack(MMSoundManagerTracks.Master); }
/// <summary>
/// Unmutes the master track, QoL method ready to bind to a UnityEvent
/// </summary>
public virtual void UnmuteMaster() { UnmuteTrack(MMSoundManagerTracks.Master); }
/// <summary>
/// Sets the volume of the Music track to the specified value, QoL method, ready to bind to a UnityEvent
/// </summary>
public virtual void SetVolumeMusic(float newVolume) { SetTrackVolume(MMSoundManagerTracks.Music, newVolume); }
/// <summary>
/// Sets the volume of the SFX track to the specified value, QoL method, ready to bind to a UnityEvent
/// </summary>
public virtual void SetVolumeSfx(float newVolume) { SetTrackVolume(MMSoundManagerTracks.Sfx, newVolume); }
/// <summary>
/// Sets the volume of the UI track to the specified value, QoL method, ready to bind to a UnityEvent
/// </summary>
public virtual void SetVolumeUI(float newVolume) { SetTrackVolume(MMSoundManagerTracks.UI, newVolume); }
/// <summary>
/// Sets the volume of the Master track to the specified value, QoL method, ready to bind to a UnityEvent
/// </summary>
public virtual void SetVolumeMaster(float newVolume) { SetTrackVolume(MMSoundManagerTracks.Master, newVolume); }
/// <summary>
/// A method that will let you mute/unmute a track, or set it to a specified volume
/// </summary>
public enum ControlTrackModes { Mute, Unmute, SetVolume }
protected virtual void ControlTrack(MMSoundManagerTracks track, ControlTrackModes trackMode, float volume = 0.5f)
{
string target = "";
float savedVolume = 0f;
switch (track)
{
case MMSoundManagerTracks.Master:
target = settingsSo.Settings.MasterVolumeParameter;
if (trackMode == ControlTrackModes.Mute) { settingsSo.TargetAudioMixer.GetFloat(target, out settingsSo.Settings.MutedMasterVolume); settingsSo.Settings.MasterOn = false; }
else if (trackMode == ControlTrackModes.Unmute) { savedVolume = settingsSo.Settings.MutedMasterVolume; settingsSo.Settings.MasterOn = true; }
break;
case MMSoundManagerTracks.Music:
target = settingsSo.Settings.MusicVolumeParameter;
if (trackMode == ControlTrackModes.Mute)
{
settingsSo.TargetAudioMixer.GetFloat(target, out settingsSo.Settings.MutedMusicVolume);
settingsSo.Settings.MusicOn = false;
}
else if (trackMode == ControlTrackModes.Unmute)
{
savedVolume = settingsSo.Settings.MutedMusicVolume;
settingsSo.Settings.MusicOn = true;
}
break;
case MMSoundManagerTracks.Sfx:
target = settingsSo.Settings.SfxVolumeParameter;
if (trackMode == ControlTrackModes.Mute) { settingsSo.TargetAudioMixer.GetFloat(target, out settingsSo.Settings.MutedSfxVolume); settingsSo.Settings.SfxOn = false; }
else if (trackMode == ControlTrackModes.Unmute) { savedVolume = settingsSo.Settings.MutedSfxVolume; settingsSo.Settings.SfxOn = true; }
break;
case MMSoundManagerTracks.UI:
target = settingsSo.Settings.UIVolumeParameter;
if (trackMode == ControlTrackModes.Mute) { settingsSo.TargetAudioMixer.GetFloat(target, out settingsSo.Settings.MutedUIVolume); settingsSo.Settings.UIOn = false; }
else if (trackMode == ControlTrackModes.Unmute) { savedVolume = settingsSo.Settings.MutedUIVolume; settingsSo.Settings.UIOn = true; }
break;
}
switch (trackMode)
{
case ControlTrackModes.Mute:
settingsSo.SetTrackVolume(track, 0f);
break;
case ControlTrackModes.Unmute:
settingsSo.SetTrackVolume(track, settingsSo.MixerVolumeToNormalized(savedVolume));
break;
case ControlTrackModes.SetVolume:
settingsSo.SetTrackVolume(track, volume);
break;
}
settingsSo.GetTrackVolumes();
if (settingsSo.Settings.AutoSave)
{
settingsSo.SaveSoundSettings();
}
}
#endregion
#region Fades
/// <summary>
/// Fades an entire track over the specified duration towards the desired finalVolume
/// </summary>
/// <param name="track"></param>
/// <param name="duration"></param>
/// <param name="initialVolume"></param>
/// <param name="finalVolume"></param>
/// <param name="tweenType"></param>
public virtual void FadeTrack(MMSoundManagerTracks track, float duration, float initialVolume = 0f, float finalVolume = 1f, MMTweenType tweenType = null)
{
StartCoroutine(FadeTrackCoroutine(track, duration, initialVolume, finalVolume, tweenType));
}
/// <summary>
/// Fades a target sound towards a final volume over time
/// </summary>
/// <param name="source"></param>
/// <param name="duration"></param>
/// <param name="initialVolume"></param>
/// <param name="finalVolume"></param>
/// <param name="tweenType"></param>
public virtual void FadeSound(AudioSource source, float duration, float initialVolume, float finalVolume, MMTweenType tweenType)
{
StartCoroutine(FadeCoroutine(source, duration, initialVolume, finalVolume, tweenType));
}
/// <summary>
/// Fades an entire track over time
/// </summary>
/// <param name="track"></param>
/// <param name="duration"></param>
/// <param name="initialVolume"></param>
/// <param name="finalVolume"></param>
/// <param name="tweenType"></param>
/// <returns></returns>
protected virtual IEnumerator FadeTrackCoroutine(MMSoundManagerTracks track, float duration, float initialVolume, float finalVolume, MMTweenType tweenType)
{
float startedAt = Time.unscaledTime;
if (tweenType == null)
{
tweenType = new MMTweenType(MMTween.MMTweenCurve.EaseInOutQuartic);
}
while (Time.unscaledTime - startedAt <= duration)
{
float elapsedTime = Time.unscaledTime - startedAt;
float newVolume = MMTween.Tween(elapsedTime, 0f, duration, initialVolume, finalVolume, tweenType);
settingsSo.SetTrackVolume(track, newVolume);
yield return null;
}
settingsSo.SetTrackVolume(track, finalVolume);
}
/// <summary>
/// Fades an audiosource's volume over time
/// </summary>
/// <param name="source"></param>
/// <param name="duration"></param>
/// <param name="initialVolume"></param>
/// <param name="finalVolume"></param>
/// <param name="tweenType"></param>
/// <returns></returns>
protected virtual IEnumerator FadeCoroutine(AudioSource source, float duration, float initialVolume, float finalVolume, MMTweenType tweenType)
{
float startedAt = Time.unscaledTime;
if (tweenType == null)
{
tweenType = new MMTweenType(MMTween.MMTweenCurve.EaseInOutQuartic);
}
while (Time.unscaledTime - startedAt <= duration)
{
float elapsedTime = Time.unscaledTime - startedAt;
float newVolume = MMTween.Tween(elapsedTime, 0f, duration, initialVolume, finalVolume, tweenType);
source.volume = newVolume;
yield return null;
}
source.volume = finalVolume;
}
#endregion
#region Solo
/// <summary>
/// Mutes all sounds playing on a specific track
/// </summary>
/// <param name="track"></param>
/// <param name="mute"></param>
/// <param name="delay"></param>
public virtual void MuteSoundsOnTrack(MMSoundManagerTracks track, bool mute, float delay = 0f)
{
StartCoroutine(MuteSoundsOnTrackCoroutine(track, mute, delay));
}
/// <summary>
/// Mutes all sounds playing on the MMSoundManager
/// </summary>
/// <param name="mute"></param>
public virtual void MuteAllSounds(bool mute = true)
{
StartCoroutine(MuteAllSoundsCoroutine(0f, mute));
}
/// <summary>
/// Mutes all sounds on the specified track after an optional delay
/// </summary>
/// <param name="track"></param>
/// <param name="mute"></param>
/// <param name="delay"></param>
/// <returns></returns>
protected virtual IEnumerator MuteSoundsOnTrackCoroutine(MMSoundManagerTracks track, bool mute, float delay)
{
if (delay > 0)
{
yield return MMCoroutine.WaitForUnscaled(delay);
}
foreach (MMSoundManagerSound sound in _sounds)
{
if (sound.Track == track)
{
sound.Source.mute = mute;
}
}
}
/// <summary>
/// Mutes all sounds after an optional delay
/// </summary>
/// <param name="delay"></param>
/// <param name="mute"></param>
/// <returns></returns>
protected virtual IEnumerator MuteAllSoundsCoroutine(float delay, bool mute = true)
{
if (delay > 0)
{
yield return MMCoroutine.WaitForUnscaled(delay);
}
foreach (MMSoundManagerSound sound in _sounds)
{
sound.Source.mute = mute;
}
}
#endregion
#region Find
/// <summary>
/// Returns an audio source played with the specified ID, if one is found
/// </summary>
/// <param name="ID"></param>
/// <returns></returns>
public virtual AudioSource FindByID(int ID)
{
foreach (MMSoundManagerSound sound in _sounds)
{
if (sound.ID == ID)
{
return sound.Source;
}
}
return null;
}
/// <summary>
/// Returns an audio source played with the specified ID, if one is found
/// </summary>
/// <param name="ID"></param>
/// <returns></returns>
public virtual AudioSource FindByClip(AudioClip clip)
{
foreach (MMSoundManagerSound sound in _sounds)
{
if (sound.Source.clip == clip)
{
return sound.Source;
}
}
return null;
}
#endregion
#region AllSoundsControls
/// <summary>
/// Pauses all sounds playing on the MMSoundManager
/// </summary>
public virtual void PauseAllSounds()
{
foreach (MMSoundManagerSound sound in _sounds)
{
sound.Source.Pause();
}
}
/// <summary>
/// Pauses all sounds playing on the MMSoundManager
/// </summary>
public virtual void PlayAllSounds()
{
foreach (MMSoundManagerSound sound in _sounds)
{
sound.Source.Play();
}
}
/// <summary>
/// Stops all sounds playing on the MMSoundManager
/// </summary>
public virtual void StopAllSounds()
{
foreach (MMSoundManagerSound sound in _sounds)
{
sound.Source.Stop();
}
}
/// <summary>
/// Stops all sounds and returns them to the pool
/// </summary>
public virtual void FreeAllSounds()
{
foreach (MMSoundManagerSound sound in _sounds)
{
if (sound.Source != null)
{
FreeSound(sound.Source);
}
}
}
/// <summary>
/// Stops all sounds except the persistent ones, and returns them to the pool
/// </summary>
public virtual void FreeAllSoundsButPersistent()
{
foreach (MMSoundManagerSound sound in _sounds)
{
if ((!sound.Persistent) && (sound.Source != null))
{
FreeSound(sound.Source);
}
}
}
/// <summary>
/// Stops all looping sounds and returns them to the pool
/// </summary>
public virtual void FreeAllLoopingSounds()
{
foreach (MMSoundManagerSound sound in _sounds)
{
if ((sound.Source.loop) && (sound.Source != null))
{
FreeSound(sound.Source);
}
}
}
#endregion
#region Events
/// <summary>
/// Registered on enable, triggers every time a new scene is loaded
/// At which point we free all sounds except the persistent ones
/// </summary>
protected virtual void OnSceneLoaded(Scene arg0, LoadSceneMode loadSceneMode)
{
FreeAllSoundsButPersistent();
}
public void OnMMEvent(MMSoundManagerTrackEvent soundManagerTrackEvent)
{
switch (soundManagerTrackEvent.TrackEventType)
{
case MMSoundManagerTrackEventTypes.MuteTrack:
MuteTrack(soundManagerTrackEvent.Track);
break;
case MMSoundManagerTrackEventTypes.UnmuteTrack:
UnmuteTrack(soundManagerTrackEvent.Track);
break;
case MMSoundManagerTrackEventTypes.SetVolumeTrack:
SetTrackVolume(soundManagerTrackEvent.Track, soundManagerTrackEvent.Volume);
break;
case MMSoundManagerTrackEventTypes.PlayTrack:
PlayTrack(soundManagerTrackEvent.Track);
break;
case MMSoundManagerTrackEventTypes.PauseTrack:
PauseTrack(soundManagerTrackEvent.Track);
break;
case MMSoundManagerTrackEventTypes.StopTrack:
StopTrack(soundManagerTrackEvent.Track);
break;
case MMSoundManagerTrackEventTypes.FreeTrack:
FreeTrack(soundManagerTrackEvent.Track);
break;
}
}
public void OnMMEvent(MMSoundManagerEvent soundManagerEvent)
{
switch (soundManagerEvent.EventType)
{
case MMSoundManagerEventTypes.SaveSettings:
SaveSettings();
break;
case MMSoundManagerEventTypes.LoadSettings:
settingsSo.LoadSoundSettings();
break;
case MMSoundManagerEventTypes.ResetSettings:
settingsSo.ResetSoundSettings();
break;
}
}
/// <summary>
/// Save sound settings to file
/// </summary>
public virtual void SaveSettings()
{
settingsSo.SaveSoundSettings();
}
/// <summary>
/// Loads sound settings from file
/// </summary>
public virtual void LoadSettings()
{
settingsSo.LoadSoundSettings();
}
/// <summary>
/// Deletes any saved sound settings
/// </summary>
public virtual void ResetSettings()
{
settingsSo.ResetSoundSettings();
}
public void OnMMEvent(MMSoundManagerSoundControlEvent soundControlEvent)
{
if (soundControlEvent.TargetSource == null)
{
_tempAudioSource = FindByID(soundControlEvent.SoundID);
}
else
{
_tempAudioSource = soundControlEvent.TargetSource;
}
if (_tempAudioSource != null)
{
switch (soundControlEvent.MMSoundManagerSoundControlEventType)
{
case MMSoundManagerSoundControlEventTypes.Pause:
PauseSound(_tempAudioSource);
break;
case MMSoundManagerSoundControlEventTypes.Resume:
ResumeSound(_tempAudioSource);
break;
case MMSoundManagerSoundControlEventTypes.Stop:
StopSound(_tempAudioSource);
break;
case MMSoundManagerSoundControlEventTypes.Free:
FreeSound(_tempAudioSource);
break;
}
}
}
public void OnMMEvent(MMSoundManagerTrackFadeEvent trackFadeEvent)
{
FadeTrack(trackFadeEvent.Track, trackFadeEvent.FadeDuration, settingsSo.GetTrackVolume(trackFadeEvent.Track), trackFadeEvent.FinalVolume, trackFadeEvent.FadeTween);
}
public void OnMMEvent(MMSoundManagerSoundFadeEvent soundFadeEvent)
{
_tempAudioSource = FindByID(soundFadeEvent.SoundID);
if (_tempAudioSource != null)
{
FadeSound(_tempAudioSource, soundFadeEvent.FadeDuration, _tempAudioSource.volume, soundFadeEvent.FinalVolume,
soundFadeEvent.FadeTween);
}
}
public void OnMMEvent(MMSoundManagerAllSoundsControlEvent allSoundsControlEvent)
{
switch (allSoundsControlEvent.EventType)
{
case MMSoundManagerAllSoundsControlEventTypes.Pause:
PauseAllSounds();
break;
case MMSoundManagerAllSoundsControlEventTypes.Play:
PlayAllSounds();
break;
case MMSoundManagerAllSoundsControlEventTypes.Stop:
StopAllSounds();
break;
case MMSoundManagerAllSoundsControlEventTypes.Free:
FreeAllSounds();
break;
case MMSoundManagerAllSoundsControlEventTypes.FreeAllButPersistent:
FreeAllSoundsButPersistent();
break;
case MMSoundManagerAllSoundsControlEventTypes.FreeAllLooping:
FreeAllLoopingSounds();
break;
}
}
private void OnMMSfxEvent(AudioClip clipToPlay, AudioMixerGroup audioGroup, float volume, float pitch)
{
MMSoundManagerPlayOptions options = MMSoundManagerPlayOptions.Default;
options.Location = this.transform.position;
options.AudioGroup = audioGroup;
options.Volume = volume;
options.Pitch = pitch;
options.MmSoundManagerTrack = MMSoundManagerTracks.Sfx;
options.Loop = false;
PlaySound(clipToPlay, options);
}
public virtual AudioSource OnMMSoundManagerSoundPlayEvent(AudioClip clip, MMSoundManagerPlayOptions options)
{
return PlaySound(clip, options);
}
/// <summary>
/// On enable we start listening for events
/// </summary>
protected virtual void OnEnable()
{
MMSfxEvent.Register(OnMMSfxEvent);
MMSoundManagerSoundPlayEvent.Register(OnMMSoundManagerSoundPlayEvent);
this.MMEventStartListening<MMSoundManagerEvent>();
this.MMEventStartListening<MMSoundManagerTrackEvent>();
this.MMEventStartListening<MMSoundManagerSoundControlEvent>();
this.MMEventStartListening<MMSoundManagerTrackFadeEvent>();
this.MMEventStartListening<MMSoundManagerSoundFadeEvent>();
this.MMEventStartListening<MMSoundManagerAllSoundsControlEvent>();
SceneManager.sceneLoaded += OnSceneLoaded;
}
/// <summary>
/// On disable we stop listening for events
/// </summary>
protected virtual void OnDisable()
{
if (_enabled)
{
MMSfxEvent.Unregister(OnMMSfxEvent);
MMSoundManagerSoundPlayEvent.Unregister(OnMMSoundManagerSoundPlayEvent);
this.MMEventStopListening<MMSoundManagerEvent>();
this.MMEventStopListening<MMSoundManagerTrackEvent>();
this.MMEventStopListening<MMSoundManagerSoundControlEvent>();
this.MMEventStopListening<MMSoundManagerTrackFadeEvent>();
this.MMEventStopListening<MMSoundManagerSoundFadeEvent>();
this.MMEventStopListening<MMSoundManagerAllSoundsControlEvent>();
SceneManager.sceneLoaded -= OnSceneLoaded;
}
}
#endregion
}
}
数据
using System;
using System.Collections;
using System.Collections.Generic;
using UnityEngine;
using UnityEngine.Audio;
namespace MoreMountains.Tools
{
/// <summary>
/// 一个用于保存声音设置的类(音乐打开或关闭,sfx打开或关闭)
/// </summary>
[Serializable]
[CreateAssetMenu(menuName = "MoreMountains/Audio/MMSoundManagerSettings")]
public class MMSoundManagerSettingsSO : ScriptableObject
{
[Header("Audio Mixer")]
[Tooltip("播放声音时使用的混音器")]
public AudioMixer TargetAudioMixer;
[Tooltip("主组")]
public AudioMixerGroup MasterAudioMixerGroup;
[Tooltip("播放所有音乐声音的组")]
public AudioMixerGroup MusicAudioMixerGroup;
[Tooltip("播放所有音效的组")]
public AudioMixerGroup SfxAudioMixerGroup;
[Tooltip("播放所有UI声音的组")]
public AudioMixerGroup UIAudioMixerGroup;
[Header("设置展开")]
[Tooltip("此MMSoundManager的完整设置")]
public MMSoundManagerSettings Settings;
protected const string _saveFolderName = "MMSoundManager/";
protected const string _saveFileName = "mmsound.settings";
#region SaveAndLoad
/// <summary>
/// 将声音设置保存到文件
/// </summary>
public virtual void SaveSoundSettings()
{
MMSaveLoadManager.Save(this.Settings, _saveFileName, _saveFolderName);
}
/// <summary>
/// 从文件加载声音设置(如果找到)
/// </summary>
public virtual void LoadSoundSettings()
{
if (Settings.OverrideMixerSettings)
{
MMSoundManagerSettings settings =
(MMSoundManagerSettings)MMSaveLoadManager.Load(typeof(MMSoundManagerSettings), _saveFileName,
_saveFolderName);
if (settings != null)
{
this.Settings = settings;
ApplyTrackVolumes();
}
}
}
/// <summary>
/// 通过销毁保存文件重置声音设置
/// </summary>
public virtual void ResetSoundSettings()
{
MMSaveLoadManager.DeleteSave(_saveFileName, _saveFolderName);
}
#endregion
#region Volume
/// <summary>
/// 将所选曲目的音量设置为参数中传递的值
/// </summary>
/// <param name="track"></param>
/// <param name="volume"></param>
public virtual void SetTrackVolume(MMSoundManager.MMSoundManagerTracks track, float volume)
{
if (volume <= 0f)
{
volume = MMSoundManagerSettings._minimalVolume;
}
switch (track)
{
case MMSoundManager.MMSoundManagerTracks.Master:
TargetAudioMixer.SetFloat(Settings.MasterVolumeParameter, NormalizedToMixerVolume(volume));
Settings.MasterVolume = volume;
break;
case MMSoundManager.MMSoundManagerTracks.Music:
TargetAudioMixer.SetFloat(Settings.MusicVolumeParameter, NormalizedToMixerVolume(volume));
Settings.MusicVolume = volume;
break;
case MMSoundManager.MMSoundManagerTracks.Sfx:
TargetAudioMixer.SetFloat(Settings.SfxVolumeParameter, NormalizedToMixerVolume(volume));
Settings.SfxVolume = volume;
break;
case MMSoundManager.MMSoundManagerTracks.UI:
TargetAudioMixer.SetFloat(Settings.UIVolumeParameter, NormalizedToMixerVolume(volume));
Settings.UIVolume = volume;
break;
}
if (Settings.AutoSave)
{
SaveSoundSettings();
}
}
/// <summary>
/// 返回指定曲目的音量
/// </summary>
/// <param name="track"></param>
/// <returns></returns>
public virtual float GetTrackVolume(MMSoundManager.MMSoundManagerTracks track)
{
float volume = 1f;
switch (track)
{
case MMSoundManager.MMSoundManagerTracks.Master:
TargetAudioMixer.GetFloat(Settings.MasterVolumeParameter, out volume);
break;
case MMSoundManager.MMSoundManagerTracks.Music:
TargetAudioMixer.GetFloat(Settings.MusicVolumeParameter, out volume);
break;
case MMSoundManager.MMSoundManagerTracks.Sfx:
TargetAudioMixer.GetFloat(Settings.SfxVolumeParameter, out volume);
break;
case MMSoundManager.MMSoundManagerTracks.UI:
TargetAudioMixer.GetFloat(Settings.UIVolumeParameter, out volume);
break;
}
return MixerVolumeToNormalized(volume);
}
/// <summary>
/// 将每个曲目的音量分配给设置值
/// </summary>
public virtual void GetTrackVolumes()
{
Settings.MasterVolume = GetTrackVolume(MMSoundManager.MMSoundManagerTracks.Master);
Settings.MusicVolume = GetTrackVolume(MMSoundManager.MMSoundManagerTracks.Music);
Settings.SfxVolume = GetTrackVolume(MMSoundManager.MMSoundManagerTracks.Sfx);
Settings.UIVolume = GetTrackVolume(MMSoundManager.MMSoundManagerTracks.UI);
}
/// <summary>
/// 将音量应用于所有曲目,并在需要时进行保存
/// </summary>
protected virtual void ApplyTrackVolumes()
{
if (Settings.OverrideMixerSettings)
{
TargetAudioMixer.SetFloat(Settings.MasterVolumeParameter, NormalizedToMixerVolume(Settings.MasterVolume));
TargetAudioMixer.SetFloat(Settings.MusicVolumeParameter, NormalizedToMixerVolume(Settings.MusicVolume));
TargetAudioMixer.SetFloat(Settings.SfxVolumeParameter, NormalizedToMixerVolume(Settings.SfxVolume));
TargetAudioMixer.SetFloat(Settings.UIVolumeParameter, NormalizedToMixerVolume(Settings.UIVolume));
if (!Settings.MasterOn) { TargetAudioMixer.SetFloat(Settings.MasterVolumeParameter, -80f); }
if (!Settings.MusicOn) { TargetAudioMixer.SetFloat(Settings.MusicVolumeParameter, -80f); }
if (!Settings.SfxOn) { TargetAudioMixer.SetFloat(Settings.SfxVolumeParameter, -80f); }
if (!Settings.UIOn) { TargetAudioMixer.SetFloat(Settings.UIVolumeParameter, -80f); }
if (Settings.AutoSave)
{
SaveSoundSettings();
}
}
}
/// <summary>
/// 将标准化音量转换为混音器组db比例
/// </summary>
/// <param name="normalizedVolume"></param>
/// <returns></returns>
public virtual float NormalizedToMixerVolume(float normalizedVolume)
{
return Mathf.Log10(normalizedVolume) * 20;
}
/// <summary>
/// 将混音器音量转换为归一化值
/// </summary>
/// <param name="mixerVolume"></param>
/// <returns></returns>
public virtual float MixerVolumeToNormalized(float mixerVolume)
{
return (float)Math.Pow(10, (mixerVolume / 20));
}
#endregion
}
}
设置
using System;
using System.Collections;
using System.Collections.Generic;
using UnityEngine;
namespace MoreMountains.Tools
{
/// <summary>
/// 此类存储MMSoundManager设置,并允许您从MMSoundManagerSettingsSO的检查器中调整它们
/// </summary>
[Serializable]
public class MMSoundManagerSettings
{
public const float _minimalVolume = 0.0001f;
public const float _maxVolume = 10f;
public const float _defaultVolume = 1f;
[Header("Audio Mixer Control")]
[Tooltip("下面描述的设置是否应覆盖AudioMixer中定义的设置")]
public bool OverrideMixerSettings = true;
[Header("混音器暴露参数")]
[Tooltip("AudioMixer中公开的MasterVolume参数的名称")]
public string MasterVolumeParameter = "MasterVolume";
[Tooltip("AudioMixer中公开的MusicVolume参数的名称")]
public string MusicVolumeParameter = "MusicVolume";
[Tooltip("AudioMixer中暴露的SfxVolume参数的名称")]
public string SfxVolumeParameter = "SfxVolume";
[Tooltip("AudioMixer中公开的UIVolume参数的名称")]
public string UIVolumeParameter = "UIVolume";
[Header("Master")]
[MMReadOnly]
[Range(_minimalVolume, _maxVolume)]
[Tooltip("主音量")]
public float MasterVolume = _defaultVolume;
[Tooltip("当前主轨道是否处于活动状态")]
[MMReadOnly]
public bool MasterOn = true;
[Tooltip("主曲目静音前的音量")]
[MMReadOnly]
public float MutedMasterVolume;
[Header("音乐")]
[MMReadOnly]
[Range(_minimalVolume, _maxVolume)]
[Tooltip("音乐音量")]
public float MusicVolume = _defaultVolume;
[Tooltip("当前音乐曲目是否处于活动状态")]
[MMReadOnly]
public bool MusicOn = true;
[Tooltip("音乐曲目静音前的音量")]
[MMReadOnly]
public float MutedMusicVolume;
[Header("音效")]
[MMReadOnly]
[Range(_minimalVolume, _maxVolume)]
[Tooltip("声音fx音量")]
public float SfxVolume = _defaultVolume;
[Tooltip("SFX轨迹当前是否处于活动状态")]
[MMReadOnly]
public bool SfxOn = true;
[Tooltip("SFX曲目静音前的音量")]
[MMReadOnly]
public float MutedSfxVolume;
[Header("UI")]
[MMReadOnly]
[Range(_minimalVolume, _maxVolume)]
[Tooltip("UI的音量")]
public float UIVolume = _defaultVolume;
[Tooltip("UI轨迹当前是否处于活动状态")]
[MMReadOnly]
public bool UIOn = true;
[Tooltip("UI曲目静音前的音量")]
[MMReadOnly]
public float MutedUIVolume;
[Header("Save & Load")]
[Tooltip("MMSoundManager在启动时是否应自动加载设置")]
public bool AutoLoad = true;
[Tooltip("是否应自动保存设置中的每次更改。如果没有,则必须调用save MMSoundManager事件以保存设置")]
public bool AutoSave = false;
}
}
播放选项
using System;
using System.Collections;
using System.Collections.Generic;
using UnityEngine;
using UnityEngine.Audio;
namespace MoreMountains.Tools
{
/// <summary>
/// 用于存储MMSoundManager播放选项的类
/// </summary>
[Serializable]
public class MMSoundManagerPlayOptions
{
/// 播放声音的曲目
public MMSoundManager.MMSoundManagerTracks MmSoundManagerTrack;
/// 放置声音的位置
public Vector3 Location;
/// 声音是否应该循环
public bool Loop;
/// 播放声音的音量
public float Volume;
/// 声音的ID,有助于以后再次找到该声音
public int ID;
/// 播放时是否要减弱声音
public bool Fade;
/// 声音衰减前的初始音量
public float FadeInitialVolume;
/// 淡入淡出的持续时间(秒)
public float FadeDuration;
/// 减弱声音时使用的tween
public MMTweenType FadeTween;
///声音是否应在场景过渡期间持续存在
public bool Persistent;
/// 如果您不想从池中选择一个,则可以使用一个音频源
public AudioSource RecycleAudioSource;
/// 如果你不想在任何预设曲目上播放,可以使用一个音频组
public AudioMixerGroup AudioGroup;
/// 音频源的音高
public float Pitch;
/// 以立体声方式(向左或向右)平移播放声音。这仅适用于单声道或立体声的声音
public float PanStereo;
/// 设置此音频源受3D空间化计算(衰减、多普勒等)的影响程度。0.0使声音完全为2D,1.0使其完全为3D
public float SpatialBlend;
/// 该声音是否应在其目标曲目上以独奏模式播放。如果是,当此声音开始播放时,该曲目上的所有其他声音都将被静音
public bool SoloSingleTrack;
/// 该声音是否应在所有其他曲目上以独奏模式播放。如果是,当此声音开始播放时,所有其他曲目都将静音
public bool SoloAllTracks;
/// 如果在任何独奏模式下,一旦声音停止播放,AutoUnSoloOnEnd将自动取消曲目静音
public bool AutoUnSoloOnEnd;
/// 旁路效果(从过滤器组件或全局侦听器过滤器应用)
public bool BypassEffects;
/// 当在AudioListener上设置全局效果时,它将不会应用于由AudioSource生成的音频信号。如果音频源正在混音器组中播放,则不适用
public bool BypassListenerEffects;
/// 当设置不将信号从音频源路由到与混响区相关的全局混响时
public bool BypassReverbZones;
/// 设置音频源的优先级
public int Priority;
/// 来自音频源的信号将混合到与混响区相关的全局混响中的量
public float ReverbZoneMix;
/// 设置此音频源的多普勒刻度
public float DopplerLevel;
/// 设置扬声器空间中3d立体声或多声道声音的扩散角度(以度为单位)
public int Spread;
/// 设置/获取音频源随距离衰减的方式
public AudioRolloffMode RolloffMode;
/// 在最小距离内,音频源的音量将不再增大
public float MinDistance;
/// (对数滚降)MaxDistance是声音停止衰减的距离
public float MaxDistance;
/// <summary>
/// 一组默认选项,旨在适应最常见的情况。
///使用选项时,最好从这个开始,只覆盖你需要覆盖的内容。
///
///示例:
///
///MMSoundManagerPlayOptions选项=MMSoundManagerPlay选项。违约;
///选项。Loop=循环;
///选项。位置=Vector3.zero;
///选项。MmSoundManagerTrack=MMSoundManager。MMSoundManager曲目。音乐;
///
///MMSoundManager声音播放事件。触发器(声音剪辑,选项);
///
///在这里,我们初始化一个新的本地选项集,覆盖其循环、位置和轨迹设置,并使用它调用一个播放事件
///
/// </summary>
public static MMSoundManagerPlayOptions Default
{
get
{
MMSoundManagerPlayOptions defaultOptions = new MMSoundManagerPlayOptions();
defaultOptions.MmSoundManagerTrack = MMSoundManager.MMSoundManagerTracks.Sfx;
defaultOptions.Location = Vector3.zero;
defaultOptions.Loop = false;
defaultOptions.Volume = 1.0f;
defaultOptions.ID = 0;
defaultOptions.Fade = false;
defaultOptions.FadeInitialVolume = 0f;
defaultOptions.FadeDuration = 1f;
defaultOptions.FadeTween = null;
defaultOptions.Persistent = false;
defaultOptions.RecycleAudioSource = null;
defaultOptions.AudioGroup = null;
defaultOptions.Pitch = 1f;
defaultOptions.PanStereo = 0f;
defaultOptions.SpatialBlend = 0.0f;
defaultOptions.SoloSingleTrack = false;
defaultOptions.SoloAllTracks = false;
defaultOptions.AutoUnSoloOnEnd = false;
defaultOptions.BypassEffects = false;
defaultOptions.BypassListenerEffects = false;
defaultOptions.BypassReverbZones = false;
defaultOptions.Priority = 128;
defaultOptions.ReverbZoneMix = 1f;
defaultOptions.DopplerLevel = 1f;
defaultOptions.Spread = 0;
defaultOptions.RolloffMode = AudioRolloffMode.Logarithmic;
defaultOptions.MinDistance = 1f;
defaultOptions.MaxDistance = 500f;
return defaultOptions;
}
}
}
}
播放监听
using System.Collections;
using System.Collections.Generic;
using UnityEngine;
using UnityEngine.Audio;
namespace MoreMountains.Tools
{
/// <summary>
/// 此事件将允许您在MMSoundManager上播放声音
///
///示例:MMSoundManager声音播放事件。触发器(ExplosionSfx,MMSoundManager.MMSoundManagerTracks.Sfx,this.transform.position);
///将在SFX轨道上调用它的对象的位置播放一个剪辑(这里我们的剪辑称为ExplosionSfx)
/// </summary>
public class MMSoundManagerSoundPlayEvent
{
public delegate AudioSource Delegate(AudioClip clip, MMSoundManagerPlayOptions options);
//event是delegate 的一种特例,就是受限制的delegate
static private event Delegate OnEvent;
static public void Register(Delegate callback)
{
OnEvent += callback;
}
static public void Unregister(Delegate callback)
{
OnEvent -= callback;
}
static public AudioSource Trigger(AudioClip clip, MMSoundManagerPlayOptions options)
{
return OnEvent?.Invoke(clip, options);
}
static public AudioSource Trigger(AudioClip audioClip, MMSoundManager.MMSoundManagerTracks mmSoundManagerTrack, Vector3 location,
bool loop = false, float volume = 1.0f, int ID = 0,
bool fade = false, float fadeInitialVolume = 0f, float fadeDuration = 1f, MMTweenType fadeTween = null,
bool persistent = false,
AudioSource recycleAudioSource = null, AudioMixerGroup audioGroup = null,
float pitch = 1f, float panStereo = 0f, float spatialBlend = 0.0f,
bool soloSingleTrack = false, bool soloAllTracks = false, bool autoUnSoloOnEnd = false,
bool bypassEffects = false, bool bypassListenerEffects = false, bool bypassReverbZones = false, int priority = 128, float reverbZoneMix = 1f,
float dopplerLevel = 1f, int spread = 0, AudioRolloffMode rolloffMode = AudioRolloffMode.Logarithmic, float minDistance = 1f, float maxDistance = 500f)
{
MMSoundManagerPlayOptions options = MMSoundManagerPlayOptions.Default;
options.MmSoundManagerTrack = mmSoundManagerTrack;
options.Location = location;
options.Loop = loop;
options.Volume = volume;
options.ID = ID;
options.Fade = fade;
options.FadeInitialVolume = fadeInitialVolume;
options.FadeDuration = fadeDuration;
options.FadeTween = fadeTween;
options.Persistent = persistent;
options.RecycleAudioSource = recycleAudioSource;
options.AudioGroup = audioGroup;
options.Pitch = pitch;
options.PanStereo = panStereo;
options.SpatialBlend = spatialBlend;
options.SoloSingleTrack = soloSingleTrack;
options.SoloAllTracks = soloAllTracks;
options.AutoUnSoloOnEnd = autoUnSoloOnEnd;
options.BypassEffects = bypassEffects;
options.BypassListenerEffects = bypassListenerEffects;
options.BypassReverbZones = bypassReverbZones;
options.Priority = priority;
options.ReverbZoneMix = reverbZoneMix;
options.DopplerLevel = dopplerLevel;
options.Spread = spread;
options.RolloffMode = rolloffMode;
options.MinDistance = minDistance;
options.MaxDistance = maxDistance;
return OnEvent?.Invoke(audioClip, options);
}
}
}
对象池
using System;
using System.Collections;
using System.Collections.Generic;
using UnityEngine;
using UnityEngine.SceneManagement;
namespace MoreMountains.Tools
{
/// <summary>
/// 此类管理音频源的对象池
/// </summary>
[Serializable]
public class MMSoundManagerAudioPool
{
protected List<AudioSource> _pool;
/// <summary>
/// 用即用型音频源填满游泳池
/// </summary>
/// <param name="poolSize"></param>
/// <param name="parent"></param>
public virtual void FillAudioSourcePool(int poolSize, Transform parent)
{
if (_pool == null)
{
_pool = new List<AudioSource>();
}
if ((poolSize <= 0) || (_pool.Count >= poolSize))
{
return;
}
foreach (AudioSource source in _pool)
{
UnityEngine.Object.Destroy(source.gameObject);
}
for (int i = 0; i < poolSize; i++)
{
GameObject temporaryAudioHost = new GameObject("MMAudioSourcePool_" + i);
SceneManager.MoveGameObjectToScene(temporaryAudioHost.gameObject, parent.gameObject.scene);
AudioSource tempSource = temporaryAudioHost.AddComponent<AudioSource>();
temporaryAudioHost.transform.SetParent(parent);
temporaryAudioHost.SetActive(false);
_pool.Add(tempSource);
}
}
/// <summary>
/// 播放完毕后禁用音频源
/// </summary>
/// <param name="duration"></param>
/// <param name="targetObject"></param>
/// <returns></returns>
public virtual IEnumerator AutoDisableAudioSource(float duration, AudioSource source, AudioClip clip)
{
yield return MMCoroutine.WaitFor(duration);
if (source.clip != clip)
{
yield break;
}
source.gameObject.SetActive(false);
}
/// <summary>
/// 从池中提取可用的音频源
/// </summary>
/// <param name="poolCanExpand"></param>
/// <param name="parent"></param>
/// <returns></returns>
public virtual AudioSource GetAvailableAudioSource(bool poolCanExpand, Transform parent)
{
foreach (AudioSource source in _pool)
{
if (!source.gameObject.activeInHierarchy)
{
source.gameObject.SetActive(true);
return source;
}
}
if (poolCanExpand)
{
GameObject temporaryAudioHost = new GameObject("MMAudioSourcePool_" + _pool.Count);
SceneManager.MoveGameObjectToScene(temporaryAudioHost.gameObject, parent.gameObject.scene);
AudioSource tempSource = temporaryAudioHost.AddComponent<AudioSource>();
temporaryAudioHost.transform.SetParent(parent);
temporaryAudioHost.SetActive(true);
_pool.Add(tempSource);
return tempSource;
}
return null;
}
/// <summary>
/// 停止音频源并将其返回到池中
/// </summary>
/// <param name="sourceToStop"></param>
/// <returns></returns>
public virtual bool FreeSound(AudioSource sourceToStop)
{
foreach (AudioSource source in _pool)
{
if (source == sourceToStop)
{
source.Stop();
source.gameObject.SetActive(false);
return true;
}
}
return false;
}
}
}
结构体
using System;
using System.Collections;
using System.Collections.Generic;
using UnityEngine;
namespace MoreMountains.Tools
{
/// <summary>
/// 一个简单的结构体,用于存储有关MMSoundManager播放的声音的信息
/// </summary>
[Serializable]
public struct MMSoundManagerSound
{
public int ID;
public MMSoundManager.MMSoundManagerTracks Track;
public AudioSource Source;
/// 此声音是否可以在多个场景中播放
public bool Persistent;
}
}
背景音乐
using MoreMountains.Tools;
using System.Collections;
using System.Collections.Generic;
using UnityEngine;
namespace MoreMountains.CorgiEngine
{
/// <summary>
/// 将此类添加到GameObject中,使其在发出通知时播放背景音乐。
///小心:一次只能播放一首背景音乐。
/// </summary>
[AddComponentMenu("Corgi Engine/Audio/Background Music")]
public class BackgroundMusic : MMPersistentHumbleSingleton<BackgroundMusic>
{
[Tooltip("要播放的背景音乐音频片段")]
public AudioClip SoundClip;
[Tooltip("音乐是否应该循环播放")]
public bool Loop = true;
public AudioSource _source;
/// <summary>
/// 获取与该游戏对象关联的音频源,并要求游戏管理器播放它
/// </summary>
protected virtual void Start()
{
MMSoundManagerPlayOptions options = MMSoundManagerPlayOptions.Default;
options.Loop = Loop;
options.Location = Vector3.zero;
options.MmSoundManagerTrack = MMSoundManager.MMSoundManagerTracks.Music;
MMSoundManagerSoundPlayEvent.Trigger(SoundClip, options);
}
}
}