308 lines
9.6 KiB
C#
Raw Normal View History

2024-08-23 15:49:34 +08:00
using System;
using System.Collections.Generic;
using Module.Log;
using UnityEngine;
using UnityEngine.Events;
using Object = UnityEngine.Object;
/// <summary>
/// 自主加载元件的管理池
/// </summary>
// 接收到需求时,如果元件池存在,则从元件池中释放一个物体;如果元件池不存在,则试图加载元件资源并建立元件池
public class AutoLoadPool<TItem, TLoadData, TLoadTask> where TItem : Component
where TLoadData : BaseLoadData<TItem, TLoadData>
where TLoadTask : BaseLoadTask<TItem, TLoadData, TLoadTask>, new()
{
public int inUseCount { get; private set; }
private readonly UnityAction<TItem> _createAction;
private readonly Transform _root;
private readonly bool _archive;
protected List<TLoadTask> loadTaskList;
// 用于标识组件池是否已经被析构的标志位,现在无法做组件析构顺序规划,暂时用这个代替
protected List<AssetPrefabPool<TItem>> poolList;
public AutoLoadPool(Transform root, bool archive, UnityAction<TItem> createAction)
{
_root = root;
_archive = archive;
_createAction = createAction;
poolList = new List<AssetPrefabPool<TItem>>();
loadTaskList = new List<TLoadTask>();
}
/// <summary>
/// 销毁自动加载池
/// </summary>
public void Kill()
{
for (var i = 0; i < poolList.Count; i++)
poolList[i].Kill();
poolList = null;
for (var i = 0; i < loadTaskList.Count; i++)
try
{
loadTaskList[i].ReleaseLoad();
}
catch (Exception e)
{
LogModule.ErrorLog(e.ToString());
}
inUseCount = 0;
loadTaskList = null;
Object.Destroy(_root.gameObject);
}
public void ShowDebugGui()
{
GUILayout.Label("Auto Pool " + poolList.Count + ", In Use Assets " + inUseCount);
for (var i = 0; i < poolList.Count; i++)
GUILayout.Label(poolList[i].assetName + ": " + poolList[i].inUseCount);
}
/// <summary>
/// 移除已经没有使用,且超过使用时间未使用的物体
/// </summary>
/// <param name="time"></param>
public void CleanUnusedPool(float time)
{
var removeTime = Time.realtimeSinceStartup - time;
for (var i = poolList.Count - 1; i >= 0; i--)
{
var pool = poolList[i];
if (pool.inUseCount <= 0 && pool.lastUseTime < removeTime)
{
pool.Kill(true);
poolList.RemoveAt(i);
}
}
}
/// <summary>
/// 从池中获得物品
/// </summary>
/// <param name="taskStarter">加载数据</param>
/// <param name="bundleName">资源包名称</param>
/// <param name="prefabName">物体名称</param>
public void PullItem(TLoadData taskStarter, string bundleName, string prefabName)
{
var pool = FindPool(bundleName, prefabName);
// 特效池不存在时,试图加载特效
if (pool == null)
{
var loadTask = FindLoadTask(bundleName, prefabName);
// 加载任务不存在时,试图加载特效
if (loadTask == null)
{
loadTask = new TLoadTask();
loadTaskList.Add(loadTask);
loadTask.Init(taskStarter, bundleName, prefabName, _archive, OnLoadTaskFinished);
}
else
{
loadTask.TryAddListener(taskStarter, bundleName, prefabName);
}
}
else if (taskStarter.callback != null)
{
taskStarter.callback(pool.PullItem(taskStarter.root), taskStarter);
inUseCount++;
}
}
protected virtual void OnLoadTaskFinished(TLoadTask loadTask)
{
loadTaskList.Remove(loadTask);
var prefab = loadTask.Prefab.GetComponent<TItem>();
var pool = new AssetPrefabPool<TItem>(loadTask.BundleName, loadTask.PrefabName, _archive, _root, prefab, _createAction);
poolList.Add(pool);
for (var i = 0; i < loadTask.taskListeners.Count; i++)
try
{
var loadData = loadTask.taskListeners[i];
if (loadData.callback != null)
{
inUseCount++;
loadData.callback(pool.PullItem(loadData.root), loadData);
}
}
catch (Exception e)
{
LogModule.ErrorLog(e.ToString());
}
}
/// <summary>
/// 将物品退回到物品池中
/// </summary>
/// <param name="bundleName">资源包名称</param>
/// <param name="prefabName">物体名称</param>
/// <param name="item">物体</param>
public void PushItem(string bundleName, string prefabName, TItem item)
{
var pool = FindPool(bundleName, prefabName);
if (pool != null)
{
inUseCount--;
pool.PushItem(item);
}
}
// 不使用物体,但是要求增加使用记录
// 主要用于预加载后,持有。
// 持有者需要主动调用ReduceCount来释放持有慎用。
// noEntity表示不是实体因此不会占用EffectLimit
public void AddCount(string bundleName, string prefabName, bool noEntity = false)
{
// 不退回物体,但是要求减少使用记录
var pool = poolList.Find(a => a.Match(bundleName, prefabName));
if (pool != null)
{
if (!noEntity)
inUseCount++;
pool.AddCount();
}
}
// noEntity表示不是实体因此不会占用EffectLimit
public void ReduceCount(string bundleName, string prefabName, bool noEntity = false)
{
// 不退回物体,但是要求减少使用记录
var pool = poolList.Find(a => a.Match(bundleName, prefabName));
if (pool != null)
{
if(!noEntity)
inUseCount--;
pool.ReduceCount();
}
}
/// <summary>
/// 预加载资源,不处理其他问题
/// </summary>
public bool Preload(string bundleName, string prefabName)
{
var pool = FindPool(bundleName, prefabName);
// 特效池不存在时,试图加载特效
if (pool == null)
{
var loadTask = FindLoadTask(bundleName, prefabName);
// 加载任务不存在时,试图加载特效
if (loadTask == null)
{
loadTask = new TLoadTask();
loadTaskList.Add(loadTask);
loadTask.Init(null, bundleName, prefabName, _archive, OnLoadTaskFinished);
}
}
return pool == null;
}
/// <summary>
/// 取消物体获得监听事件
/// </summary>
public void RemoveLoadTask(Predicate<TLoadData> predicate)
{
// if (!Killed)
// {
// 移除对加载事件的监听
for (var i = 0; i < loadTaskList.Count; i++)
try
{
// 注故意不移除loadTaskList仍然完成加载并建立物品池
loadTaskList[i].RemoveListener(predicate);
}
catch (Exception e)
{
LogModule.ErrorLog(e.ToString());
}
}
private TLoadTask FindLoadTask(string bundleName, string prefabName)
{
TLoadTask result = null;
for (var i = 0; i < loadTaskList.Count; i++)
if (loadTaskList[i].Match(bundleName, prefabName))
{
result = loadTaskList[i];
break;
}
return result;
}
private AssetPrefabPool<TItem> FindPool(string bundleName, string prefabName)
{
AssetPrefabPool<TItem> result = null;
for (var i = 0; i < poolList.Count; i++)
if (poolList[i].Match(bundleName, prefabName))
{
result = poolList[i];
break;
}
return result;
}
}
/// <summary>
/// 记录资源包名以及资源名称的池
/// </summary>
public class AssetPrefabPool<T> : CommonPool<T> where T : Component
{
public readonly string assetName;
public readonly string bundleName;
public readonly bool archive;
public AssetPrefabPool(string bundleName, string assetName, bool archive, Transform poolRoot, T prefab,
UnityAction<T> createAction = null) : base(poolRoot, prefab, createAction)
{
this.bundleName = bundleName;
this.assetName = assetName;
this.archive = archive;
lastUseTime = Time.realtimeSinceStartup;
}
public int inUseCount { get; private set; }
public float lastUseTime { get; private set; }
public bool Match(string pBundleName, string pAssetName)
{
return bundleName == pBundleName && assetName == pAssetName;
}
// 注已知播放完销毁的特效inUseCount会只增不减但这种情况下也不会返回资源池因此不需要清理
protected override void OnPullItem(T item)
{
base.OnPullItem(item);
inUseCount++;
}
protected override void OnPushItem(T item)
{
base.OnPushItem(item);
ReduceCount();
}
public void AddCount()
{
inUseCount = Mathf.Max(0, inUseCount) + 1;
lastUseTime = Time.realtimeSinceStartup;
}
public void ReduceCount()
{
inUseCount--;
if (inUseCount <= 0)
lastUseTime = Time.realtimeSinceStartup;
}
public override void Kill(bool destroyCache = false)
{
if (archive)
LoadAssetBundle.Instance.UnloadAsset(bundleName, assetName);
base.Kill(destroyCache);
}
}