This commit is contained in:
2024-10-16 00:03:41 +08:00
commit 897058435c
5033 changed files with 1009728 additions and 0 deletions

View File

@@ -0,0 +1,8 @@
fileFormatVersion: 2
guid: 1f76c21eafadf4e4a80036fdc0671ff4
folderAsset: yes
DefaultImporter:
externalObjects: {}
userData:
assetBundleName:
assetBundleVariant:

View File

@@ -0,0 +1,272 @@
using System;
using System.Collections.Generic;
using Audio;
using Framework.Timer;
using UnityEngine;
namespace Framework.Audio
{
/// <summary>
/// @Foldcc
/// 基于通用音频管理器派生而来针对该项目的使用需求改进部分api和逻辑
/// </summary>
public class AudioManager : BaseAudioManager
{
#region
private static AudioManager _instance;
public static AudioManager Instance
{
get
{
if (_instance != null) return _instance;
var obj = new GameObject { name = "[ AudioManager ]" };
DontDestroyOnLoad (obj);
_instance = obj.AddComponent<AudioManager> ();
obj.AddComponent<AudioListener> ();
//_instance.Init(new AudioSourceHandler(_AUDIO_Path));
return _instance;
}
}
#endregion
private static readonly string _MUSIC_VALUE = "MusicValue";
private static readonly string _SOUND_VALUE = "SoundValue";
public static readonly string _AUDIO_Path = "Assets/GameRes/Audio";
/// <summary>
/// 用于debug数据监视
/// </summary>
/// <returns></returns>
public Dictionary<string, string> EditorLOG ()
{
#if UNITY_EDITOR
return this.AudioSourceHandler?.EditorLog ();
#else
return null;
#endif
}
public Dictionary<string, string> CacheMusic;
public float SoundValue
{
get => this._localSoundVolume;
set
{
this._localSoundVolume = value;
this.SetGlobalVolume (AudioType.SOUND, value);
this.SaveVolume ();
}
}
public float MusicValue
{
get => this._localMusicVolume;
set
{
this._localMusicVolume = value;
this.SetGlobalVolume (AudioType.BGM, value);
this.SaveVolume ();
}
}
public float BSEValue
{
get => this._localBSEVolume;
set
{
this._localBSEVolume = value;
this.SetGlobalVolume (AudioType.BSE, value);
this.SaveVolume ();
}
}
private float _localMusicVolume;
private float _localSoundVolume;
private float _localBSEVolume;
public float GetGroupVolume (AudioType audioType)
{
switch (audioType)
{
case AudioType.BGM:
return MusicValue;
case AudioType.BSE:
return BSEValue;
case AudioType.SOUND:
return SoundValue;
}
return SoundValue;
}
private TimeHandler _curTimeHandler;
/// <summary>
/// 初始化音频管理器
/// </summary>
public override void Init (IAudioSourceHandler audioSourceHandler)
{
this.AudioSourceHandler = audioSourceHandler;
this.CacheMusic = new Dictionary<string, string> ();
//设置该项目下音频资源的根目录
// this.SetSourcePath (_AUDIO_Path);
//初始化音频资源加载器
//初始化音量
this.LoadVolume ();
// 提前缓存部分音轨,若游戏中同时播放很多音轨会自动增加额外音轨,这里的作用类似对象池预先缓存资源
this._AudioFree.Add (this.CreateAudioTrack (AudioType.BGM));
this._AudioFree.Add (this.CreateAudioTrack (AudioType.BSE));
this._AudioFree.Add (this.CreateAudioTrack (AudioType.BSE));
this._AudioFree.Add (this.CreateAudioTrack (AudioType.BSE));
this._AudioFree.Add (this.CreateAudioTrack (AudioType.SOUND));
this._AudioFree.Add (this.CreateAudioTrack (AudioType.SOUND));
this._AudioFree.Add (this.CreateAudioTrack (AudioType.SOUND));
//重要挂载Update 到 GameUpdaterMgr中用于回收音频资源以及音频播放的Update
GameUpdateMgr.Instance.AddUpdater (this.DoUpdate);
}
public bool InitHook ()
{
return true;
}
#region
/// <summary>
/// 存储当前各组音轨的音量设置
/// </summary>
public virtual void SaveVolume ()
{
AudioSourceHandler.SaveVolume (MusicValue, BSEValue, SoundValue);
}
/// <summary>
/// 读取本地音量设置
/// </summary>
public virtual void LoadVolume ()
{
var volumes = AudioSourceHandler.LoadVolume ();
this.MusicValue = volumes[0];
this.BSEValue = volumes[1];
this.SoundValue = volumes[2];
}
#endregion
#region API
/// <summary>
/// 卸载所有0引用的音频资源
/// </summary>
public void UnLoadAllInvalid ()
{
(this.AudioSourceHandler).UnLoadAllInvalid ();
}
/// <summary>
/// 获取bgm的音轨
/// </summary>
/// <returns></returns>
public AudioTrack GetBGM ()
{
var tracks = this.GetPlayingAudio ((ushort) AudioType.BGM);
if (tracks?.Count > 0)
{
return tracks[0];
}
return null;
}
/// <summary>
/// 用于播放背景音乐做特殊处理, 背景音乐只限制为一条音轨,如果新的背景音乐和当前相同,则继续播放,反之覆盖播放
/// </summary>
/// <param name="audioName"></param>
/// <param name="volume"></param>
/// <param name="audioGroup"></param>
/// <param name="inTime"></param>
/// <param name="isLoop"></param>
/// <returns></returns>
public AudioTrack PlayBGM (string audioName, string audioGroup, float volume = 1, float inTime = 0, bool isLoop = true)
{
var tracks = this.GetPlayingAudio ((ushort) AudioType.BGM);
if (tracks?.Count > 0)
{
if (audioName == tracks[0].AudioName)
{
tracks[0].UnPause (inTime);
return tracks[0];
}
else
{
tracks[0].Kill (inTime);
}
}
if (audioName != null)
{
var audioTrack = this.BasePlay (audioName, volume, 0, inTime, (ushort) AudioType.BGM, audioGroup, isLoop);
if (audioTrack != null)
{
(this._AudioPlaying[0], this._AudioPlaying[^1])
= (this._AudioPlaying[^1], this._AudioPlaying[0]);
return audioTrack;
}
}
return null;
}
public AudioTrack PlayBGM (string audioName, float volume = 1, float inTime = 0, bool isLoop = true)
{
return this.PlayBGM (audioName, null, volume, inTime, isLoop);
}
public AudioTrack PlayBSE (string audioName, float volume = 1, float inTime = 0, bool isLoop = true)
{
return this.BasePlay (audioName, volume, 0, inTime, AudioType.BSE, null, isLoop);
}
public AudioTrack PlayBSE (string audioName, string group, float volume = 1, float inTime = 0, bool isLoop = true)
{
return this.BasePlay (audioName, volume, 0, inTime, AudioType.BSE, group, isLoop);
}
public AudioTrack PlaySound (string audioName, string group, float volume = 1, float inTime = 0, bool isLoop = false)
{
return this.BasePlay (audioName, volume, 0, inTime, AudioType.SOUND, group, isLoop);
}
public AudioTrack PlaySound (string audioName, float volume = 1, float inTime = 0, bool isLoop = false)
{
return this.PlaySound (audioName, null, volume, inTime, isLoop);
}
public AudioTrack PlaySound3D (string audioName, Vector3 worldPosition, float volume = 1, float inTime = 0, bool isLoop = false)
{
var track = this.BasePlay (audioName, volume, 1, inTime, AudioType.SOUND, null, isLoop);
if (track != null)
{
track.transform.position = worldPosition;
}
return track;
}
public AudioTrack PlayBSE3D (string audioName, Vector3 worldPosition, float volume = 1, float inTime = 0, bool isLoop = false)
{
var track = this.BasePlay (audioName, volume, 1, inTime, AudioType.BSE, null, isLoop);
if (track != null)
track.transform.position = worldPosition;
return track;
}
#endregion
}
}

