2024-08-23 15:49:34 +08:00

308 lines
9.6 KiB
C#
Raw Blame History

This file contains ambiguous Unicode characters

This file contains Unicode characters that might be confused with other characters. If you think that this is intentional, you can safely ignore this warning. Use the Escape button to reveal them.

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