| | |
| | | using System.Collections.Generic; |
| | | using System.Collections.Generic; |
| | | using UnityEngine; |
| | | using System; |
| | | using Cysharp.Threading.Tasks; |
| | | using System.Linq; |
| | | |
| | | #if UNITY_EDITOR |
| | | using UnityEngine.Profiling; |
| | | #endif |
| | | |
| | | |
| | | public class GameObjectPoolManager : SingletonMonobehaviour<GameObjectPoolManager> |
| | | { |
| | | #if UNITY_EDITOR |
| | | private Dictionary<int, DebugItem> m_DebugInstIDDict = null; |
| | | private bool m_ShowDebugPanel = false; |
| | | private Vector2 m_ScrollPosition = Vector2.zero; |
| | | |
| | | |
| | | private void OnGUI() |
| | | { |
| | | if (!m_ShowDebugPanel) return; |
| | | |
| | | GUILayout.BeginArea(new Rect(10, 10, 400, 600)); |
| | | GUILayout.BeginVertical("Box"); |
| | | m_ScrollPosition = GUILayout.BeginScrollView(m_ScrollPosition, GUILayout.Width(380), GUILayout.Height(580)); |
| | | |
| | | GUILayout.Label("对象池调试面板", GUILayout.Height(30)); |
| | | LogPoolMemoryUsage(); |
| | | GUILayout.Space(10); |
| | | |
| | | foreach (var poolEntry in m_PoolDict) |
| | | { |
| | | int prefabInstanceId = poolEntry.Key; |
| | | GameObjectPool pool = poolEntry.Value; |
| | | |
| | | GUILayout.BeginVertical("Box"); |
| | | GUILayout.Label($"池名称: {pool.Prefab.name}"); |
| | | GUILayout.Label($"活跃对象数: {pool.m_ActiveHashSet.Count}"); |
| | | GUILayout.Label($"空闲对象数: {pool.m_FreeQueue.Count}"); |
| | | int count = m_PoolUsageFrequency.TryGetValue(prefabInstanceId, out int frequency) ? frequency : 0; |
| | | GUILayout.Label($"使用频率: {count}"); |
| | | GUILayout.EndVertical(); |
| | | GUILayout.Space(5); |
| | | } |
| | | |
| | | GUILayout.EndScrollView(); |
| | | GUILayout.EndVertical(); |
| | | GUILayout.EndArea(); |
| | | } |
| | | |
| | | [UnityEditor.MenuItem("Tools/对象池/显示调试面板")] |
| | | private static void ToggleDebugPanel() |
| | | { |
| | | if (Instance != null) |
| | | { |
| | | Instance.m_ShowDebugPanel = !Instance.m_ShowDebugPanel; |
| | | } |
| | | } |
| | | |
| | | public void LogPoolMemoryUsage() |
| | | { |
| | | long totalMemory = 0; |
| | | long totalFreeMemory = 0; |
| | | var prefabMemoryDict = new Dictionary<string, long>(); |
| | | |
| | | foreach (var poolEntry in m_PoolDict) |
| | | { |
| | | int prefabInstanceId = poolEntry.Key; |
| | | GameObjectPool pool = poolEntry.Value; |
| | | string prefabName = pool.Prefab.name; |
| | | long prefabMemory = 0; |
| | | long freeMemory = 0; |
| | | |
| | | // 计算活跃对象的内存占用 |
| | | foreach (var gameObject in pool.m_ActiveHashSet) |
| | | { |
| | | if (gameObject != null) |
| | | { |
| | | long memory = Profiler.GetRuntimeMemorySizeLong(gameObject); |
| | | prefabMemory += memory; |
| | | } |
| | | } |
| | | |
| | | // 计算空闲对象的内存占用 |
| | | foreach (var gameObject in pool.m_FreeQueue) |
| | | { |
| | | if (gameObject != null) |
| | | { |
| | | long memory = Profiler.GetRuntimeMemorySizeLong(gameObject); |
| | | prefabMemory += memory; |
| | | freeMemory += memory; |
| | | } |
| | | } |
| | | |
| | | totalMemory += prefabMemory; |
| | | totalFreeMemory += freeMemory; |
| | | prefabMemoryDict[prefabName] = prefabMemory; |
| | | } |
| | | |
| | | // 按内存占用排序 |
| | | var sortedPrefabs = prefabMemoryDict.OrderByDescending(kv => kv.Value).Take(3).ToList(); |
| | | |
| | | GUILayout.Label($"总内存占用: {totalMemory / 1024} KB", GUILayout.Height(30)); |
| | | GUILayout.Label($"空闲内存占用: {totalFreeMemory / 1024} KB", GUILayout.Height(30)); |
| | | GUILayout.Label("占用最高的前3预制体名:", GUILayout.Height(30)); |
| | | foreach (var prefabInstance in sortedPrefabs) |
| | | { |
| | | GUILayout.BeginHorizontal("Box"); |
| | | GUILayout.Label($"{prefabInstance.Key}({prefabInstance.Value / 1024} KB)", GUILayout.Height(25)); |
| | | GUILayout.EndHorizontal(); |
| | | } |
| | | |
| | | } |
| | | |
| | | |
| | | #endif |
| | | // 池统计数据 |
| | | public Dictionary<int, PoolStats> PoolStatistics { get; private set; } = new Dictionary<int, PoolStats>(); |
| | | |
| | | public class PoolStats |
| | | { |
| | | public int TotalObjects { get; set; } |
| | | public int ActiveObjects { get; set; } |
| | | public int FreeObjects { get; set; } |
| | | public int UsageFrequency { get; set; } |
| | | } |
| | | |
| | | // 更新池统计数据 |
| | | private void UpdatePoolStats() |
| | | { |
| | | if (m_PoolDict == null) |
| | | return; |
| | | |
| | | foreach (var poolEntry in m_PoolDict) |
| | | { |
| | | int prefabInstanceId = poolEntry.Key; |
| | | GameObjectPool pool = poolEntry.Value; |
| | | |
| | | if (!PoolStatistics.ContainsKey(prefabInstanceId)) |
| | | { |
| | | PoolStatistics[prefabInstanceId] = new PoolStats(); |
| | | } |
| | | |
| | | PoolStats stats = PoolStatistics[prefabInstanceId]; |
| | | stats.TotalObjects = pool.m_ActiveHashSet.Count + pool.m_FreeQueue.Count; |
| | | stats.ActiveObjects = pool.m_ActiveHashSet.Count; |
| | | stats.FreeObjects = pool.m_FreeQueue.Count; |
| | | stats.UsageFrequency = m_PoolUsageFrequency.TryGetValue(prefabInstanceId, out int frequency) ? frequency : 0; |
| | | } |
| | | } |
| | | |
| | | |
| | | private Dictionary<int, GameObjectPool> m_PoolDict = null; |
| | | |
| | | // 不需要销毁的对象列表 |
| | | private List<int> dontDestoryGoInstIDList = null; |
| | | |
| | | private Transform m_TargetContainer; |
| | | |
| | | // 定期检查池的时间间隔(秒) |
| | | private float m_PoolCheckInterval = 600f; |
| | | |
| | | // 记录每个池的使用频率 |
| | | private Dictionary<int, int> m_PoolUsageFrequency = new Dictionary<int, int>(); |
| | | |
| | | //需要动态卸载的预制体,例如切换场景时 |
| | | public List<GameObject> needDestroyPrefabList = new List<GameObject>(); |
| | | |
| | | public void Initialize() |
| | | { |
| | |
| | | dontDestoryGoInstIDList = new List<int>(); |
| | | } |
| | | |
| | | #if UNITY_EDITOR |
| | | if (m_DebugInstIDDict == null) |
| | | // 启动定期检查池的协程 |
| | | CheckPoolUsage(); |
| | | } |
| | | |
| | | private async UniTask CheckPoolUsage() |
| | | { |
| | | while (true) |
| | | { |
| | | m_DebugInstIDDict = new Dictionary<int, DebugItem>(); |
| | | await UniTask.Delay(TimeSpan.FromSeconds(m_PoolCheckInterval)); |
| | | |
| | | foreach (var poolEntry in m_PoolDict) |
| | | { |
| | | int prefabInstanceId = poolEntry.Key; |
| | | GameObjectPool pool = poolEntry.Value; |
| | | |
| | | // 如果池的使用频率低于阈值,减少池的大小 |
| | | if (m_PoolUsageFrequency.TryGetValue(prefabInstanceId, out int usageCount) && usageCount < 5) |
| | | { |
| | | // 保留至少一个对象,避免频繁创建和销毁 |
| | | int targetSize = Mathf.Max(1, pool.m_FreeQueue.Count / 2); |
| | | while (pool.m_FreeQueue.Count > targetSize) |
| | | { |
| | | GameObject obj = pool.m_FreeQueue.Dequeue(); |
| | | if (!dontDestoryGoInstIDList.Contains(obj.GetInstanceID())) |
| | | { |
| | | Destroy(obj); |
| | | } |
| | | } |
| | | } |
| | | |
| | | // 重置使用频率 |
| | | m_PoolUsageFrequency[prefabInstanceId] = 0; |
| | | } |
| | | } |
| | | #endif |
| | | } |
| | | |
| | | public void AddDontDestroyGoInstID(int id) |
| | |
| | | return false; |
| | | } |
| | | |
| | | /// <summary> |
| | | /// 传入的为预制体,而非组件,否则会导致GetInstanceID管理混乱,每次实例化都会改变 |
| | | /// </summary> |
| | | /// <param name="prefab"></param> |
| | | /// <returns></returns> |
| | | public GameObjectPool RequestPool(GameObject prefab) |
| | | { |
| | | if (prefab == null) |
| | | { |
| | | return null; |
| | | } |
| | | |
| | | #if UNITY_EDITOR |
| | | if (UnityEditor.PrefabUtility.GetPrefabAssetType(prefab) == UnityEditor.PrefabAssetType.NotAPrefab) |
| | | { |
| | | Debug.LogError($"{prefab.name}不是一个预制体!"); |
| | | return null; |
| | | } |
| | | #endif |
| | | |
| | | |
| | | int _prefabInstanceId = prefab.GetInstanceID(); |
| | | |
| | |
| | | return null; |
| | | } |
| | | |
| | | #if UNITY_EDITOR |
| | | if (UnityEditor.PrefabUtility.GetPrefabAssetType(prefab) == UnityEditor.PrefabAssetType.NotAPrefab) |
| | | { |
| | | Debug.LogError($"{prefab.name}不是一个预制体!"); |
| | | return null; |
| | | } |
| | | #endif |
| | | return RequestPool(prefab).Request(); |
| | | } |
| | | |
| | | |
| | | //特定情况下需要动态卸载的,如切换场景时 |
| | | public void UnLoadAll(bool force = false) |
| | | { |
| | | List<int> _removeList = new List<int>(); |
| | | foreach (var _pool in m_PoolDict.Values) |
| | | { |
| | | // if (GAMgr.Instance.needDestroyPrefabList.Contains(_pool.Prefab)) |
| | | // { |
| | | if (needDestroyPrefabList.Contains(_pool.Prefab)) |
| | | { |
| | | _pool.Clear(); |
| | | _removeList.Add(_pool.PrefabInstanceId); |
| | | // } |
| | | } |
| | | } |
| | | |
| | | for (int i = 0; i < _removeList.Count; ++i) |
| | |
| | | return m_Prefab; |
| | | } |
| | | } |
| | | |
| | | //预制体实例id,用于检索池 |
| | | public int PrefabInstanceId |
| | | { |
| | | get; private set; |
| | | } |
| | | |
| | | public List<GameObject> m_ActiveList = null; |
| | | private List<GameObject> m_FreeList = null; |
| | | public HashSet<GameObject> m_ActiveHashSet = new HashSet<GameObject>(); |
| | | public Queue<GameObject> m_FreeQueue = null; |
| | | private int Pool_FreeList_Warning_Threshold = 10; //当空闲对象高于此值时,会进行日志输出提醒 |
| | | |
| | | Action<GameObject> releaseCallBack; |
| | | #if UNITY_EDITOR && !UNITY_ANDROID && !UNITY_IOS |
| | | private Transform m_DebugContainer; |
| | | private Transform m_DebugActive; |
| | | private Transform m_DebugFree; |
| | | #endif |
| | | |
| | | |
| | | public string assetBundleName; |
| | | public string assetName; |
| | |
| | | { |
| | | m_Prefab = prefab; |
| | | PrefabInstanceId = m_Prefab.GetInstanceID(); |
| | | m_ActiveList = new List<GameObject>(); |
| | | m_FreeList = new List<GameObject>(); |
| | | m_ActiveHashSet = new HashSet<GameObject>(); |
| | | m_FreeQueue = new Queue<GameObject>(); |
| | | |
| | | #if UNITY_EDITOR && !UNITY_ANDROID && !UNITY_IOS |
| | | m_DebugContainer = new GameObject(prefab.name).transform; |
| | | m_DebugActive = new GameObject("Active").transform; |
| | | m_DebugFree = new GameObject("Free").transform; |
| | | m_DebugActive.SetParent(m_DebugContainer); |
| | | m_DebugFree.SetParent(m_DebugContainer); |
| | | m_DebugContainer.SetParent(Instance.transform); |
| | | #endif |
| | | } |
| | | |
| | | public void AddReleaseListener(Action<GameObject> _callBack) |
| | |
| | | for (int i = 0; i < count; ++i) |
| | | { |
| | | _go = Instantiate(m_Prefab, Constants.Special_Hide_Position, Quaternion.identity); |
| | | m_FreeList.Add(_go); |
| | | m_FreeQueue.Enqueue(_go); |
| | | _go.transform.SetParent(Instance.m_TargetContainer); |
| | | _go.transform.position = Constants.Special_Hide_Position; |
| | | |
| | | #if UNITY_EDITOR && !UNITY_ANDROID && !UNITY_IOS |
| | | |
| | | DebugItem _debugItem = new GameObject(_go.GetInstanceID().ToString()).AddMissingComponent<DebugItem>(); |
| | | _debugItem.transform.SetParent(m_DebugFree); |
| | | _debugItem.target = _go; |
| | | Instance.m_DebugInstIDDict[_go.GetInstanceID()] = _debugItem; |
| | | #if UNITY_EDITOR |
| | | // 检查空闲列表数量是否超过阈值 |
| | | if (m_FreeQueue.Count > Pool_FreeList_Warning_Threshold) |
| | | { |
| | | Debug.LogWarning($"GameObjectPool {m_Prefab.name} 的空闲对象数量 ({m_FreeQueue.Count}) 超过阈值 ({Pool_FreeList_Warning_Threshold}),请检查是否需要清理。"); |
| | | } |
| | | #endif |
| | | var animator = _go.GetComponent<Animator>(); |
| | | if (animator != null) |
| | |
| | | { |
| | | GameObject _go = null; |
| | | |
| | | while (m_FreeList.Count > 0) |
| | | if (m_FreeQueue.Count > 0) |
| | | { |
| | | if (m_FreeList[0] != null) |
| | | _go = m_FreeQueue.Dequeue(); |
| | | |
| | | m_ActiveHashSet.Add(_go); |
| | | |
| | | // 统计池的使用频率 |
| | | if (Instance.m_PoolUsageFrequency.ContainsKey(m_Prefab.GetInstanceID())) |
| | | { |
| | | _go = m_FreeList[0]; |
| | | |
| | | m_ActiveList.Add(_go); |
| | | m_FreeList.RemoveAt(0); |
| | | |
| | | #if UNITY_EDITOR && !UNITY_ANDROID && !UNITY_IOS |
| | | if (Instance.m_DebugInstIDDict.ContainsKey(_go.GetInstanceID())) |
| | | { |
| | | DebugItem _debugItem = Instance.m_DebugInstIDDict[_go.GetInstanceID()]; |
| | | _debugItem.transform.SetParent(m_DebugActive); |
| | | } |
| | | #endif |
| | | break; |
| | | Instance.m_PoolUsageFrequency[m_Prefab.GetInstanceID()]++; |
| | | } |
| | | else |
| | | { |
| | | m_FreeList.RemoveAt(0); |
| | | Instance.m_PoolUsageFrequency[m_Prefab.GetInstanceID()] = 1; |
| | | } |
| | | |
| | | } |
| | | |
| | | |
| | | if (_go == null) |
| | | { |
| | | _go = Instantiate(m_Prefab, Constants.Special_Hide_Position, Quaternion.identity); |
| | | m_ActiveList.Add(_go); |
| | | m_ActiveHashSet.Add(_go); |
| | | |
| | | #if UNITY_EDITOR && !UNITY_ANDROID && !UNITY_IOS |
| | | DebugItem _debugItem = new GameObject(_go.GetInstanceID().ToString()).AddMissingComponent<DebugItem>(); |
| | | _debugItem.transform.SetParent(m_DebugActive); |
| | | _debugItem.target = _go; |
| | | Instance.m_DebugInstIDDict[_go.GetInstanceID()] = _debugItem; |
| | | #endif |
| | | } |
| | | |
| | | _go.transform.SetParent(null); |
| | | _go.transform.localScale = Vector3.one; |
| | | |
| | | // 更新池统计数据 |
| | | Instance.UpdatePoolStats(); |
| | | |
| | | return _go; |
| | | } |
| | | |
| | | |
| | | public void Release(GameObject go) |
| | | { |
| | |
| | | { |
| | | return; |
| | | } |
| | | if (m_ActiveList.Contains(go)) |
| | | { |
| | | m_ActiveList.Remove(go); |
| | | } |
| | | if (m_FreeList.Contains(go)) |
| | | { |
| | | return; |
| | | } |
| | | m_ActiveHashSet.Remove(go); |
| | | |
| | | m_FreeList.Add(go); |
| | | m_FreeQueue.Enqueue(go); |
| | | go.transform.SetParent(Instance.m_TargetContainer); |
| | | |
| | | go.transform.position = Constants.Special_Hide_Position; |
| | | |
| | | #if UNITY_EDITOR && !UNITY_ANDROID && !UNITY_IOS |
| | | if (Instance.m_DebugInstIDDict.ContainsKey(go.GetInstanceID())) |
| | | |
| | | #if UNITY_EDITOR |
| | | // 检查空闲列表数量是否超过阈值 |
| | | if (m_FreeQueue.Count > Pool_FreeList_Warning_Threshold) |
| | | { |
| | | DebugItem _debugItem = Instance.m_DebugInstIDDict[go.GetInstanceID()]; |
| | | _debugItem.transform.SetParent(m_DebugFree); |
| | | Debug.LogWarning($"GameObjectPool {m_Prefab.name} 的空闲对象数量 ({m_FreeQueue.Count}) 超过阈值 ({Pool_FreeList_Warning_Threshold}),请检查是否需要清理。"); |
| | | } |
| | | #endif |
| | | |
| | |
| | | { |
| | | releaseCallBack(go); |
| | | } |
| | | |
| | | // 更新池统计数据 |
| | | Instance.UpdatePoolStats(); |
| | | } |
| | | |
| | | public void ReleaseAll() |
| | | { |
| | | for (int i = m_ActiveList.Count - 1; i >= 0; i--) |
| | | //释放 m_ActiveHashSet中 的GameObject,排除 |
| | | foreach (GameObject go in m_ActiveHashSet) |
| | | { |
| | | var _go = m_ActiveList[i]; |
| | | m_ActiveList.Remove(_go); |
| | | |
| | | if (!m_FreeList.Contains(_go)) |
| | | if (go != null) |
| | | { |
| | | m_FreeList.Add(_go); |
| | | } |
| | | m_FreeQueue.Enqueue(go); |
| | | go.transform.SetParent(Instance.m_TargetContainer); |
| | | go.transform.position = Constants.Special_Hide_Position; |
| | | |
| | | #if UNITY_EDITOR && !UNITY_ANDROID && !UNITY_IOS |
| | | DebugItem _debugItem = Instance.m_DebugInstIDDict[_go.GetInstanceID()]; |
| | | _debugItem.transform.SetParent(m_DebugFree); |
| | | #endif |
| | | _go.transform.SetParent(Instance.m_TargetContainer); |
| | | _go.transform.position = Constants.Special_Hide_Position; |
| | | |
| | | if (releaseCallBack != null) |
| | | { |
| | | releaseCallBack(_go); |
| | | if (releaseCallBack != null) |
| | | { |
| | | releaseCallBack(go); |
| | | } |
| | | } |
| | | |
| | | } |
| | | m_ActiveHashSet.Clear(); |
| | | |
| | | // 更新池统计数据 |
| | | Instance.UpdatePoolStats(); |
| | | } |
| | | |
| | | public void Clear() |
| | | { |
| | | for (int i = m_ActiveList.Count - 1; i >= 0; --i) |
| | | foreach (GameObject go in m_ActiveHashSet) |
| | | { |
| | | if (m_ActiveList[i] == null |
| | | || Instance.dontDestoryGoInstIDList.Contains(m_ActiveList[i].GetInstanceID())) |
| | | continue; |
| | | #if UNITY_EDITOR && !UNITY_ANDROID && !UNITY_IOS |
| | | Destroy(Instance.m_DebugInstIDDict[m_ActiveList[i].GetInstanceID()].gameObject); |
| | | #endif |
| | | Destroy(m_ActiveList[i]); |
| | | m_ActiveList.RemoveAt(i); |
| | | } |
| | | |
| | | #if UNITY_EDITOR && !UNITY_ANDROID && !UNITY_IOS |
| | | if (m_ActiveList.Count != 0) |
| | | { |
| | | Debug.LogWarningFormat("{0} 池清理后, 还有 {1} 个活跃对象. ", m_Prefab.name, m_ActiveList.Count); |
| | | for (int i = 0; i < m_ActiveList.Count; ++i) |
| | | if (go != null) |
| | | { |
| | | Debug.LogWarningFormat(" |-- {0}", m_ActiveList[i].name); |
| | | if (!Instance.dontDestoryGoInstIDList.Contains(go.GetInstanceID())) |
| | | { |
| | | m_ActiveHashSet.Remove(go); |
| | | Destroy(go); |
| | | } |
| | | |
| | | } |
| | | |
| | | } |
| | | |
| | | #if UNITY_EDITOR |
| | | if (m_ActiveHashSet.Count != 0) |
| | | { |
| | | Debug.LogWarningFormat("{0} 池清理后, 还有 {1} 个活跃对象. ", m_Prefab.name, m_ActiveHashSet.Count); |
| | | } |
| | | #endif |
| | | |
| | | for (int i = m_FreeList.Count - 1; i >= 0; --i) |
| | | Queue<GameObject> tempQueue = new Queue<GameObject>(); |
| | | while (m_FreeQueue.Count > 0) |
| | | { |
| | | if (m_FreeList[i] == null |
| | | || Instance.dontDestoryGoInstIDList.Contains(m_FreeList[i].GetInstanceID())) |
| | | continue; |
| | | #if UNITY_EDITOR && !UNITY_ANDROID && !UNITY_IOS |
| | | Destroy(Instance.m_DebugInstIDDict[m_FreeList[i].GetInstanceID()].gameObject); |
| | | #endif |
| | | Destroy(m_FreeList[i]); |
| | | m_FreeList.RemoveAt(i); |
| | | GameObject obj = m_FreeQueue.Dequeue(); |
| | | if (Instance.dontDestoryGoInstIDList.Contains(obj.GetInstanceID())) |
| | | tempQueue.Enqueue(obj); |
| | | else |
| | | Destroy(obj); |
| | | } |
| | | m_FreeQueue = tempQueue; |
| | | |
| | | #if UNITY_EDITOR && !UNITY_ANDROID && !UNITY_IOS |
| | | if (m_FreeList.Count != 0) |
| | | |
| | | #if UNITY_EDITOR |
| | | if (m_FreeQueue.Count != 0) |
| | | { |
| | | Debug.LogWarningFormat("{0} 池清理后, 还有 {1} 个空闲对象. ", m_Prefab.name, m_FreeList.Count); |
| | | for (int i = 0; i < m_FreeList.Count; ++i) |
| | | { |
| | | Debug.LogWarningFormat(" |-- {0}", m_FreeList[i].name); |
| | | } |
| | | Debug.LogWarningFormat("{0} 池清理后, 还有 {1} 个空闲对象. ", m_Prefab.name, m_FreeQueue.Count); |
| | | // for (int i = 0; i < m_FreeList.Count; ++i) |
| | | // { |
| | | // Debug.LogWarningFormat(" |-- {0}", m_FreeList[i].name); |
| | | // } |
| | | } |
| | | #endif |
| | | if (IsEmpty()) |
| | | { |
| | | m_Prefab = null; |
| | | #if UNITY_EDITOR && !UNITY_ANDROID && !UNITY_IOS |
| | | Destroy(m_DebugContainer.gameObject); |
| | | #endif |
| | | |
| | | } |
| | | |
| | | if (!string.IsNullOrEmpty(assetBundleName) && !string.IsNullOrEmpty(assetName)) |
| | | { |
| | | ResManager.Instance.UnloadAsset(assetBundleName); |
| | | ResManager.Instance.UnloadAsset(assetBundleName, assetName); |
| | | // ResManager.Instance.UnloadAssetBundle(assetBundleName, true, false); |
| | | } |
| | | } |
| | | |
| | | public bool IsEmpty() |
| | | { |
| | | return m_ActiveList.Count == 0 && m_FreeList.Count == 0; |
| | | return m_ActiveHashSet.Count == 0 && m_FreeQueue.Count == 0; |
| | | } |
| | | } |
| | | } |