View File

@@ -0,0 +1,11 @@
fileFormatVersion: 2
guid: ff5d30ec7c6d4cd386536c7b9f70ddf4
MonoImporter:
externalObjects: {}
serializedVersion: 2
defaultReferences: []
executionOrder: 0
icon: {instanceID: 0}
userData:
assetBundleName:
assetBundleVariant:

View File

@@ -0,0 +1,247 @@
using Audio;
using Framework.Timer;
using Unity.Collections;
using UnityEngine;
namespace Framework.Audio
{
[RequireComponent (typeof(AudioSource))]
public class AudioTrack : MonoBehaviour
{
private AudioSource _audioSource;
private TimeHandler _timeHandler;
private TimeHandler _offsetTimeHandler;
private string _audioName;
private IAudioSourceHandler _audioSourceHandler;
[ReadOnly , SerializeField] private string _AudioGroup;
[ReadOnly , SerializeField] private AudioType _AudioType;
[ReadOnly , SerializeField] private float _Volume;
[ReadOnly , SerializeField] private float _VolumeOffset;
private float _LastVolumeOffset;
private float _TargetVolumeOffset;
public bool IsPause;
private bool _isReady;
private float CurVolume => this._Volume * this._VolumeOffset;
public bool IsDone =>
(this._isReady && this._audioSource.clip == null) ||
(!this.IsPause && !this._audioSource.isPlaying);
public AudioSource AudioSource => this._audioSource;
public string AudioGroup => this._AudioGroup;
public AudioType AudioType => this._AudioType;
public string AudioName => this._audioName;
/// <summary>
/// 设置音频音量缩放系数
/// </summary>
/// <param name="volumeOffset">[0-1]</param>
/// <param name="time"></param>
public void SetVolumeOffset (float volumeOffset , float time = 0)
{
this._LastVolumeOffset = _VolumeOffset;
this._TargetVolumeOffset = volumeOffset;
_offsetTimeHandler?.Kill ();
_offsetTimeHandler = GameUpdateMgr.Instance.CreateTimer (time, () =>
{
_VolumeOffset = _TargetVolumeOffset;
UpdateVolume ();
}, f =>
{
_VolumeOffset = Mathf.Lerp (_LastVolumeOffset, _TargetVolumeOffset, f);
UpdateVolume ();
});
void UpdateVolume ()
{
if (this._timeHandler?.IsDone ?? true)
{
this._audioSource.volume = this.CurVolume;
}
}
}
/// <summary>
/// 设置音频音量
/// </summary>
/// <param name="volume">[0-1]</param>
public void SetVolume (float volume)
{
this._Volume = volume;
if (this._timeHandler?.IsDone ?? true)
{
// Debug.Log($"设置音量 {this.CurVolume}", this);
this._audioSource.volume = this.CurVolume;
}
}
/// <summary>
/// 初始化音频文件
/// </summary>
/// <param name="audioSource"></param>
/// <param name="audioType"></param>
/// <param name="audioSourceHandler"></param>
/// <returns></returns>
internal AudioTrack Init (AudioSource audioSource, AudioType audioType , IAudioSourceHandler audioSourceHandler)
{
this._audioSourceHandler = audioSourceHandler;
this._audioSource = audioSource;
this._AudioType = audioType;
this._audioSource.playOnAwake = false;
this._isReady = false;
return this;
}
/// <summary>
/// 播放指定音频
/// </summary>
/// <param name="audioName"></param>
/// <param name="audioGroup"></param>
/// <param name="volumeOffset"></param>
/// <param name="volume"></param>
/// <param name="inTime"></param>
/// <param name="isLoop"></param>
public async void Play (string audioName, string audioGroup , float volumeOffset, float volume = 1, float inTime = 0,
bool isLoop = false)
{
this._AudioGroup = audioGroup;
this.IsPause = true;
this._isReady = false;
AudioClip audioClip = null;
if (this._audioSourceHandler.IsAsync ())
audioClip = await this._audioSourceHandler.GetAudioClipAsync (this._AudioType, audioName);
else
// ReSharper disable once MethodHasAsyncOverload
audioClip = this._audioSourceHandler.GetAudioClip (this._AudioType, audioName);
if (audioClip != null)
{
this._timeHandler?.Kill ();
this._offsetTimeHandler?.Kill ();
this.name = audioClip.name;
this._audioSource.loop = isLoop;
this._audioSource.pitch = 1;
this._audioSource.clip = audioClip;
this._audioName = audioName;
this._Volume = volume;
this._VolumeOffset = volumeOffset;
this._TargetVolumeOffset = volumeOffset;
if (inTime == 0)
{
this._audioSource.volume = this.CurVolume;
}
else
{
this._audioSource.volume = 0;
this._timeHandler = GameUpdateMgr.Instance.CreateTimer (inTime,
() => this._audioSource.volume = this.CurVolume,
f => this._audioSource.volume = f * this.CurVolume);
}
this.IsPause = false;
this._audioSource.Play ();
}
// Debug.Log("播放音乐 : " + audioName);
this._isReady = true;
}
/// <summary>
/// 暂停音频
/// </summary>
/// <param name="outTime"></param>
public void Pause (float outTime)
{
if (this.IsPause)
{
return;
}
this.IsPause = true;
this._timeHandler?.Kill ();
if (outTime == 0)
{
this._audioSource.Pause ();
}
else
{
this._timeHandler = GameUpdateMgr.Instance.CreateTimer (outTime, () => { this._audioSource.Pause (); },
f => { this._audioSource.volume = (1 - f) * this.CurVolume; });
}
}
/// <summary>
/// 恢复音频播放
/// </summary>
/// <param name="inTime"></param>
public void UnPause (float inTime)
{
if (!this.IsPause)
{
return;
}
this.IsPause = false;
this._timeHandler?.Kill ();
if (inTime == 0)
{
this._audioSource.volume = this.CurVolume;
}
else
{
this._timeHandler = GameUpdateMgr.Instance.CreateTimer (inTime,
() => { this._audioSource.volume = this.CurVolume; },
f => { this._audioSource.volume = f * this.CurVolume; });
}
this._audioSource.UnPause ();
}
/// <summary>
/// 销毁音频
/// </summary>
/// <param name="outTime"></param>
public void Kill (float outTime = 0)
{
this._timeHandler?.Kill ();
this._offsetTimeHandler?.Kill ();
if (outTime == 0)
{
this.OverReset ();
}
else
{
this._timeHandler = GameUpdateMgr.Instance.CreateTimer (outTime, this.OverReset,
f => this._audioSource.volume = (1 - f) * this.CurVolume);
}
}
/// <summary>
/// 重置音频播放器
/// </summary>
internal void OverReset ()
{
this._timeHandler?.Kill ();
this._offsetTimeHandler?.Kill ();
#if UNITY_EDITOR
this.name = $"[{this._AudioType}]Audio_Track";
#endif
this._AudioGroup = null;
if (this._audioSource.isPlaying)
this._audioSource.Stop ();
this._audioSource.clip = null;
this._audioSourceHandler.OnAudioInvalid (this._AudioType, this._audioName);
this._audioName = null;
this._audioSource.spatialBlend = 0;
this._audioSource.transform.localPosition = Vector3.zero;
}
}
}

