gameobject pool fix

This commit is contained in:
Pedro Gomes 2025-09-26 22:24:10 +01:00
parent 4c85272900
commit bf1725ee71

View File

@ -1,6 +1,7 @@
using System.Collections.Generic; using System.Collections.Generic;
using UnityEngine; using UnityEngine;
using UnityEngine.Pool; using UnityEngine.Pool;
using UnityEngine.SceneManagement;
/// <summary> /// <summary>
/// Centralized manager for GameObject pooling using Unity's built-in ObjectPool system. /// Centralized manager for GameObject pooling using Unity's built-in ObjectPool system.
@ -58,6 +59,10 @@ public class GameObjectPoolManager : MonoBehaviour
{ {
_instance = this; _instance = this;
DontDestroyOnLoad(gameObject); DontDestroyOnLoad(gameObject);
// Subscribe to scene loaded events
SceneManager.sceneLoaded += OnSceneLoaded;
InitializePools(); InitializePools();
} }
else if (_instance != this) else if (_instance != this)
@ -65,6 +70,15 @@ public class GameObjectPoolManager : MonoBehaviour
Destroy(gameObject); Destroy(gameObject);
} }
} }
private void OnDestroy()
{
// Unsubscribe from events
if (_instance == this)
{
SceneManager.sceneLoaded -= OnSceneLoaded;
}
}
#endregion #endregion
[Header("Pool Configuration")] [Header("Pool Configuration")]
@ -86,8 +100,13 @@ public class GameObjectPoolManager : MonoBehaviour
Debug.Log("All pool configs reset to default values"); 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")] [Header("Debug Info")]
[SerializeField] private bool showDebugLogs = true; // Temporarily enabled for debugging [SerializeField] private bool showDebugLogs = true;
[SerializeField] private bool showPoolStats = false; [SerializeField] private bool showPoolStats = false;
// Runtime pool storage - maps prefab to its ObjectPool // 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 // Track which pool each active object belongs to for release
private Dictionary<GameObject, GameObject> activeObjectToPrefab = new Dictionary<GameObject, GameObject>(); private Dictionary<GameObject, GameObject> activeObjectToPrefab = new Dictionary<GameObject, GameObject>();
#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();
}
}
}
/// <summary>
/// Clears all pools and active object tracking. Called automatically on scene load.
/// </summary>
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 #region Public API
/// <summary> /// <summary>
@ -122,6 +180,17 @@ public class GameObjectPoolManager : MonoBehaviour
// Get object from pool // Get object from pool
GameObject obj = pools[prefab].Get(); 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 // Set position and rotation
obj.transform.position = position; obj.transform.position = position;
obj.transform.rotation = rotation; obj.transform.rotation = rotation;
@ -155,6 +224,15 @@ public class GameObjectPoolManager : MonoBehaviour
return; 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 // Remove from tracking
activeObjectToPrefab.Remove(obj); activeObjectToPrefab.Remove(obj);
@ -293,6 +371,9 @@ public class GameObjectPoolManager : MonoBehaviour
GameObject obj = Instantiate(prefab); GameObject obj = Instantiate(prefab);
obj.name = prefab.name + "(Pooled)"; obj.name = prefab.name + "(Pooled)";
// Move to DontDestroyOnLoad so it persists across scenes
DontDestroyOnLoad(obj);
// Add PooledObject component to track pool membership // Add PooledObject component to track pool membership
var pooledComponent = obj.GetComponent<PooledObject>(); var pooledComponent = obj.GetComponent<PooledObject>();
if (pooledComponent == null) if (pooledComponent == null)
@ -309,6 +390,13 @@ public class GameObjectPoolManager : MonoBehaviour
private void OnGetFromPool(GameObject obj) 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 // Reset any IPoolable components FIRST
var poolables = obj.GetComponents<IPoolable>(); var poolables = obj.GetComponents<IPoolable>();
for (int i = 0; i < poolables.Length; i++) for (int i = 0; i < poolables.Length; i++)
@ -322,6 +410,8 @@ public class GameObjectPoolManager : MonoBehaviour
private void OnReleaseToPool(GameObject obj) private void OnReleaseToPool(GameObject obj)
{ {
if (obj == null) return;
// Reset any IPoolable components // Reset any IPoolable components
var poolables = obj.GetComponents<IPoolable>(); var poolables = obj.GetComponents<IPoolable>();
for (int i = 0; i < poolables.Length; i++) for (int i = 0; i < poolables.Length; i++)
@ -348,13 +438,20 @@ public class GameObjectPoolManager : MonoBehaviour
{ {
if (!showPoolStats) return; 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("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) foreach (var kvp in pools)
{ {
string prefabName = kvp.Key.name; string prefabName = kvp.Key != null ? kvp.Key.name : "NULL";
int activeCount = activeObjectToPrefab.Values.Count; int activeCount = 0;
foreach (var activeKvp in activeObjectToPrefab)
{
if (activeKvp.Value == kvp.Key) activeCount++;
}
GUILayout.Label($"{prefabName}: {activeCount} active"); GUILayout.Label($"{prefabName}: {activeCount} active");
} }
@ -387,4 +484,4 @@ public class PooledObject : MonoBehaviour
{ {
sourcePrefab = prefab; sourcePrefab = prefab;
} }
} }