288 lines
9.0 KiB
C#
288 lines
9.0 KiB
C#
using System;
|
|
using UnityEngine;
|
|
using System.Collections.Generic;
|
|
using System.Threading;
|
|
using UnityEngine.SceneManagement;
|
|
|
|
namespace Stary.Evo.AudioCore
|
|
{
|
|
/// <summary>
|
|
/// 对象池
|
|
/// </summary>
|
|
public interface IAudioSourcePool
|
|
{
|
|
/// <summary>
|
|
/// 将对象存进池里
|
|
/// </summary>
|
|
/// <param name="obj"></param>
|
|
void Creation();
|
|
|
|
/// <summary>
|
|
/// 从未激活池里面取对象,并放入激活池
|
|
/// </summary>
|
|
/// <returns></returns>
|
|
AudioSourceToken Spawn();
|
|
|
|
/// <summary>
|
|
/// 从激活池中释放对象到未激活池中
|
|
/// </summary>
|
|
AudioSourceToken RecycleSpawn(AudioSourceToken source);
|
|
|
|
/// <summary>
|
|
/// 回收对象
|
|
/// </summary>
|
|
void RecycleAll();
|
|
|
|
/// <summary>
|
|
/// 清空对象池
|
|
/// </summary>
|
|
void RemoveAll();
|
|
|
|
int GetPolLength();
|
|
}
|
|
|
|
public class AudioSourcePool : IAudioSourcePool
|
|
{
|
|
private Transform active;
|
|
|
|
/// <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;
|
|
var poolObject = GameObject.Find(poolName);
|
|
if (poolObject == null)
|
|
{
|
|
// 如果不存在,创建一个新对象
|
|
active = new GameObject(poolName).transform;
|
|
}
|
|
else
|
|
{
|
|
active = poolObject.transform;
|
|
}
|
|
|
|
var recycleObject = GameObject.Find($"recycle_{poolName}");
|
|
if (recycle == null)
|
|
{
|
|
// 如果不存在,创建一个新对象
|
|
recycle = new GameObject($"recycle_{poolName}").transform;
|
|
}
|
|
else
|
|
{
|
|
recycle = recycleObject.transform;
|
|
}
|
|
}
|
|
|
|
/// <summary>
|
|
/// 将对象存进池里
|
|
/// </summary>
|
|
/// <param name="obj"></param>
|
|
public void Creation()
|
|
{
|
|
AudioSource source = CreatAudioSource();
|
|
if (source == null)
|
|
{
|
|
Debug.LogErrorFormat("对象池【{0}】已达最大数量【{1}】,无法创建新对象", active.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.parent = active;
|
|
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}】,无法创建新对象", active.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(active.gameObject);
|
|
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($"{active.name}_{poolCount}").AddComponent<AudioSource>();
|
|
source.gameObject.transform.SetParent(recycle);
|
|
source.transform.SetParent(active.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 AudioSourceToken(AudioSourceToken sourceToken)
|
|
{
|
|
this.source = sourceToken.source;
|
|
this.cancellationToken = sourceToken.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
|
|
{
|
|
cts.Dispose();
|
|
}
|
|
catch (ObjectDisposedException)
|
|
{
|
|
// 已经被释放,无需处理
|
|
}
|
|
}
|
|
}
|
|
}
|
|
}
|
|
} |