View File

@@ -0,0 +1,11 @@
fileFormatVersion: 2
guid: d78fddb3fba44af98edab57cb5c782aa
MonoImporter:
externalObjects: {}
serializedVersion: 2
defaultReferences: []
executionOrder: 0
icon: {instanceID: 0}
userData:
assetBundleName:
assetBundleVariant:

View File

@@ -0,0 +1,9 @@
namespace Framework.Audio
{
public enum AudioType
{
BGM = 0,
BSE = 1,
SOUND = 2
}
}

View File

@@ -0,0 +1,11 @@
fileFormatVersion: 2
guid: ce653cae5b0d4fb1a5ffe29d74968abf
MonoImporter:
externalObjects: {}
serializedVersion: 2
defaultReferences: []
executionOrder: 0
icon: {instanceID: 0}
userData:
assetBundleName:
assetBundleVariant:

View File

@@ -0,0 +1,278 @@
using System;
using System.Collections.Generic;
using Framework.Audio;
using UnityEngine;
using AudioType = Framework.Audio.AudioType;
namespace Audio
{
/// <summary>
/// Foldcc
/// 通用音频管理器
/// </summary>
public class BaseAudioManager : MonoBehaviour
{
private static readonly int _MAX_TRACK = 20;
protected BaseAudioManager ()
{
this._AudioFree = new List<AudioTrack> ();
this._AudioPlaying = new List<AudioTrack> ();
this._GlobalAudioVolumeOffset = new Dictionary<AudioType, float> ();
}
protected List<AudioTrack> _AudioFree;
protected List<AudioTrack> _AudioPlaying;
protected Dictionary<AudioType, float> _GlobalAudioVolumeOffset; //全局音频音量缩放系数
protected Dictionary<string, float> _GroupVolumeOffset; //音频组音量缩放系数
// protected string SourcePath;
private bool _isActiveUpdater = true;
public IAudioSourceHandler AudioSourceHandler;
public virtual void Init (IAudioSourceHandler audioSourceHandler)
{
this.AudioSourceHandler = audioSourceHandler;
}
protected virtual void DoUpdate ()
{
// UtilProfiler.Instance.Start("AudioManager");
if (this._isActiveUpdater == false)
{
return;
}
for (int i = this._AudioPlaying.Count - 1; i >= 0; i--)
{
if (this._AudioPlaying[i].IsDone)
{
var audioTrack = this._AudioPlaying[i];
this._AudioPlaying.RemoveAt (i);
audioTrack.OverReset ();
if (this._AudioFree.Count < _MAX_TRACK)
this._AudioFree.Add (audioTrack);
else
Destroy (audioTrack.gameObject);
}
}
// UtilProfiler.Instance.End();
}
protected AudioTrack CreateAudioTrack (AudioType audioType)
{
var obj = new GameObject { name = $"[{audioType}]Audio_Track" };
var audioSource = obj.AddComponent<AudioSource> ();
obj.transform.SetParent (this.transform);
return obj.AddComponent<AudioTrack> ().Init (audioSource, audioType , this.AudioSourceHandler);
}
/// <summary>
/// 按类型设置音量
/// </summary>
/// <param name="audioType"></param>
/// <param name="volume"></param>
/// <param name="time"></param>
public void SetGlobalVolume (AudioType audioType, float volume , float time = 0)
{
ushort audioShort = (ushort)audioType;
this._GlobalAudioVolumeOffset[audioType] = Mathf.Clamp (volume, 0, 1);
var audios = this.GetPlayingAudio (audioType);
foreach (var audioTrack in audios)
{
audioTrack.SetVolumeOffset (GetVolumeOffset (audioType , audioTrack.AudioGroup) , time);
}
}
/// <summary>
/// 按组设置音量
/// </summary>
/// <param name="audioGroup"></param>
/// <param name="volume"></param>
/// <param name="time"></param>
public void SetGroupVolume (string audioGroup, float volume , float time = 0)
{
this._GroupVolumeOffset[audioGroup] = Mathf.Clamp (volume, 0, 1);
foreach (var audioPlayer in this._AudioPlaying)
{
if (audioPlayer.AudioGroup == audioGroup)
{
audioPlayer.SetVolumeOffset (GetVolumeOffset (audioPlayer.AudioType , audioGroup) , time);
}
}
}
public AudioTrack BasePlay (string audioName, float volume = 1, float spatialBlend = 0, float inTime = 0,
AudioType audioType = AudioType.SOUND, string audioGroup = null, bool isLoop = false)
{
if (string.IsNullOrEmpty (audioName))
{
return null;
}
var player = this.GetFreePlayer (audioType);
if (!this._GlobalAudioVolumeOffset.ContainsKey (audioType))
{
//默认值
this._GlobalAudioVolumeOffset[audioType] = 1;
}
player.AudioSource.spatialBlend = spatialBlend;
player.Play (audioName, audioGroup, this.GetVolumeOffset (audioType , audioGroup), volume, inTime, isLoop);
this._AudioPlaying.Add (player);
return player;
}
private float GetVolumeOffset (AudioType audioType , string audioGroup)
{
var volume = 1f;
if (!this._GlobalAudioVolumeOffset.ContainsKey (audioType))
{
//默认值
this._GlobalAudioVolumeOffset[audioType] = 1;
}
volume *= this._GlobalAudioVolumeOffset[audioType];
if (!string.IsNullOrEmpty (audioGroup))
{
if ( !this._GroupVolumeOffset.ContainsKey (audioGroup))
{
this._GroupVolumeOffset[audioGroup] = 1;
}
volume *= this._GroupVolumeOffset[audioGroup];
}
return volume;
}
/// <summary>
/// 按照音频类型暂停或者播放
/// </summary>
/// <param name="audioType"></param>
/// <param name="isPause"></param>
/// <param name="outTime"></param>
public void PausePlayers (AudioType audioType, bool isPause, float outTime = 0)
{
ushort audioShort = (ushort)audioType;
var players = this.GetPlayingAudio (audioType);
foreach (var audioPlayer in players)
{
if (isPause)
audioPlayer.Pause (outTime);
else
audioPlayer.UnPause (outTime);
}
}
/// <summary>
/// 按照音频组暂停或者播放
/// </summary>
/// <param name="audioGroup"></param>
/// <param name="isPause"></param>
/// <param name="outTime"></param>
public void PausePlayers (string audioGroup, bool isPause, float outTime = 0)
{
if (string.IsNullOrEmpty (audioGroup))
{
PauseAllPlayer (isPause , outTime);
return;
}
foreach (var audioPlayer in this._AudioPlaying)
{
if (audioPlayer.AudioGroup == audioGroup)
{
if (isPause)
audioPlayer.Pause (outTime);
else
audioPlayer.UnPause (outTime);
}
}
}
public void StopPlayers (string audioName, float outTime = 0)
{
foreach (var audioPlayer in this._AudioPlaying)
{
if (audioPlayer.AudioName == audioName)
{
audioPlayer.Kill (outTime);
}
}
}
public void StopPlayers (AudioType audioGroup, float outTime = 0)
{
ushort audioShort = (ushort)audioGroup;
var players = this.GetPlayingAudio (audioGroup);
foreach (var audioPlayer in players)
{
audioPlayer.Kill (outTime);
}
}
public void StopAllPlayer (float outTime = 0)
{
foreach (var audioPlayer in this._AudioPlaying)
{
audioPlayer.Kill (outTime);
}
}
public void PauseAllPlayer (bool isPause, float outTime = 0)
{
foreach (var audioPlayer in this._AudioPlaying)
{
if (isPause)
{
audioPlayer.Pause (outTime);
}
else
{
audioPlayer.UnPause (outTime);
}
}
}
protected AudioTrack GetFreePlayer (AudioType audioType)
{
foreach (var audioPlayer in this._AudioFree)
{
if (audioPlayer.AudioType == audioType)
{
this._AudioFree.Remove (audioPlayer);
return audioPlayer;
}
}
return this.CreateAudioTrack (audioType);
}
protected List<AudioTrack> GetPlayingAudio (AudioType audioType)
{
List<AudioTrack> audioPlayers = new List<AudioTrack> ();
foreach (var audioPlayer in this._AudioPlaying)
{
if (audioPlayer.AudioType == audioType)
{
audioPlayers.Add (audioPlayer);
}
}
return audioPlayers;
}
private void OnApplicationPause (bool pauseStatus)
{
this._isActiveUpdater = !pauseStatus;
}
}
}

