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);
|
|||
|
}
|
|||
|
}
|