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 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() 
 | 
    { 
 | 
        if (m_PoolDict == null) 
 | 
        { 
 | 
            m_PoolDict = new Dictionary<int, GameObjectPool>(); 
 | 
        } 
 | 
  
 | 
        if (!m_TargetContainer) 
 | 
        { 
 | 
            m_TargetContainer = new GameObject("Container").transform; 
 | 
            m_TargetContainer.SetParent(transform); 
 | 
        } 
 | 
  
 | 
        if (dontDestoryGoInstIDList == null) 
 | 
        { 
 | 
            dontDestoryGoInstIDList = new List<int>(); 
 | 
        } 
 | 
  
 | 
        // 启动定期检查池的协程 
 | 
        CheckPoolUsage(); 
 | 
    } 
 | 
  
 | 
    private async UniTask CheckPoolUsage() 
 | 
    { 
 | 
        while (true) 
 | 
        { 
 | 
            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; 
 | 
            } 
 | 
        } 
 | 
    } 
 | 
  
 | 
    public void AddDontDestroyGoInstID(int id) 
 | 
    { 
 | 
        if (!dontDestoryGoInstIDList.Contains(id)) 
 | 
        { 
 | 
            dontDestoryGoInstIDList.Add(id); 
 | 
        } 
 | 
    } 
 | 
  
 | 
    public bool ExistPool(GameObject prefab) 
 | 
    { 
 | 
        if (prefab == null) 
 | 
        { 
 | 
            return false; 
 | 
        } 
 | 
  
 | 
        if (m_PoolDict.ContainsKey(prefab.GetInstanceID())) 
 | 
        { 
 | 
            return true; 
 | 
        } 
 | 
  
 | 
        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(); 
 | 
  
 | 
        GameObjectPool _pool = null; 
 | 
        if (m_PoolDict.TryGetValue(_prefabInstanceId, out _pool)) 
 | 
        { 
 | 
            return _pool; 
 | 
        } 
 | 
  
 | 
        _pool = new GameObjectPool(prefab); 
 | 
        m_PoolDict[_prefabInstanceId] = _pool; 
 | 
  
 | 
        return _pool; 
 | 
    } 
 | 
  
 | 
  
 | 
    public void CacheGameObject(GameObject prefab, int count, bool _prefabActive) 
 | 
    { 
 | 
        if (prefab == null) 
 | 
        { 
 | 
            return; 
 | 
        } 
 | 
  
 | 
        RequestPool(prefab).Cache(count, _prefabActive); 
 | 
    } 
 | 
  
 | 
  
 | 
    public GameObject RequestGameObject(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 
 | 
        return RequestPool(prefab).Request(); 
 | 
    } 
 | 
  
 | 
  
 | 
    //特定情况下需要动态卸载的,如切换场景时 
 | 
    public void UnLoadAll(bool force = false) 
 | 
    { 
 | 
        List<int> _removeList = new List<int>(); 
 | 
        foreach (var _pool in m_PoolDict.Values) 
 | 
        { 
 | 
            if (needDestroyPrefabList.Contains(_pool.Prefab)) 
 | 
            { 
 | 
                _pool.Clear(); 
 | 
                _removeList.Add(_pool.PrefabInstanceId); 
 | 
            } 
 | 
        } 
 | 
  
 | 
        for (int i = 0; i < _removeList.Count; ++i) 
 | 
        { 
 | 
            m_PoolDict.Remove(_removeList[i]); 
 | 
        } 
 | 
    } 
 | 
  
 | 
    public class GameObjectPool 
 | 
    { 
 | 
        private GameObject m_Prefab = null; 
 | 
        public GameObject Prefab 
 | 
        { 
 | 
            get 
 | 
            { 
 | 
                return m_Prefab; 
 | 
            } 
 | 
        } 
 | 
  
 | 
        //预制体实例id,用于检索池 
 | 
        public int PrefabInstanceId 
 | 
        { 
 | 
            get; private set; 
 | 
        } 
 | 
  
 | 
        public HashSet<GameObject> m_ActiveHashSet = new HashSet<GameObject>(); 
 | 
        public Queue<GameObject> m_FreeQueue = null; 
 | 
        private int Pool_FreeList_Warning_Threshold = 10;    //当空闲对象高于此值时,会进行日志输出提醒 
 | 
  
 | 
        Action<GameObject> releaseCallBack; 
 | 
  
 | 
  
 | 
        public string assetBundleName; 
 | 
        public string assetName; 
 | 
  
 | 
        internal protected GameObjectPool(GameObject prefab) 
 | 
        { 
 | 
            m_Prefab = prefab; 
 | 
            PrefabInstanceId = m_Prefab.GetInstanceID(); 
 | 
            m_ActiveHashSet = new HashSet<GameObject>(); 
 | 
            m_FreeQueue = new Queue<GameObject>(); 
 | 
  
 | 
        } 
 | 
  
 | 
        public void AddReleaseListener(Action<GameObject> _callBack) 
 | 
        { 
 | 
            releaseCallBack = _callBack; 
 | 
        } 
 | 
  
 | 
        public void RemoveReleaseListener() 
 | 
        { 
 | 
            releaseCallBack = null; 
 | 
        } 
 | 
  
 | 
        public void Cache(int count, bool _prefabActive) 
 | 
        { 
 | 
            GameObject _go; 
 | 
            for (int i = 0; i < count; ++i) 
 | 
            { 
 | 
                _go = Instantiate(m_Prefab, Constants.Special_Hide_Position, Quaternion.identity); 
 | 
                m_FreeQueue.Enqueue(_go); 
 | 
                _go.transform.SetParent(Instance.m_TargetContainer); 
 | 
                _go.transform.position = Constants.Special_Hide_Position; 
 | 
  
 | 
  
 | 
#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) 
 | 
                { 
 | 
                    animator.enabled = false; 
 | 
                } 
 | 
                _go.SetActive(_prefabActive); 
 | 
            } 
 | 
        } 
 | 
  
 | 
        public GameObject Request() 
 | 
        { 
 | 
            GameObject _go = null; 
 | 
  
 | 
            if (m_FreeQueue.Count > 0) 
 | 
            { 
 | 
                _go = m_FreeQueue.Dequeue(); 
 | 
  
 | 
                m_ActiveHashSet.Add(_go); 
 | 
  
 | 
                // 统计池的使用频率 
 | 
                if (Instance.m_PoolUsageFrequency.ContainsKey(m_Prefab.GetInstanceID())) 
 | 
                { 
 | 
                    Instance.m_PoolUsageFrequency[m_Prefab.GetInstanceID()]++; 
 | 
                } 
 | 
                else 
 | 
                { 
 | 
                    Instance.m_PoolUsageFrequency[m_Prefab.GetInstanceID()] = 1; 
 | 
                } 
 | 
  
 | 
            } 
 | 
  
 | 
  
 | 
            if (_go == null) 
 | 
            { 
 | 
                _go = Instantiate(m_Prefab, Constants.Special_Hide_Position, Quaternion.identity); 
 | 
                m_ActiveHashSet.Add(_go); 
 | 
  
 | 
            } 
 | 
  
 | 
            _go.transform.SetParent(null); 
 | 
            _go.transform.localScale = Vector3.one; 
 | 
  
 | 
            // 更新池统计数据 
 | 
            Instance.UpdatePoolStats(); 
 | 
  
 | 
            return _go; 
 | 
        } 
 | 
  
 | 
  
 | 
        public void Release(GameObject go) 
 | 
        { 
 | 
            if (go == null) 
 | 
            { 
 | 
                return; 
 | 
            } 
 | 
            m_ActiveHashSet.Remove(go); 
 | 
  
 | 
            m_FreeQueue.Enqueue(go); 
 | 
            go.transform.SetParent(Instance.m_TargetContainer); 
 | 
  
 | 
            go.transform.position = Constants.Special_Hide_Position; 
 | 
  
 | 
  
 | 
#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 
 | 
  
 | 
            if (releaseCallBack != null) 
 | 
            { 
 | 
                releaseCallBack(go); 
 | 
            } 
 | 
  
 | 
            // 更新池统计数据 
 | 
            Instance.UpdatePoolStats(); 
 | 
        } 
 | 
  
 | 
        public void ReleaseAll() 
 | 
        { 
 | 
            //释放 m_ActiveHashSet中 的GameObject,排除 
 | 
            foreach (GameObject go in m_ActiveHashSet) 
 | 
            { 
 | 
                if (go != null) 
 | 
                { 
 | 
                    m_FreeQueue.Enqueue(go); 
 | 
                    go.transform.SetParent(Instance.m_TargetContainer); 
 | 
                    go.transform.position = Constants.Special_Hide_Position; 
 | 
  
 | 
                    if (releaseCallBack != null) 
 | 
                    { 
 | 
                        releaseCallBack(go); 
 | 
                    } 
 | 
                } 
 | 
                 
 | 
            } 
 | 
            m_ActiveHashSet.Clear(); 
 | 
  
 | 
            // 更新池统计数据 
 | 
            Instance.UpdatePoolStats(); 
 | 
        } 
 | 
  
 | 
        public void Clear() 
 | 
        { 
 | 
            foreach (GameObject go in m_ActiveHashSet) 
 | 
            { 
 | 
                if (go != null) 
 | 
                { 
 | 
                    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 
 | 
  
 | 
            Queue<GameObject> tempQueue = new Queue<GameObject>(); 
 | 
            while (m_FreeQueue.Count > 0) 
 | 
            { 
 | 
                GameObject obj = m_FreeQueue.Dequeue(); 
 | 
                if (Instance.dontDestoryGoInstIDList.Contains(obj.GetInstanceID())) 
 | 
                    tempQueue.Enqueue(obj); 
 | 
                else 
 | 
                    Destroy(obj); 
 | 
            } 
 | 
            m_FreeQueue = tempQueue; 
 | 
  
 | 
  
 | 
#if UNITY_EDITOR 
 | 
            if (m_FreeQueue.Count != 0) 
 | 
            { 
 | 
                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 (!string.IsNullOrEmpty(assetBundleName) && !string.IsNullOrEmpty(assetName)) 
 | 
            { 
 | 
                ResManager.Instance.UnloadAsset(assetBundleName, assetName); 
 | 
                // ResManager.Instance.UnloadAssetBundle(assetBundleName, true, false); 
 | 
            } 
 | 
        } 
 | 
  
 | 
        public bool IsEmpty() 
 | 
        { 
 | 
            return m_ActiveHashSet.Count == 0 && m_FreeQueue.Count == 0; 
 | 
        } 
 | 
    } 
 | 
} 
 |