From bf1725ee71b3f99df5401c55a0644c52ae251a05 Mon Sep 17 00:00:00 2001 From: Pedro Gomes Date: Fri, 26 Sep 2025 22:24:10 +0100 Subject: [PATCH] gameobject pool fix --- .../ObjectPooling/GameObjectPoolManager.cs | 107 +++++++++++++++++- 1 file changed, 102 insertions(+), 5 deletions(-) diff --git a/Assets/_ProjectRevive/Scripts/ObjectPooling/GameObjectPoolManager.cs b/Assets/_ProjectRevive/Scripts/ObjectPooling/GameObjectPoolManager.cs index b977be4c..4c0515cf 100644 --- a/Assets/_ProjectRevive/Scripts/ObjectPooling/GameObjectPoolManager.cs +++ b/Assets/_ProjectRevive/Scripts/ObjectPooling/GameObjectPoolManager.cs @@ -1,6 +1,7 @@ using System.Collections.Generic; using UnityEngine; using UnityEngine.Pool; +using UnityEngine.SceneManagement; /// /// Centralized manager for GameObject pooling using Unity's built-in ObjectPool system. @@ -58,6 +59,10 @@ public class GameObjectPoolManager : MonoBehaviour { _instance = this; DontDestroyOnLoad(gameObject); + + // Subscribe to scene loaded events + SceneManager.sceneLoaded += OnSceneLoaded; + InitializePools(); } else if (_instance != this) @@ -65,6 +70,15 @@ public class GameObjectPoolManager : MonoBehaviour Destroy(gameObject); } } + + private void OnDestroy() + { + // Unsubscribe from events + if (_instance == this) + { + SceneManager.sceneLoaded -= OnSceneLoaded; + } + } #endregion [Header("Pool Configuration")] @@ -86,8 +100,13 @@ public class GameObjectPoolManager : MonoBehaviour Debug.Log("All pool configs reset to default values"); } + [Header("Scene Management")] + [SerializeField] private bool clearPoolsOnSceneLoad = true; + [Tooltip("If true, pools will be recreated immediately on scene load. If false, they'll be recreated on-demand.")] + [SerializeField] private bool preCreatePoolsOnSceneLoad = false; + [Header("Debug Info")] - [SerializeField] private bool showDebugLogs = true; // Temporarily enabled for debugging + [SerializeField] private bool showDebugLogs = true; [SerializeField] private bool showPoolStats = false; // Runtime pool storage - maps prefab to its ObjectPool @@ -96,6 +115,45 @@ public class GameObjectPoolManager : MonoBehaviour // Track which pool each active object belongs to for release private Dictionary activeObjectToPrefab = new Dictionary(); + #region Scene Management + + private void OnSceneLoaded(Scene scene, LoadSceneMode mode) + { + if (clearPoolsOnSceneLoad) + { + if (showDebugLogs) + Debug.Log($"GameObjectPoolManager: Scene '{scene.name}' loaded, clearing pools"); + + ClearAllPools(); + + if (preCreatePoolsOnSceneLoad) + { + InitializePools(); + } + } + } + + /// + /// Clears all pools and active object tracking. Called automatically on scene load. + /// + public void ClearAllPools() + { + // Clear pools (this will dispose of all pooled objects) + foreach (var pool in pools.Values) + { + pool.Clear(); + } + pools.Clear(); + + // Clear active object tracking + activeObjectToPrefab.Clear(); + + if (showDebugLogs) + Debug.Log("GameObjectPoolManager: Cleared all pools"); + } + + #endregion + #region Public API /// @@ -122,6 +180,17 @@ public class GameObjectPoolManager : MonoBehaviour // Get object from pool GameObject obj = pools[prefab].Get(); + // Double-check the object is valid (in case pool had stale references) + if (obj == null) + { + Debug.LogWarning($"GameObjectPoolManager: Pool returned null object for {prefab.name}. Recreating pool."); + // Clear and recreate the pool + pools[prefab].Clear(); + pools.Remove(prefab); + CreatePoolForPrefab(prefab); + obj = pools[prefab].Get(); + } + // Set position and rotation obj.transform.position = position; obj.transform.rotation = rotation; @@ -155,6 +224,15 @@ public class GameObjectPoolManager : MonoBehaviour return; } + // Check if the pool still exists (might have been cleared on scene change) + if (!pools.ContainsKey(prefab)) + { + Debug.LogWarning($"GameObjectPoolManager: Pool for {prefab.name} no longer exists. Destroying object instead."); + activeObjectToPrefab.Remove(obj); + Destroy(obj); + return; + } + // Remove from tracking activeObjectToPrefab.Remove(obj); @@ -293,6 +371,9 @@ public class GameObjectPoolManager : MonoBehaviour GameObject obj = Instantiate(prefab); obj.name = prefab.name + "(Pooled)"; + // Move to DontDestroyOnLoad so it persists across scenes + DontDestroyOnLoad(obj); + // Add PooledObject component to track pool membership var pooledComponent = obj.GetComponent(); if (pooledComponent == null) @@ -309,6 +390,13 @@ public class GameObjectPoolManager : MonoBehaviour private void OnGetFromPool(GameObject obj) { + // Check if object is still valid (might be destroyed if scene changed) + if (obj == null) + { + Debug.LogError("GameObjectPoolManager: Pool returned a destroyed object!"); + return; + } + // Reset any IPoolable components FIRST var poolables = obj.GetComponents(); for (int i = 0; i < poolables.Length; i++) @@ -322,6 +410,8 @@ public class GameObjectPoolManager : MonoBehaviour private void OnReleaseToPool(GameObject obj) { + if (obj == null) return; + // Reset any IPoolable components var poolables = obj.GetComponents(); for (int i = 0; i < poolables.Length; i++) @@ -348,13 +438,20 @@ public class GameObjectPoolManager : MonoBehaviour { if (!showPoolStats) return; - GUILayout.BeginArea(new Rect(10, 10, 300, 200)); + GUILayout.BeginArea(new Rect(10, 10, 300, 300)); GUILayout.Label("Pool Stats", GUI.skin.box); + GUILayout.Label($"Active Pools: {pools.Count}"); + GUILayout.Label($"Active Objects: {activeObjectToPrefab.Count}"); + GUILayout.Space(10); foreach (var kvp in pools) { - string prefabName = kvp.Key.name; - int activeCount = activeObjectToPrefab.Values.Count; + string prefabName = kvp.Key != null ? kvp.Key.name : "NULL"; + int activeCount = 0; + foreach (var activeKvp in activeObjectToPrefab) + { + if (activeKvp.Value == kvp.Key) activeCount++; + } GUILayout.Label($"{prefabName}: {activeCount} active"); } @@ -387,4 +484,4 @@ public class PooledObject : MonoBehaviour { sourcePrefab = prefab; } -} +} \ No newline at end of file