308 lines
9.6 KiB
C#
308 lines
9.6 KiB
C#
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);
|
||
}
|
||
} |