1111
This commit is contained in:
@@ -164,11 +164,7 @@ public class VideoPanel : BasePanel
|
||||
private void OnStopMove()
|
||||
{
|
||||
Debug.Log("UnityEvo:视频播放完成");
|
||||
AudioCoreManager.SetMusicVolume(new AudioData()
|
||||
{
|
||||
fadeDuration = 2f,
|
||||
volume = 1f,
|
||||
});
|
||||
AudioCoreManager.SetMusicVolume(2f, 1f);
|
||||
_animator.Play("vid_maskDefault", 0, 0);
|
||||
if (renderTexture != null)
|
||||
{
|
||||
|
||||
@@ -25,11 +25,7 @@ public class VideoSystem : AbstractSystem, IVideoSystem
|
||||
|
||||
public async void PlayVideo(VideoPanel.VideoInfo info)
|
||||
{
|
||||
AudioCoreManager.SetMusicVolume(new AudioData()
|
||||
{
|
||||
fadeDuration = 2f,
|
||||
volume = 0f,
|
||||
});
|
||||
AudioCoreManager.SetMusicVolume(2f, 0f);
|
||||
await this.GetSystem<IPanelSystem>().PushQueue<VideoPanel>(parent:AppConfig.GetDefaultMainInstance().transform,packageName:"Main");
|
||||
this.GetSystem<IPanelSystem>().SendPanelEvent(ModeType.VideoStart,info);
|
||||
}
|
||||
|
||||
@@ -1,6 +1,8 @@
|
||||
using System.Collections;
|
||||
using System;
|
||||
using System.Collections;
|
||||
using System.Collections.Generic;
|
||||
using System.Threading;
|
||||
using Cysharp.Threading.Tasks;
|
||||
using UnityEngine;
|
||||
|
||||
namespace Stary.Evo.AudioCore
|
||||
@@ -15,6 +17,16 @@ namespace Stary.Evo.AudioCore
|
||||
/// </summary>
|
||||
public float delayTime;
|
||||
|
||||
/// <summary>
|
||||
/// 资源包名
|
||||
/// </summary>
|
||||
public string packageName;
|
||||
|
||||
/// <summary>
|
||||
/// 资源名
|
||||
/// </summary>
|
||||
public string assetName;
|
||||
|
||||
/// <summary>
|
||||
/// 音频
|
||||
/// </summary>
|
||||
@@ -59,36 +71,55 @@ namespace Stary.Evo.AudioCore
|
||||
/// 3D声音最大距离
|
||||
/// </summary>
|
||||
public float audio3DMaxDistance;
|
||||
|
||||
/// <summary>
|
||||
/// 异步取消令牌
|
||||
/// </summary>
|
||||
public CancellationToken cancellationToken;
|
||||
}
|
||||
|
||||
public interface IAudio
|
||||
{
|
||||
IResources Resources { get; set; }
|
||||
AudioSourcePool AudioSourcePool { get; set; }
|
||||
AudioData Initialize(AudioData audioData);
|
||||
void Play(AudioData audioData);
|
||||
|
||||
void Stop(AudioData audioData);
|
||||
UniTask PlayAsync(AudioData audioData);
|
||||
UniTask FadeInMusic(AudioSource source, float fadeDuration);
|
||||
UniTask FadeOutMusic(AudioSource source, float fadeDuration);
|
||||
|
||||
|
||||
void Stop();
|
||||
|
||||
void StopAll();
|
||||
}
|
||||
|
||||
public abstract class AbstractAudio : IAudio
|
||||
{
|
||||
public IResources Resources { get; set; }
|
||||
|
||||
public AudioSourcePool AudioSourcePool { get; set; }
|
||||
public abstract void Play(AudioData audioData);
|
||||
|
||||
public virtual void Stop(AudioData audioData){}
|
||||
public abstract UniTask PlayAsync(AudioData audioData);
|
||||
|
||||
public virtual void Stop(){}
|
||||
public abstract void StopAll();
|
||||
|
||||
|
||||
public virtual void Stop()
|
||||
{
|
||||
}
|
||||
|
||||
protected AbstractAudio(IResources resources)
|
||||
{
|
||||
Resources = resources;
|
||||
if (!GetType().Name.Contains("Music"))
|
||||
{
|
||||
AudioSourcePool = new AudioSourcePool(GetType().Name, -1);
|
||||
AudioSourcePool.Creation();
|
||||
}
|
||||
}
|
||||
/// <summary>
|
||||
/// 音频数据初始化
|
||||
/// </summary>
|
||||
/// <param name="audioData"></param>
|
||||
/// <returns></returns>
|
||||
public virtual AudioData AudioDataInitialize(AudioData audioData)
|
||||
public virtual AudioData Initialize(AudioData audioData)
|
||||
{
|
||||
if (audioData.volume == 0)
|
||||
{
|
||||
@@ -99,7 +130,88 @@ namespace Stary.Evo.AudioCore
|
||||
{
|
||||
audioData.fadeDuration = 1f;
|
||||
}
|
||||
|
||||
return audioData;
|
||||
}
|
||||
|
||||
/// <summary>
|
||||
/// 音频淡入
|
||||
/// </summary>
|
||||
/// <param name="source"></param>
|
||||
/// <param name="fadeDuration"></param>
|
||||
/// <returns></returns>
|
||||
public async UniTask FadeInMusic(AudioSource source, float fadeDuration)
|
||||
{
|
||||
float targetVolume = source.volume;
|
||||
source.volume = 0;
|
||||
|
||||
while (source.volume < targetVolume)
|
||||
{
|
||||
source.volume += targetVolume * Time.deltaTime / fadeDuration;
|
||||
await UniTask.Yield();
|
||||
}
|
||||
}
|
||||
|
||||
/// <summary>
|
||||
/// 音频淡出
|
||||
/// </summary>
|
||||
/// <param name="source"></param>
|
||||
/// <param name="fadeDuration"></param>
|
||||
/// <returns></returns>
|
||||
public async UniTask FadeOutMusic(AudioSource source, float fadeDuration)
|
||||
{
|
||||
if (source != null)
|
||||
{
|
||||
float startVolume = source.volume;
|
||||
|
||||
while (source.volume > 0)
|
||||
{
|
||||
source.volume -= startVolume * Time.deltaTime / fadeDuration;
|
||||
await UniTask.Yield();
|
||||
}
|
||||
|
||||
source.Stop();
|
||||
}
|
||||
}
|
||||
|
||||
/// <summary>
|
||||
/// 播放音效的协程
|
||||
/// </summary>
|
||||
/// <param name="source"></param>
|
||||
/// <param name="delay"></param>
|
||||
/// <param name="onComplete"></param>
|
||||
/// <returns></returns>
|
||||
public async UniTask PlayAudioAWait(AudioSourceToken audioSourceToken, float delay, System.Action onComplete)
|
||||
{
|
||||
await PlayAudioAWait(audioSourceToken, delay);
|
||||
onComplete?.Invoke();
|
||||
}
|
||||
|
||||
/// <summary>
|
||||
/// 播放音效的协程
|
||||
/// </summary>
|
||||
/// <param name="source"></param>
|
||||
/// <param name="delay"></param>
|
||||
/// <param name="onComplete"></param>
|
||||
/// <returns></returns>
|
||||
public async UniTask PlayAudioAWait(AudioSourceToken audioSourceToken, float delay)
|
||||
{
|
||||
Debug.Log($"PlayAudioAWait:{GetType().Name}");
|
||||
if (!GetType().Name.Contains("Voice"))
|
||||
{
|
||||
await UniTask.Delay(TimeSpan.FromSeconds(audioSourceToken.source.clip.length - 2f),
|
||||
cancellationToken: audioSourceToken.cancellationToken.Token);
|
||||
await FadeOutMusic(audioSourceToken.source, 2f);
|
||||
await UniTask.Delay(TimeSpan.FromSeconds(delay),
|
||||
cancellationToken: audioSourceToken.cancellationToken.Token);
|
||||
}
|
||||
else
|
||||
{
|
||||
await UniTask.Delay(TimeSpan.FromSeconds(audioSourceToken.source.clip.length + delay),
|
||||
cancellationToken: audioSourceToken.cancellationToken.Token);
|
||||
}
|
||||
|
||||
AudioSourcePool.RecycleSpawn(audioSourceToken);
|
||||
}
|
||||
}
|
||||
}
|
||||
15
Assets/04.AudioCore/RunTime/Abstract/IResources.cs
Normal file
15
Assets/04.AudioCore/RunTime/Abstract/IResources.cs
Normal file
@@ -0,0 +1,15 @@
|
||||
using Cysharp.Threading.Tasks;
|
||||
using UnityEngine;
|
||||
|
||||
namespace Stary.Evo.AudioCore
|
||||
{
|
||||
public interface IResources
|
||||
{
|
||||
/// <summary>
|
||||
/// 加载音频资源
|
||||
/// </summary>
|
||||
/// <param name="path"></param>
|
||||
/// <returns></returns>
|
||||
UniTask<T> LoadAssetAsync<T>(string packageName, string assetName) where T : Object;
|
||||
}
|
||||
}
|
||||
3
Assets/04.AudioCore/RunTime/Abstract/IResources.cs.meta
Normal file
3
Assets/04.AudioCore/RunTime/Abstract/IResources.cs.meta
Normal file
@@ -0,0 +1,3 @@
|
||||
fileFormatVersion: 2
|
||||
guid: 376ffa6a0d374fe69c2c2e30f0f6d244
|
||||
timeCreated: 1767163427
|
||||
@@ -1,148 +1,274 @@
|
||||
using System;
|
||||
using UnityEngine;
|
||||
using System.Collections.Generic;
|
||||
using System.Threading;
|
||||
using UnityEngine.SceneManagement;
|
||||
|
||||
namespace Stary.Evo.AudioCore
|
||||
{
|
||||
public class AudioSourcePool: IDisposable
|
||||
/// <summary>
|
||||
/// 对象池
|
||||
/// </summary>
|
||||
public interface IAudioSourcePool
|
||||
{
|
||||
private Dictionary<string, Queue<GameObject>> poolDict = new Dictionary<string, Queue<GameObject>>();
|
||||
private GameObject poolObject;
|
||||
/// <summary>
|
||||
/// 将对象存进池里
|
||||
/// </summary>
|
||||
/// <param name="obj"></param>
|
||||
void Creation();
|
||||
|
||||
/// <summary>
|
||||
/// 对象池初始化
|
||||
/// 从未激活池里面取对象,并放入激活池
|
||||
/// </summary>
|
||||
private void PoolAwake()
|
||||
{
|
||||
// 检查是否已经存在一个名为"AudioSourcePool"的对象
|
||||
SceneManager.sceneUnloaded += OnSceneUnloaded;
|
||||
poolObject = GameObject.Find("AudioSourcePool");
|
||||
if (poolObject == null)
|
||||
{
|
||||
// 如果不存在,创建一个新对象
|
||||
poolObject = new GameObject("AudioSourcePool");
|
||||
}
|
||||
|
||||
// 初始化 Voice 池(只有 1 个)
|
||||
poolDict["Voice"] = new Queue<GameObject>();
|
||||
CreateAudioSource("Voice");
|
||||
|
||||
// 初始化 Music 池(最多 2 个)
|
||||
poolDict["Music"] = new Queue<GameObject>();
|
||||
for (int i = 0; i < 2; i++)
|
||||
{
|
||||
CreateAudioSource("Music");
|
||||
}
|
||||
|
||||
// 初始化 SFX 池(初始 1 个,可动态扩展)
|
||||
poolDict["SFX"] = new Queue<GameObject>();
|
||||
CreateAudioSource("SFX");
|
||||
}
|
||||
|
||||
/// <summary>
|
||||
/// 创建对象
|
||||
/// </summary>
|
||||
/// <param name="type">定义对象类型</param>
|
||||
private void CreateAudioSource(string type)
|
||||
{
|
||||
GameObject newObject = new GameObject($"AudioSource_{type}");
|
||||
newObject.transform.SetParent(poolObject.transform); // 将新对象作为当前对象的子对象
|
||||
newObject.AddComponent<AudioSource>().playOnAwake = false; // 添加 AudioSource 组件并禁用自动播放
|
||||
if (type == "Music")
|
||||
{
|
||||
newObject.GetComponent<AudioSource>().loop = true;
|
||||
}
|
||||
poolDict[type].Enqueue(newObject);
|
||||
}
|
||||
|
||||
/// <summary>
|
||||
/// 获取对象
|
||||
/// </summary>
|
||||
/// <param name="type"></param>
|
||||
/// <returns></returns>
|
||||
public AudioSource GetAudioSource(string type)
|
||||
{
|
||||
if (poolObject == null)
|
||||
{
|
||||
PoolAwake();
|
||||
}
|
||||
AudioSourceToken Spawn();
|
||||
|
||||
if (!poolDict.ContainsKey(type))
|
||||
{
|
||||
Debug.LogError($"UnityEvo:对象池中不存在类型: {type}");
|
||||
return null;
|
||||
}
|
||||
|
||||
if (poolDict[type].Count == 0)
|
||||
{
|
||||
// 如果池为空,动态创建新的 GameObject(仅限 SFX 与 Voice)
|
||||
if (type == "SFX" || type == "Voice")
|
||||
{
|
||||
CreateAudioSource(type);
|
||||
}
|
||||
else
|
||||
{
|
||||
Debug.LogWarning($"UnityEvo:对象池 {type} 已用完,无法分配新的 AudioSource");
|
||||
return null;
|
||||
}
|
||||
|
||||
CreateAudioSource(type);
|
||||
}
|
||||
|
||||
GameObject audioObject = poolDict[type].Dequeue();
|
||||
AudioSource audioSource = audioObject.GetComponent<AudioSource>();
|
||||
return audioSource;
|
||||
}
|
||||
/// <summary>
|
||||
/// 从激活池中释放对象到未激活池中
|
||||
/// </summary>
|
||||
AudioSourceToken RecycleSpawn(AudioSourceToken source);
|
||||
|
||||
/// <summary>
|
||||
/// 回收对象
|
||||
/// </summary>
|
||||
/// <param name="type"></param>
|
||||
/// <param name="audioObject"></param>
|
||||
public void ReturnAudioSource(string type, GameObject audioObject)
|
||||
{
|
||||
if (!poolDict.ContainsKey(type))
|
||||
{
|
||||
Debug.LogError($"UnityEvo:对象池中不存在类型: {type}");
|
||||
return;
|
||||
void RecycleAll();
|
||||
|
||||
/// <summary>
|
||||
/// 清空对象池
|
||||
/// </summary>
|
||||
void RemoveAll();
|
||||
|
||||
int GetPolLength();
|
||||
}
|
||||
|
||||
AudioSource audioSource = audioObject.GetComponent<AudioSource>();
|
||||
audioSource.Stop(); // 停止播放
|
||||
audioSource.clip = null; // 清空音频剪辑
|
||||
audioSource.volume = 1f; // 音量大小恢复
|
||||
poolDict[type].Enqueue(audioObject); // 回收到对象池
|
||||
public class AudioSourcePool : IAudioSourcePool
|
||||
{
|
||||
private GameObject poolObject;
|
||||
|
||||
/// <summary>
|
||||
/// 回收对象的父物体
|
||||
/// </summary>
|
||||
private Transform recycle;
|
||||
|
||||
protected List<AudioSourceToken> activepool = new List<AudioSourceToken>();
|
||||
|
||||
protected Queue<AudioSourceToken> inactivepool = new Queue<AudioSourceToken>();
|
||||
|
||||
//没有回收的个数
|
||||
protected int noRecycleCount;
|
||||
|
||||
/// <summary>
|
||||
/// 对象池物体总数量
|
||||
/// </summary>
|
||||
public int poolCount = 0;
|
||||
|
||||
/// <summary>
|
||||
/// 对象池最大数量
|
||||
/// </summary>
|
||||
private int poolSize = 0;
|
||||
|
||||
public AudioSourcePool(string poolName, int poolSize)
|
||||
{
|
||||
this.poolSize = poolSize;
|
||||
poolObject = GameObject.Find(poolName);
|
||||
if (poolObject == null)
|
||||
{
|
||||
// 如果不存在,创建一个新对象
|
||||
poolObject = new GameObject(poolName);
|
||||
}
|
||||
|
||||
poolObject = GameObject.Find($"recycle_{poolName}");
|
||||
if (poolObject == null)
|
||||
{
|
||||
// 如果不存在,创建一个新对象
|
||||
poolObject = new GameObject($"recycle_{poolName}");
|
||||
}
|
||||
}
|
||||
|
||||
/// <summary>
|
||||
/// 场景销毁时清空对象池
|
||||
/// 将对象存进池里
|
||||
/// </summary>
|
||||
/// <param name="scene"></param>
|
||||
void OnSceneUnloaded(Scene scene)
|
||||
/// <param name="obj"></param>
|
||||
public void Creation()
|
||||
{
|
||||
foreach (var pair in poolDict)
|
||||
AudioSource source = CreatAudioSource();
|
||||
if (source == null)
|
||||
{
|
||||
Queue<GameObject> queue = pair.Value;
|
||||
while (queue.Count > 0)
|
||||
{
|
||||
GameObject obj = queue.Dequeue();
|
||||
if (obj != null)
|
||||
{
|
||||
UnityEngine.Object.Destroy(obj);
|
||||
}
|
||||
}
|
||||
}
|
||||
poolDict.Clear();
|
||||
Debug.LogErrorFormat("对象池【{0}】已达最大数量【{1}】,无法创建新对象", poolObject.name, poolSize);
|
||||
return;
|
||||
}
|
||||
|
||||
// if (type == "Music")
|
||||
// {
|
||||
// newObject.GetComponent<AudioSource>().loop = true;
|
||||
// }
|
||||
inactivepool.Enqueue(new AudioSourceToken(source, new CancellationTokenSource()));
|
||||
|
||||
noRecycleCount++;
|
||||
}
|
||||
|
||||
|
||||
/// <summary>
|
||||
/// 从未激活池里面取对象,并放入激活池
|
||||
/// </summary>
|
||||
/// <param name="createIfPoolEmpty">如果为空是否new出来</param>
|
||||
public AudioSourceToken Spawn()
|
||||
{
|
||||
AudioSourceToken audioSourceToken = null;
|
||||
if (noRecycleCount > 0)
|
||||
{
|
||||
audioSourceToken = inactivepool.Dequeue();
|
||||
audioSourceToken.source.transform.SetParent(poolObject.gameObject.transform);
|
||||
noRecycleCount--;
|
||||
if (audioSourceToken.source == null)
|
||||
Debug.LogErrorFormat("对象池中不存在此对象【{0}】请排查代码", audioSourceToken.source);
|
||||
audioSourceToken.cancellationToken = new CancellationTokenSource();
|
||||
}
|
||||
else
|
||||
{
|
||||
var source = CreatAudioSource();
|
||||
if (source == null)
|
||||
{
|
||||
Debug.LogErrorFormat("对象池【{0}】已达最大数量【{1}】,无法创建新对象", poolObject.name, poolSize);
|
||||
return null;
|
||||
}
|
||||
|
||||
audioSourceToken = new AudioSourceToken(source, new CancellationTokenSource());
|
||||
}
|
||||
|
||||
audioSourceToken.source.transform.localPosition = Vector3.zero;
|
||||
audioSourceToken.source.transform.localRotation = Quaternion.identity;
|
||||
audioSourceToken.source.transform.localScale = Vector3.one;
|
||||
activepool.Add(audioSourceToken);
|
||||
|
||||
|
||||
return audioSourceToken;
|
||||
}
|
||||
|
||||
/// <summary>
|
||||
/// 从激活池中释放对象到未激活池中
|
||||
/// </summary>
|
||||
public AudioSourceToken RecycleSpawn(AudioSourceToken audioSourceToken)
|
||||
{
|
||||
if (audioSourceToken.source != null && activepool.Contains(audioSourceToken))
|
||||
{
|
||||
activepool.Remove(audioSourceToken);
|
||||
inactivepool.Enqueue(audioSourceToken);
|
||||
audioSourceToken.source.transform.parent = recycle;
|
||||
audioSourceToken.source.Stop(); // 停止播放
|
||||
audioSourceToken.source.clip = null; // 清空音频剪辑
|
||||
audioSourceToken.source.volume = 1f; // 音量大小恢复
|
||||
noRecycleCount++;
|
||||
// 释放CancellationToken资源
|
||||
audioSourceToken.Dispose();
|
||||
}
|
||||
|
||||
return null;
|
||||
}
|
||||
|
||||
/// <summary>
|
||||
/// 回收所有对象
|
||||
/// </summary>
|
||||
public void RecycleAll()
|
||||
{
|
||||
for (int i = 0; i < activepool.Count; i++)
|
||||
{
|
||||
RecycleSpawn(activepool[i]);
|
||||
}
|
||||
}
|
||||
|
||||
/// <summary>
|
||||
/// 清空对象池
|
||||
/// </summary>
|
||||
public void RemoveAll()
|
||||
{
|
||||
noRecycleCount = 0;
|
||||
poolCount = 0;
|
||||
GameObject.Destroy(poolObject);
|
||||
inactivepool.Clear();
|
||||
GameObject.Destroy(recycle.gameObject);
|
||||
for (int i = 0; i < activepool.Count; i++)
|
||||
{
|
||||
RecycleSpawn(activepool[i]);
|
||||
}
|
||||
|
||||
activepool.Clear();
|
||||
}
|
||||
|
||||
/// <summary>
|
||||
/// 创建一个新的AudioSource对象
|
||||
/// </summary>
|
||||
/// <param name="obj"></param>
|
||||
private AudioSource CreatAudioSource()
|
||||
{
|
||||
if (poolCount >= poolSize && poolSize > 0)
|
||||
return null;
|
||||
poolCount++;
|
||||
AudioSource source = new GameObject($"{poolObject.name}_{poolCount}").AddComponent<AudioSource>();
|
||||
source.gameObject.transform.SetParent(recycle);
|
||||
source.transform.SetParent(poolObject.transform); // 将新对象作为当前对象的子对象
|
||||
source.playOnAwake = false; // 添加 AudioSource 组件并禁用自动播放
|
||||
return source;
|
||||
}
|
||||
|
||||
public int GetPolLength()
|
||||
{
|
||||
return inactivepool.Count;
|
||||
}
|
||||
}
|
||||
|
||||
public class AudioSourceToken
|
||||
{
|
||||
public AudioSource source;
|
||||
public CancellationTokenSource cancellationToken;
|
||||
|
||||
public AudioSourceToken(AudioSource source, CancellationTokenSource cancellationToken)
|
||||
{
|
||||
this.source = source;
|
||||
this.cancellationToken = cancellationToken;
|
||||
}
|
||||
|
||||
// 添加释放方法
|
||||
public void Dispose()
|
||||
{
|
||||
if (poolObject != null)
|
||||
// 使用局部变量避免多线程环境下的竞态条件
|
||||
var cts = Interlocked.Exchange(ref cancellationToken, null);
|
||||
if (cts != null)
|
||||
{
|
||||
GameObject.Destroy(poolObject);
|
||||
try
|
||||
{
|
||||
// 取消令牌,触发注册的回调
|
||||
cts.Cancel();
|
||||
cts.Dispose();
|
||||
}
|
||||
catch (ObjectDisposedException)
|
||||
{
|
||||
// 已经被释放,无需处理
|
||||
}
|
||||
catch (AggregateException ex)
|
||||
{
|
||||
// 处理注册回调抛出的异常
|
||||
// 只记录第一个异常,避免日志过于冗长
|
||||
Debug.LogError($"CancellationTokenSource.Cancel() 回调抛出异常: {ex.InnerExceptions[0]}");
|
||||
// 可以选择不重新抛出,避免中断释放流程
|
||||
}
|
||||
catch (Exception ex)
|
||||
{
|
||||
Debug.LogError($"CancellationTokenSource.Cancel() 抛出异常: {ex}");
|
||||
}
|
||||
finally
|
||||
{
|
||||
// 确保始终释放资源
|
||||
try
|
||||
{
|
||||
cts.Dispose();
|
||||
}
|
||||
catch (ObjectDisposedException)
|
||||
{
|
||||
// 已经被释放,无需处理
|
||||
}
|
||||
}
|
||||
}
|
||||
poolDict.Clear();
|
||||
}
|
||||
}
|
||||
}
|
||||
@@ -1,11 +1,3 @@
|
||||
fileFormatVersion: 2
|
||||
guid: a03bd7e1c53a64843bdc29b9bd9c5bb6
|
||||
MonoImporter:
|
||||
externalObjects: {}
|
||||
serializedVersion: 2
|
||||
defaultReferences: []
|
||||
executionOrder: 0
|
||||
icon: {instanceID: 0}
|
||||
userData:
|
||||
assetBundleName:
|
||||
assetBundleVariant:
|
||||
guid: 1fdac552e09c46afb9a4116d1ce2fb8e
|
||||
timeCreated: 1767150802
|
||||
@@ -1,56 +0,0 @@
|
||||
using System;
|
||||
using System.Collections;
|
||||
using UnityEngine;
|
||||
using UnityEngine.Internal;
|
||||
|
||||
namespace Stary.Evo.AudioCore
|
||||
{
|
||||
// 音频协程启动器
|
||||
public class CoroutineHelper : IDisposable
|
||||
{
|
||||
private static CoroutineHelper _instance = new CoroutineHelper();
|
||||
public static CoroutineHelper Instance => _instance;
|
||||
private CoroutineRunner _coroutineRunner;
|
||||
|
||||
private CoroutineHelper()
|
||||
{
|
||||
GameObject runnerObject = new GameObject("CoroutineRunner");
|
||||
_coroutineRunner= runnerObject.AddComponent<CoroutineRunner>();
|
||||
}
|
||||
public Coroutine StartCoroutine(IEnumerator coroutine)
|
||||
{
|
||||
Coroutine myCoroutine = _coroutineRunner.StartIEnumerator(coroutine);
|
||||
return myCoroutine;
|
||||
}
|
||||
public void StopCoroutine(Coroutine myCoroutine)
|
||||
{
|
||||
_coroutineRunner.StopIEnumerator(myCoroutine);
|
||||
}
|
||||
public void Dispose()
|
||||
{
|
||||
if (_coroutineRunner != null)
|
||||
{
|
||||
GameObject.Destroy(_coroutineRunner.gameObject);
|
||||
_coroutineRunner = null;
|
||||
}
|
||||
if (CoroutineHelper.Instance != null)
|
||||
{
|
||||
CoroutineHelper._instance = null;
|
||||
}
|
||||
}
|
||||
|
||||
public class CoroutineRunner : MonoBehaviour
|
||||
{
|
||||
public Coroutine StartIEnumerator(IEnumerator coroutine)
|
||||
{
|
||||
Coroutine myCoroutine = StartCoroutine(coroutine);
|
||||
return myCoroutine;
|
||||
}
|
||||
public void StopIEnumerator(Coroutine myCoroutine)
|
||||
{
|
||||
StopCoroutine(myCoroutine);
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
@@ -1,11 +0,0 @@
|
||||
fileFormatVersion: 2
|
||||
guid: 6f7491906db8d634a8aa1655c3b5621a
|
||||
MonoImporter:
|
||||
externalObjects: {}
|
||||
serializedVersion: 2
|
||||
defaultReferences: []
|
||||
executionOrder: 0
|
||||
icon: {instanceID: 0}
|
||||
userData:
|
||||
assetBundleName:
|
||||
assetBundleVariant:
|
||||
@@ -1,19 +1,29 @@
|
||||
using System.Collections;
|
||||
using System;
|
||||
using Cysharp.Threading.Tasks;
|
||||
using UnityEngine;
|
||||
using Object = UnityEngine.Object;
|
||||
|
||||
namespace Stary.Evo.AudioCore
|
||||
{
|
||||
public class MusicPlayer : AbstractAudio
|
||||
public class MusicPlayer : AbstractAudio, IDisposable
|
||||
{
|
||||
private AudioSourcePool audioSourcePool;
|
||||
private GameObject poolObject;
|
||||
private AudioSource audioSource1;
|
||||
private AudioSource audioSource2;
|
||||
private AudioSource currentAudioSource;
|
||||
private Coroutine myCoroutine;
|
||||
|
||||
public MusicPlayer(AudioSourcePool audioSourcePool)
|
||||
public MusicPlayer(IResources resources) : base(resources)
|
||||
{
|
||||
this.audioSourcePool = audioSourcePool;
|
||||
poolObject = GameObject.Find(GetType().Name);
|
||||
if (poolObject == null)
|
||||
{
|
||||
// 如果不存在,创建一个新对象
|
||||
poolObject = new GameObject(GetType().Name);
|
||||
}
|
||||
|
||||
audioSource1 = CreatAudioSource("audioSource1", out AudioSource source1);
|
||||
audioSource2 = CreatAudioSource("audioSource2", out AudioSource source2);
|
||||
}
|
||||
|
||||
/// <summary>
|
||||
@@ -21,12 +31,37 @@ namespace Stary.Evo.AudioCore
|
||||
/// </summary>
|
||||
/// <param name="audioData">{[clip:音频], [volume:音量], [fadeDuration:自然过渡时间],
|
||||
/// [is3DAudio:是否3D音频], [audio3DPosition:3D音频位置], [audio3DMaxDistance:3D音频最大距离]}</param>
|
||||
public override void Play(AudioData audioData)
|
||||
public override async void Play(AudioData audioData)
|
||||
{
|
||||
audioData = AudioDataInitialize(audioData);
|
||||
if (Resources == null)
|
||||
{
|
||||
Debug.LogError("AudioCoreManager: Resources is null, please initialize it first.");
|
||||
return;
|
||||
}
|
||||
|
||||
audioData = Initialize(audioData);
|
||||
|
||||
if (!string.IsNullOrEmpty(audioData.packageName) && !string.IsNullOrEmpty(audioData.packageName))
|
||||
{
|
||||
var clip = await Resources.LoadAssetAsync<AudioClip>(audioData.packageName, audioData.assetName);
|
||||
if (clip == null)
|
||||
{
|
||||
Debug.LogErrorFormat($"从资源包【{audioData.packageName}】加载音频片段【{audioData.assetName}】失败");
|
||||
}
|
||||
else
|
||||
{
|
||||
audioData.clip = clip;
|
||||
}
|
||||
}
|
||||
|
||||
if (audioData.clip == null)
|
||||
{
|
||||
Debug.LogError("播放的音效音频片段为空");
|
||||
return;
|
||||
}
|
||||
|
||||
if (audioSource1 == null)
|
||||
{
|
||||
audioSource1 = audioSourcePool.GetAudioSource("Music");
|
||||
audioSource1.clip = audioData.clip;
|
||||
audioSource1.volume = audioData.volume;
|
||||
|
||||
@@ -62,7 +97,6 @@ namespace Stary.Evo.AudioCore
|
||||
{
|
||||
if (audioSource2 == null)
|
||||
{
|
||||
audioSource2 = audioSourcePool.GetAudioSource("Music");
|
||||
audioSource2.clip = audioData.clip;
|
||||
audioSource2.volume = audioData.volume;
|
||||
|
||||
@@ -99,33 +133,27 @@ namespace Stary.Evo.AudioCore
|
||||
Debug.LogWarning("UnityEvo:已同时存在两个背景乐在切换");
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
public override UniTask PlayAsync(AudioData audioData)
|
||||
{
|
||||
return UniTask.CompletedTask;
|
||||
}
|
||||
|
||||
public override void StopAll()
|
||||
{
|
||||
FadeAllMusic();
|
||||
}
|
||||
|
||||
/// <summary>
|
||||
/// 关闭背景音乐
|
||||
/// </summary>
|
||||
/// <param name="audioData">{[fadeDuration:自然过渡时间]}</param>
|
||||
public override void Stop(AudioData audioData)
|
||||
public override void Stop()
|
||||
{
|
||||
FadeAllMusic();
|
||||
}
|
||||
|
||||
/// <summary>
|
||||
/// 设置背景音乐音量
|
||||
/// </summary>
|
||||
/// <param name="audioData"></param>
|
||||
public void SetMusicVolume(AudioData audioData)
|
||||
{
|
||||
if(currentAudioSource == null) return;
|
||||
|
||||
if (myCoroutine != null)
|
||||
{
|
||||
CoroutineHelper.Instance.StopCoroutine(myCoroutine);
|
||||
myCoroutine = null;
|
||||
}
|
||||
myCoroutine = CoroutineHelper.Instance.StartCoroutine(SetMusicVolume(audioData.fadeDuration, audioData.volume));
|
||||
}
|
||||
|
||||
/// <summary>
|
||||
/// 设置自然过渡到指定音量
|
||||
@@ -133,10 +161,10 @@ namespace Stary.Evo.AudioCore
|
||||
/// <param name="fadeDuration"></param>
|
||||
/// <param name="targetVolume"></param>
|
||||
/// <returns></returns>
|
||||
private IEnumerator SetMusicVolume(float fadeDuration, float targetVolume)
|
||||
public async UniTask SetMusicVolume(float fadeDuration, float targetVolume)
|
||||
{
|
||||
float startVolume = currentAudioSource.volume;
|
||||
while (currentAudioSource.volume != targetVolume)
|
||||
while (!Mathf.Approximately(currentAudioSource.volume, targetVolume))
|
||||
{
|
||||
if (currentAudioSource.volume > targetVolume)
|
||||
{
|
||||
@@ -151,7 +179,8 @@ namespace Stary.Evo.AudioCore
|
||||
{
|
||||
currentAudioSource.volume = targetVolume;
|
||||
}
|
||||
yield return null;
|
||||
|
||||
await UniTask.Yield();
|
||||
}
|
||||
}
|
||||
|
||||
@@ -164,78 +193,39 @@ namespace Stary.Evo.AudioCore
|
||||
/// <returns></returns>
|
||||
private void FadeMusic(AudioSource source1, float fadeDuration, AudioSource source2 = null)
|
||||
{
|
||||
CoroutineHelper.Instance.StartCoroutine(FadeInMusic(source1, fadeDuration));
|
||||
|
||||
if (source2 != null)
|
||||
{
|
||||
CoroutineHelper.Instance.StartCoroutine(FadeOutMusic(source2, fadeDuration));
|
||||
FadeInMusic(source1, fadeDuration);
|
||||
FadeOutMusic(source2, fadeDuration);
|
||||
}
|
||||
|
||||
}
|
||||
|
||||
/// <summary>
|
||||
/// 关闭音频的协程
|
||||
/// </summary>
|
||||
/// <param name="source"></param>
|
||||
/// <param name="fadeDuration"></param>
|
||||
/// <returns></returns>
|
||||
private IEnumerator FadeOutMusic(AudioSource source, float fadeDuration)
|
||||
{
|
||||
if (source == null) yield break;
|
||||
float startVolume = source.volume;
|
||||
|
||||
while (source.volume > 0)
|
||||
{
|
||||
source.volume -= startVolume * Time.deltaTime / fadeDuration;
|
||||
yield return null;
|
||||
}
|
||||
|
||||
source.Stop();
|
||||
if (audioSource1 == source)
|
||||
{
|
||||
audioSource1 = null;
|
||||
}
|
||||
else if (audioSource2 == source)
|
||||
{
|
||||
audioSource2 = null;
|
||||
}
|
||||
audioSourcePool.ReturnAudioSource("Music", source.gameObject);
|
||||
}
|
||||
|
||||
private void FadeAllMusic()
|
||||
{
|
||||
if (currentAudioSource != null && currentAudioSource.isPlaying)
|
||||
{
|
||||
if (currentAudioSource == audioSource1)
|
||||
{
|
||||
audioSource1 = null;
|
||||
}
|
||||
else
|
||||
{
|
||||
audioSource2 = null;
|
||||
}
|
||||
audioSourcePool.ReturnAudioSource("Music", currentAudioSource.gameObject);
|
||||
currentAudioSource = null;
|
||||
}
|
||||
|
||||
audioSource1.Stop();
|
||||
audioSource2.Stop();
|
||||
}
|
||||
|
||||
/// <summary>
|
||||
/// 开启音频的协程
|
||||
/// 创建一个新的AudioSource对象
|
||||
/// </summary>
|
||||
/// <param name="source"></param>
|
||||
/// <param name="fadeDuration"></param>
|
||||
/// <returns></returns>
|
||||
private IEnumerator FadeInMusic(AudioSource source, float fadeDuration)
|
||||
/// <param name="obj"></param>
|
||||
private AudioSource CreatAudioSource(string name, out AudioSource source)
|
||||
{
|
||||
float targetVolume = source.volume;
|
||||
source.volume = 0;
|
||||
source = new GameObject(name).AddComponent<AudioSource>();
|
||||
source.gameObject.transform.SetParent(poolObject.transform);
|
||||
source.transform.SetParent(poolObject.transform); // 将新对象作为当前对象的子对象
|
||||
source.playOnAwake = false; // 添加 AudioSource 组件并禁用自动播放
|
||||
return source;
|
||||
}
|
||||
|
||||
while (source.volume < targetVolume)
|
||||
public void Dispose()
|
||||
{
|
||||
source.volume += targetVolume * Time.deltaTime / fadeDuration;
|
||||
yield return null;
|
||||
}
|
||||
FadeAllMusic();
|
||||
Object.Destroy(audioSource1.gameObject);
|
||||
Object.Destroy(audioSource2.gameObject);
|
||||
audioSource1 = null;
|
||||
audioSource2 = null;
|
||||
}
|
||||
}
|
||||
}
|
||||
@@ -1,17 +1,15 @@
|
||||
using System.Collections;
|
||||
using System;
|
||||
using System.Collections;
|
||||
using System.Collections.Generic;
|
||||
using Cysharp.Threading.Tasks;
|
||||
using UnityEngine;
|
||||
|
||||
namespace Stary.Evo.AudioCore
|
||||
{
|
||||
public class SFXPlayer : AbstractAudio
|
||||
public class SFXPlayer : AbstractAudio, IDisposable
|
||||
{
|
||||
private AudioSourcePool audioSourcePool;
|
||||
private List<AudioSource> activeSources = new List<AudioSource>(); // 正在播放的 AudioSource 列表
|
||||
|
||||
public SFXPlayer(AudioSourcePool audioSourcePool)
|
||||
public SFXPlayer(IResources resources) : base(resources)
|
||||
{
|
||||
this.audioSourcePool = audioSourcePool;
|
||||
}
|
||||
|
||||
/// <summary>
|
||||
@@ -20,18 +18,57 @@ namespace Stary.Evo.AudioCore
|
||||
/// <param name="audioData">{[clip:音频], [volume:音量],
|
||||
/// [onComplete:回调行为], [delayOnCompleteTime:延迟回调执行的时间],
|
||||
/// [is3DAudio:是否3D音频], [audio3DPosition:3D音频位置], [audio3DMaxDistance:3D音频最大距离]}</param>
|
||||
public override void Play(AudioData audioData)
|
||||
public override async void Play(AudioData audioData)
|
||||
{
|
||||
audioData = AudioDataInitialize(audioData);
|
||||
AudioSourceToken audioSourceToken = await PlayAudio(audioData);
|
||||
// 使用协程处理延迟和回调
|
||||
PlayAudioAWait(audioSourceToken, audioData.delayOnCompleteTime,
|
||||
audioData.onComplete);
|
||||
}
|
||||
|
||||
public override async UniTask PlayAsync(AudioData audioData)
|
||||
{
|
||||
AudioSourceToken audioSourceToken = await PlayAudio(audioData);
|
||||
await PlayAudioAWait(audioSourceToken, audioData.delayOnCompleteTime);
|
||||
}
|
||||
|
||||
|
||||
private async UniTask<AudioSourceToken> PlayAudio(AudioData audioData)
|
||||
{
|
||||
if (Resources == null)
|
||||
{
|
||||
Debug.LogError("AudioCoreManager: Resources is null, please initialize it first.");
|
||||
return null;
|
||||
}
|
||||
|
||||
audioData = Initialize(audioData);
|
||||
if (!string.IsNullOrEmpty(audioData.packageName) && !string.IsNullOrEmpty(audioData.packageName))
|
||||
{
|
||||
var clip = await Resources.LoadAssetAsync<AudioClip>(audioData.packageName, audioData.assetName);
|
||||
if (clip == null)
|
||||
{
|
||||
Debug.LogErrorFormat($"从资源包【{audioData.packageName}】加载音频片段【{audioData.assetName}】失败");
|
||||
}
|
||||
else
|
||||
{
|
||||
audioData.clip = clip;
|
||||
}
|
||||
}
|
||||
|
||||
if (audioData.clip == null)
|
||||
{
|
||||
Debug.LogError("播放的音效音频片段为空");
|
||||
return;
|
||||
return null;
|
||||
}
|
||||
|
||||
AudioSource source = audioSourcePool.GetAudioSource("SFX");
|
||||
if (source == null) return;
|
||||
AudioSourceToken audioSourceToken = AudioSourcePool.Spawn();
|
||||
if (audioSourceToken == null)
|
||||
{
|
||||
Debug.LogError("音效池为空,无法播放音效");
|
||||
return null;
|
||||
}
|
||||
|
||||
AudioSource source = audioSourceToken.source;
|
||||
source.clip = audioData.clip;
|
||||
source.volume = audioData.volume;
|
||||
|
||||
@@ -58,46 +95,27 @@ namespace Stary.Evo.AudioCore
|
||||
source.minDistance = 1f;
|
||||
source.maxDistance = 500f;
|
||||
}
|
||||
|
||||
source.Play();
|
||||
|
||||
// 将 AudioSource 加入活动列表
|
||||
activeSources.Add(source);
|
||||
|
||||
// 使用协程处理延迟和回调
|
||||
CoroutineHelper.Instance.StartCoroutine(PlaySFXCoroutine(source, audioData.delayOnCompleteTime, audioData.onComplete));
|
||||
return audioSourceToken;
|
||||
}
|
||||
|
||||
/// <summary>
|
||||
/// 停止所有音效
|
||||
/// </summary>
|
||||
/// <param name="audioData">{[无可使用变量]}</param>
|
||||
public override void Stop()
|
||||
{
|
||||
if (activeSources == null) return;
|
||||
foreach (var source in activeSources)
|
||||
{
|
||||
if (source.isPlaying)
|
||||
{
|
||||
source.Stop();
|
||||
audioSourcePool.ReturnAudioSource("SFX", source.gameObject);
|
||||
}
|
||||
}
|
||||
activeSources.Clear();
|
||||
AudioSourcePool.RecycleAll();
|
||||
}
|
||||
|
||||
/// <summary>
|
||||
/// 播放音效的协程
|
||||
/// </summary>
|
||||
/// <param name="source"></param>
|
||||
/// <param name="delay"></param>
|
||||
/// <param name="onComplete"></param>
|
||||
/// <returns></returns>
|
||||
private IEnumerator PlaySFXCoroutine(AudioSource source, float delay, System.Action onComplete)
|
||||
public override void StopAll()
|
||||
{
|
||||
yield return new WaitForSeconds(source.clip.length + delay);
|
||||
audioSourcePool.ReturnAudioSource("SFX", source.gameObject);
|
||||
onComplete?.Invoke();
|
||||
activeSources.Remove(source);
|
||||
AudioSourcePool.RecycleAll();
|
||||
}
|
||||
|
||||
public void Dispose()
|
||||
{
|
||||
AudioSourcePool.RecycleAll();
|
||||
}
|
||||
}
|
||||
}
|
||||
@@ -2,19 +2,17 @@
|
||||
using System.Collections;
|
||||
using System.Threading;
|
||||
using System.Threading.Tasks;
|
||||
using Cysharp.Threading.Tasks;
|
||||
using UnityEngine;
|
||||
|
||||
namespace Stary.Evo.AudioCore
|
||||
{
|
||||
public class VoicePlayer : AbstractAudio
|
||||
public class VoicePlayer : AbstractAudio, IDisposable
|
||||
{
|
||||
private AudioSourcePool audioSourcePool;
|
||||
private AudioSource currentSource;
|
||||
private Coroutine myCoroutine;
|
||||
private CancellationTokenSource cancelTokenSource;
|
||||
public VoicePlayer(AudioSourcePool audioSourcePool)
|
||||
private AudioSourceToken currentSource;
|
||||
|
||||
public VoicePlayer(IResources resources) : base(resources)
|
||||
{
|
||||
this.audioSourcePool = audioSourcePool;
|
||||
}
|
||||
|
||||
/// <summary>
|
||||
@@ -24,80 +22,12 @@ namespace Stary.Evo.AudioCore
|
||||
/// [onComplete:回调行为], [delayOnCompleteTime:延迟回调执行的时间],
|
||||
/// [is3DAudio:是否3D音频], [audio3DPosition:3D音频位置],
|
||||
/// [audio3DMaxDistance:3D音频最大距离]}</param>
|
||||
public override void Play(AudioData audioData)
|
||||
public override async void Play(AudioData audioData)
|
||||
{
|
||||
// 停止当前正在播放的语音与旧协程,或旧异步
|
||||
Stop();
|
||||
|
||||
audioData = AudioDataInitialize(audioData);
|
||||
|
||||
if (myCoroutine != null)
|
||||
{
|
||||
CoroutineHelper.Instance.StopCoroutine(myCoroutine);
|
||||
myCoroutine = null;
|
||||
}
|
||||
currentSource = audioSourcePool.GetAudioSource("Voice");
|
||||
if (currentSource == null) return;
|
||||
|
||||
currentSource.clip = audioData.clip;
|
||||
currentSource.volume = audioData.volume;
|
||||
|
||||
// 设置2D与3D音频
|
||||
if (audioData.is3DAudio)
|
||||
{
|
||||
currentSource.transform.position = audioData.audio3DPosition;
|
||||
currentSource.spatialBlend = 1;
|
||||
currentSource.minDistance = 1f;
|
||||
if (audioData.audio3DMaxDistance != 0)
|
||||
{
|
||||
currentSource.maxDistance = audioData.audio3DMaxDistance;
|
||||
}
|
||||
else
|
||||
{
|
||||
// 默认3D最大距离为3米
|
||||
currentSource.maxDistance = 3f;
|
||||
}
|
||||
}
|
||||
else
|
||||
{
|
||||
currentSource.transform.position = Vector3.zero;
|
||||
currentSource.spatialBlend = 0;
|
||||
currentSource.minDistance = 1f;
|
||||
currentSource.maxDistance = 500f;
|
||||
}
|
||||
|
||||
currentSource.Play();
|
||||
|
||||
AudioSourceToken audioSourceToken = await PlayAudio(audioData);
|
||||
// 使用协程处理延迟和回调
|
||||
myCoroutine = CoroutineHelper.Instance.StartCoroutine(PlayVoiceCoroutine(currentSource, audioData.delayOnCompleteTime, audioData.onComplete));
|
||||
}
|
||||
|
||||
/// <summary>
|
||||
/// 停止语音
|
||||
/// </summary>
|
||||
/// /// <param name="audioData">{[无可使用变量]}</param>
|
||||
public override void Stop()
|
||||
{
|
||||
// 停止当前协程
|
||||
if (currentSource != null && currentSource.isPlaying)
|
||||
{
|
||||
currentSource.Stop();
|
||||
audioSourcePool.ReturnAudioSource("Voice", currentSource.gameObject);
|
||||
currentSource = null;
|
||||
if (myCoroutine != null)
|
||||
{
|
||||
CoroutineHelper.Instance.StopCoroutine(myCoroutine);
|
||||
myCoroutine = null;
|
||||
}
|
||||
}
|
||||
|
||||
// 停掉异步任务
|
||||
if (cancelTokenSource != null && cancelTokenSource.IsCancellationRequested)
|
||||
{
|
||||
cancelTokenSource .Cancel();
|
||||
cancelTokenSource.Dispose();
|
||||
cancelTokenSource = null;
|
||||
}
|
||||
PlayAudioAWait(audioSourceToken,
|
||||
audioData.delayOnCompleteTime, audioData.onComplete);
|
||||
}
|
||||
|
||||
/// <summary>
|
||||
@@ -108,59 +38,108 @@ namespace Stary.Evo.AudioCore
|
||||
/// [is3DAudio:是否3D音频], [audio3DPosition:3D音频位置],
|
||||
/// [audio3DMaxDistance:3D音频最大距离],
|
||||
/// [cancellationToken:异步取消令牌]}</param>
|
||||
public async Task PlayAsync(AudioData audioData)
|
||||
public override async UniTask PlayAsync(AudioData audioData)
|
||||
{
|
||||
AudioSourceToken audioSourceToken = await PlayAudio(audioData);
|
||||
await PlayAudioAWait(audioSourceToken, audioData.delayOnCompleteTime);
|
||||
}
|
||||
|
||||
|
||||
private async UniTask<AudioSourceToken> PlayAudio(AudioData audioData)
|
||||
{
|
||||
if (Resources == null)
|
||||
{
|
||||
Debug.LogError("AudioCoreManager: Resources is null, please initialize it first.");
|
||||
return null;
|
||||
}
|
||||
|
||||
// 停止当前正在播放的语音与旧协程,或旧异步
|
||||
Stop();
|
||||
audioData = AudioDataInitialize(audioData);
|
||||
currentSource = audioSourcePool.GetAudioSource("Voice");
|
||||
if (currentSource == null) return;
|
||||
audioData = Initialize(audioData);
|
||||
if (!string.IsNullOrEmpty(audioData.packageName) && !string.IsNullOrEmpty(audioData.packageName))
|
||||
{
|
||||
var clip = await Resources.LoadAssetAsync<AudioClip>(audioData.packageName, audioData.assetName);
|
||||
if (clip == null)
|
||||
{
|
||||
Debug.LogErrorFormat($"从资源包【{audioData.packageName}】加载音频片段【{audioData.assetName}】失败");
|
||||
}
|
||||
else
|
||||
{
|
||||
audioData.clip = clip;
|
||||
}
|
||||
}
|
||||
|
||||
currentSource.clip = audioData.clip;
|
||||
currentSource.volume = audioData.volume;
|
||||
if (audioData.clip == null)
|
||||
{
|
||||
Debug.LogError("播放的音效音频片段为空");
|
||||
return null;
|
||||
}
|
||||
|
||||
|
||||
AudioSourceToken audioSourceToken = AudioSourcePool.Spawn();
|
||||
if (audioSourceToken == null)
|
||||
{
|
||||
Debug.LogError("音效池为空,无法播放音效");
|
||||
return null;
|
||||
}
|
||||
|
||||
currentSource = audioSourceToken;
|
||||
|
||||
currentSource.source.clip = audioData.clip;
|
||||
currentSource.source.volume = audioData.volume;
|
||||
|
||||
// 设置2D与3D音频
|
||||
if (audioData.is3DAudio)
|
||||
{
|
||||
currentSource.transform.position = audioData.audio3DPosition;
|
||||
currentSource.spatialBlend = 1;
|
||||
currentSource.minDistance = 1f;
|
||||
currentSource.source.transform.position = audioData.audio3DPosition;
|
||||
currentSource.source.spatialBlend = 1;
|
||||
currentSource.source.minDistance = 1f;
|
||||
if (audioData.audio3DMaxDistance != 0)
|
||||
{
|
||||
currentSource.maxDistance = audioData.audio3DMaxDistance;
|
||||
currentSource.source.maxDistance = audioData.audio3DMaxDistance;
|
||||
}
|
||||
else
|
||||
{
|
||||
// 默认3D最大距离为3米
|
||||
currentSource.maxDistance = 3f;
|
||||
currentSource.source.maxDistance = 3f;
|
||||
}
|
||||
}
|
||||
else
|
||||
{
|
||||
currentSource.transform.position = Vector3.zero;
|
||||
currentSource.spatialBlend = 0;
|
||||
currentSource.minDistance = 1f;
|
||||
currentSource.maxDistance = 500f;
|
||||
currentSource.source.transform.position = Vector3.zero;
|
||||
currentSource.source.spatialBlend = 0;
|
||||
currentSource.source.minDistance = 1f;
|
||||
currentSource.source.maxDistance = 500f;
|
||||
}
|
||||
|
||||
currentSource.Play();
|
||||
await Task.Delay(TimeSpan.FromSeconds(audioData.clip.length), audioData.cancellationToken);
|
||||
currentSource.source.Play();
|
||||
|
||||
return audioSourceToken;
|
||||
}
|
||||
|
||||
/// <summary>
|
||||
/// 播放语音的协程
|
||||
/// 停止语音
|
||||
/// </summary>
|
||||
/// <param name="source"></param>
|
||||
/// <param name="delayOnComplete"></param>
|
||||
/// <param name="onComplete"></param>
|
||||
/// <returns></returns>
|
||||
private IEnumerator PlayVoiceCoroutine(AudioSource source, float delayOnComplete, System.Action onComplete)
|
||||
/// /// <param name="audioData">{[无可使用变量]}</param>
|
||||
public override void Stop()
|
||||
{
|
||||
yield return new WaitForSeconds(source.clip.length + delayOnComplete);
|
||||
audioSourcePool.ReturnAudioSource("Voice", source.gameObject);
|
||||
onComplete?.Invoke();
|
||||
// 停止当前协程
|
||||
if (currentSource != null && currentSource.source.isPlaying)
|
||||
{
|
||||
currentSource.source.Stop();
|
||||
AudioSourcePool.RecycleSpawn(currentSource);
|
||||
currentSource = null;
|
||||
myCoroutine = null;
|
||||
}
|
||||
}
|
||||
|
||||
public override void StopAll()
|
||||
{
|
||||
AudioSourcePool.RecycleAll();
|
||||
}
|
||||
|
||||
public void Dispose()
|
||||
{
|
||||
AudioSourcePool.RecycleAll();
|
||||
}
|
||||
}
|
||||
}
|
||||
@@ -2,23 +2,25 @@
|
||||
using System.Collections.Generic;
|
||||
using System.Security.Cryptography.X509Certificates;
|
||||
using System.Threading.Tasks;
|
||||
using Cysharp.Threading.Tasks;
|
||||
|
||||
namespace Stary.Evo.AudioCore
|
||||
{
|
||||
public static class AudioCoreManager
|
||||
{
|
||||
private static AudioSourcePool audioSourcePool;
|
||||
private static VoicePlayer Voice;
|
||||
private static SFXPlayer SFX;
|
||||
private static IAudio Voice;
|
||||
private static IAudio SFX;
|
||||
private static MusicPlayer Music;
|
||||
private static IResources Resources;
|
||||
|
||||
public static void Initialize()
|
||||
public static void Initialize(IResources resources)
|
||||
{
|
||||
audioSourcePool = new AudioSourcePool();
|
||||
// 初始化播放器
|
||||
Voice = new VoicePlayer(audioSourcePool);
|
||||
SFX = new SFXPlayer(audioSourcePool);
|
||||
Music = new MusicPlayer(audioSourcePool);
|
||||
Resources = resources;
|
||||
Voice = new VoicePlayer(Resources);
|
||||
SFX = new SFXPlayer(Resources);
|
||||
Music = new MusicPlayer(Resources);
|
||||
|
||||
}
|
||||
|
||||
#region 语音
|
||||
@@ -32,8 +34,12 @@ namespace Stary.Evo.AudioCore
|
||||
/// [audio3DMaxDistance:3D音频最大距离]}</param>
|
||||
public static void PlayVoice(AudioData audioData)
|
||||
{
|
||||
if(Voice == null) Initialize();
|
||||
Voice.Play(audioData);
|
||||
if (Voice == null)
|
||||
{
|
||||
Debug.LogError("AudioCoreManager: Voice is null, please initialize it first.");
|
||||
return;
|
||||
}
|
||||
Voice?.Play(audioData);
|
||||
}
|
||||
|
||||
/// <summary>
|
||||
@@ -45,9 +51,13 @@ namespace Stary.Evo.AudioCore
|
||||
/// [audio3DMaxDistance:3D音频最大距离],
|
||||
/// [cancellationToken:异步取消令牌]}</param>
|
||||
/// <returns></returns>
|
||||
public static Task PlayAsyncVoice(AudioData audioData)
|
||||
public static UniTask PlayAsyncVoice(AudioData audioData)
|
||||
{
|
||||
if(Voice == null) Initialize();
|
||||
if (Voice == null)
|
||||
{
|
||||
Debug.LogError("AudioCoreManager: Voice is null, please initialize it first.");
|
||||
return UniTask.CompletedTask;
|
||||
}
|
||||
return Voice.PlayAsync(audioData);
|
||||
}
|
||||
|
||||
@@ -56,7 +66,11 @@ namespace Stary.Evo.AudioCore
|
||||
/// </summary>
|
||||
public static void StopVoice()
|
||||
{
|
||||
if(Voice == null) return;
|
||||
if (Voice == null)
|
||||
{
|
||||
Debug.LogError("AudioCoreManager: Voice is null, please initialize it first.");
|
||||
return;
|
||||
}
|
||||
Voice.Stop();
|
||||
}
|
||||
|
||||
@@ -73,7 +87,11 @@ namespace Stary.Evo.AudioCore
|
||||
/// [audio3DMaxDistance:3D音频最大距离]}</param>
|
||||
public static void PlaySFX(AudioData audioData)
|
||||
{
|
||||
if(SFX == null) Initialize();
|
||||
if (SFX == null)
|
||||
{
|
||||
Debug.LogError("AudioCoreManager: SFX is null, please initialize it first.");
|
||||
return;
|
||||
}
|
||||
SFX.Play(audioData);
|
||||
}
|
||||
|
||||
@@ -82,7 +100,11 @@ namespace Stary.Evo.AudioCore
|
||||
/// </summary>
|
||||
public static void StopAllSFX()
|
||||
{
|
||||
if(SFX == null) return;
|
||||
if (SFX == null)
|
||||
{
|
||||
Debug.LogError("AudioCoreManager: SFX is null, please initialize it first.");
|
||||
return;
|
||||
}
|
||||
AudioData audioData = new AudioData();
|
||||
SFX.Stop();
|
||||
}
|
||||
@@ -99,18 +121,28 @@ namespace Stary.Evo.AudioCore
|
||||
/// [audio3DMaxDistance:3D音频最大距离]}</param>
|
||||
public static void PlayMusic(AudioData audioData)
|
||||
{
|
||||
if(Music == null) Initialize();
|
||||
if (Music == null)
|
||||
{
|
||||
Debug.LogError("AudioCoreManager: Music is null, please initialize it first.");
|
||||
return;
|
||||
}
|
||||
Music.Play(audioData);
|
||||
}
|
||||
|
||||
/// <summary>
|
||||
/// 设置背景音乐音量
|
||||
/// Bgm设置自然过渡到指定音量
|
||||
/// </summary>
|
||||
/// <param name="audioData">{[volume:音量], [fadeDuration:自然过渡时间]}</param>
|
||||
public static void SetMusicVolume(AudioData audioData)
|
||||
/// <param name="fadeDuration"></param>
|
||||
/// <param name="targetVolume"></param>
|
||||
/// <returns></returns>
|
||||
public static void SetMusicVolume(float fadeDuration, float targetVolume)
|
||||
{
|
||||
if(Music == null) return;
|
||||
Music.SetMusicVolume(audioData);
|
||||
if (Music == null)
|
||||
{
|
||||
Debug.LogError("AudioCoreManager: Music is null, please initialize it first.");
|
||||
return;
|
||||
}
|
||||
Music.SetMusicVolume(fadeDuration, targetVolume);
|
||||
}
|
||||
|
||||
|
||||
@@ -120,10 +152,12 @@ namespace Stary.Evo.AudioCore
|
||||
/// <param name="fadeDuration">自然过渡时间</param>
|
||||
public static void StopMusic(float fadeDuration = 1f)
|
||||
{
|
||||
if(Music == null) return;
|
||||
AudioData audioData = new AudioData();
|
||||
audioData.fadeDuration = fadeDuration;
|
||||
Music.Stop(audioData);
|
||||
if (Music == null)
|
||||
{
|
||||
Debug.LogError("AudioCoreManager: Music is null, please initialize it first.");
|
||||
return;
|
||||
}
|
||||
Music.Stop();
|
||||
}
|
||||
|
||||
#endregion
|
||||
@@ -132,14 +166,14 @@ namespace Stary.Evo.AudioCore
|
||||
/// 重新初始化
|
||||
/// </summary>
|
||||
/// <returns></returns>
|
||||
public static void Reinitialize()
|
||||
public static void Dispose()
|
||||
{
|
||||
if (audioSourcePool == null) Initialize();
|
||||
|
||||
StopVoice();
|
||||
StopAllSFX();
|
||||
StopMusic();
|
||||
}
|
||||
|
||||
Voice = null;
|
||||
SFX = null;
|
||||
Music = null;
|
||||
}
|
||||
}
|
||||
}
|
||||
@@ -1,7 +1,9 @@
|
||||
{
|
||||
"name": "com.audiocore.runtime",
|
||||
"rootNamespace": "",
|
||||
"references": [],
|
||||
"references": [
|
||||
"GUID:f51ebe6a0ceec4240a699833d6309b23"
|
||||
],
|
||||
"includePlatforms": [],
|
||||
"excludePlatforms": [],
|
||||
"allowUnsafeCode": false,
|
||||
|
||||
@@ -1,6 +1,6 @@
|
||||
{
|
||||
"name": "com.staryevo.audiocore",
|
||||
"version": "1.0.20",
|
||||
"version": "1.1.0",
|
||||
"displayName": "04.AudioCore",
|
||||
"description": "音频播放工具",
|
||||
"unity": "2021.3",
|
||||
@@ -9,6 +9,9 @@
|
||||
"unity",
|
||||
"scirpt"
|
||||
],
|
||||
"dependencies": {
|
||||
"com.cysharp.unitask": "2.5.10"
|
||||
},
|
||||
"author": {
|
||||
"name": "staryEvo",
|
||||
"url": "https://www.unity3d.com"
|
||||
|
||||
Reference in New Issue
Block a user