View File

@@ -0,0 +1,11 @@
fileFormatVersion: 2
guid: d661110a59d865f42862815ff36fdd55
MonoImporter:
externalObjects: {}
serializedVersion: 2
defaultReferences: []
executionOrder: 0
icon: {instanceID: 0}
userData:
assetBundleName:
assetBundleVariant:

View File

@@ -0,0 +1,29 @@
/*
* Author: Foldcc
* Mail: lhyuau@qq.com
* IAudioSourceHandler
*/
using System.Collections.Generic;
using System.Threading.Tasks;
using UnityEngine;
using AudioType = Framework.Audio.AudioType;
namespace Audio
{
public interface IAudioSourceHandler
{
bool IsAsync ();
Task<AudioClip> GetAudioClipAsync (AudioType audioType, string audioName);
AudioClip GetAudioClip (AudioType audioType, string audioName);
void OnAudioInvalid (AudioType audioType, string audioName);
void UnLoadAllInvalid ();
float[] LoadVolume ();
void SaveVolume (float bgm, float bse, float se);
Dictionary<string, string> EditorLog ();
}
}

View File

@@ -0,0 +1,11 @@
fileFormatVersion: 2
guid: d2ba5c28c56f42909e53aa17e477822e
MonoImporter:
externalObjects: {}
serializedVersion: 2
defaultReferences: []
executionOrder: 0
icon: {instanceID: 0}
userData:
assetBundleName:
assetBundleVariant: