using System; using System.Collections.Generic; using Module.Log; using UnityEngine; using UnityEngine.Events; using Object = UnityEngine.Object; /// /// 自主加载元件的管理池 /// // 接收到需求时,如果元件池存在,则从元件池中释放一个物体;如果元件池不存在,则试图加载元件资源并建立元件池 public class AutoLoadPool where TItem : Component where TLoadData : BaseLoadData where TLoadTask : BaseLoadTask, new() { public int inUseCount { get; private set; } private readonly UnityAction _createAction; private readonly Transform _root; private readonly bool _archive; protected List loadTaskList; // 用于标识组件池是否已经被析构的标志位,现在无法做组件析构顺序规划,暂时用这个代替 protected List> poolList; public AutoLoadPool(Transform root, bool archive, UnityAction createAction) { _root = root; _archive = archive; _createAction = createAction; poolList = new List>(); loadTaskList = new List(); } /// /// 销毁自动加载池 /// 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); } /// /// 移除已经没有使用,且超过使用时间未使用的物体 /// /// 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); } } } /// /// 从池中获得物品 /// /// 加载数据 /// 资源包名称 /// 物体名称 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(); var pool = new AssetPrefabPool(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()); } } /// /// 将物品退回到物品池中 /// /// 资源包名称 /// 物体名称 /// 物体 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(); } } /// /// 预加载资源,不处理其他问题 /// 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; } /// /// 取消物体获得监听事件 /// public void RemoveLoadTask(Predicate 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 FindPool(string bundleName, string prefabName) { AssetPrefabPool result = null; for (var i = 0; i < poolList.Count; i++) if (poolList[i].Match(bundleName, prefabName)) { result = poolList[i]; break; } return result; } } /// /// 记录资源包名以及资源名称的池 /// public class AssetPrefabPool : CommonPool 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 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); } }