1111
This commit is contained in:
@@ -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)
|
||||
void RecycleAll();
|
||||
|
||||
/// <summary>
|
||||
/// 清空对象池
|
||||
/// </summary>
|
||||
void RemoveAll();
|
||||
|
||||
int GetPolLength();
|
||||
}
|
||||
|
||||
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)
|
||||
{
|
||||
if (!poolDict.ContainsKey(type))
|
||||
this.poolSize = poolSize;
|
||||
poolObject = GameObject.Find(poolName);
|
||||
if (poolObject == null)
|
||||
{
|
||||
Debug.LogError($"UnityEvo:对象池中不存在类型: {type}");
|
||||
return;
|
||||
// 如果不存在,创建一个新对象
|
||||
poolObject = new GameObject(poolName);
|
||||
}
|
||||
|
||||
AudioSource audioSource = audioObject.GetComponent<AudioSource>();
|
||||
audioSource.Stop(); // 停止播放
|
||||
audioSource.clip = null; // 清空音频剪辑
|
||||
audioSource.volume = 1f; // 音量大小恢复
|
||||
poolDict[type].Enqueue(audioObject); // 回收到对象池
|
||||
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)
|
||||
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)
|
||||
{
|
||||
GameObject obj = queue.Dequeue();
|
||||
if (obj != 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()
|
||||
{
|
||||
// 使用局部变量避免多线程环境下的竞态条件
|
||||
var cts = Interlocked.Exchange(ref cancellationToken, null);
|
||||
if (cts != null)
|
||||
{
|
||||
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
|
||||
{
|
||||
UnityEngine.Object.Destroy(obj);
|
||||
cts.Dispose();
|
||||
}
|
||||
catch (ObjectDisposedException)
|
||||
{
|
||||
// 已经被释放,无需处理
|
||||
}
|
||||
}
|
||||
}
|
||||
poolDict.Clear();
|
||||
}
|
||||
|
||||
public void Dispose()
|
||||
{
|
||||
if (poolObject != null)
|
||||
{
|
||||
GameObject.Destroy(poolObject);
|
||||
}
|
||||
poolDict.Clear();
|
||||
}
|
||||
}
|
||||
}
|
||||
Reference in New Issue
Block a user