using UnityEngine; using UnityEditor; using System.Collections.Generic; using System; using System.Reflection; using System.Collections; using Pooling; namespace Editor.Utilities { public class PoolMonitorWindow : EditorWindow { private Vector2 scrollPosition; private bool autoRefresh = true; private float refreshInterval = 1.0f; private float lastRefreshTime; private bool showSinglePrefabPools = true; private bool showMultiPrefabPools = true; [MenuItem("Tools/Pool Monitor")] public static void ShowWindow() { GetWindow("Pool Monitor"); } void OnGUI() { EditorGUILayout.BeginVertical(); EditorGUILayout.LabelField("Object Pool Monitor", EditorStyles.boldLabel); EditorGUILayout.BeginHorizontal(); autoRefresh = EditorGUILayout.Toggle("Auto Refresh", autoRefresh); if (autoRefresh) { refreshInterval = EditorGUILayout.Slider("Refresh Interval", refreshInterval, 0.1f, 5f); } if (GUILayout.Button("Refresh Now")) { RefreshPoolInfo(); } EditorGUILayout.EndHorizontal(); EditorGUILayout.Space(); // Display toggles for showing different pool types EditorGUILayout.BeginHorizontal(); showSinglePrefabPools = EditorGUILayout.ToggleLeft("Show Single Prefab Pools", showSinglePrefabPools, GUILayout.Width(200)); showMultiPrefabPools = EditorGUILayout.ToggleLeft("Show Multi-Prefab Pools", showMultiPrefabPools, GUILayout.Width(200)); EditorGUILayout.EndHorizontal(); EditorGUILayout.Space(); scrollPosition = EditorGUILayout.BeginScrollView(scrollPosition); if (Application.isPlaying) { DisplayPoolInfo(); } else { EditorGUILayout.HelpBox("Enter play mode to see pool statistics.", MessageType.Info); } EditorGUILayout.EndScrollView(); EditorGUILayout.EndVertical(); } void Update() { if (autoRefresh && Application.isPlaying) { float currentTime = (float)EditorApplication.timeSinceStartup; if (currentTime - lastRefreshTime > refreshInterval) { lastRefreshTime = currentTime; RefreshPoolInfo(); Repaint(); } } } void RefreshPoolInfo() { if (!Application.isPlaying) return; // Find all pool types and call LogPoolStats if (showSinglePrefabPools) { // Find all types that derive from BaseObjectPool foreach (var pool in FindObjectsOfBaseType(typeof(Component), typeof(BaseObjectPool<>))) { if (pool != null && pool.gameObject.activeInHierarchy) { var logMethod = pool.GetType().GetMethod("LogPoolStats"); if (logMethod != null) { logMethod.Invoke(pool, null); } } } } if (showMultiPrefabPools) { // Find all types that derive from MultiPrefabPool foreach (var pool in FindObjectsOfBaseType(typeof(Component), typeof(MultiPrefabPool<>))) { if (pool != null && pool.gameObject.activeInHierarchy) { var logMethod = pool.GetType().GetMethod("LogPoolStats"); if (logMethod != null) { logMethod.Invoke(pool, null); } } } } } void DisplayPoolInfo() { EditorGUILayout.LabelField("Scene Statistics:", EditorStyles.boldLabel); EditorGUILayout.LabelField($"Total GameObjects: {UnityEngine.Object.FindObjectsByType(FindObjectsSortMode.None).Length}"); EditorGUILayout.Space(); if (showSinglePrefabPools) { DisplaySinglePrefabPoolInfo(); } if (showMultiPrefabPools) { DisplayMultiPrefabPoolInfo(); } } void DisplaySinglePrefabPoolInfo() { // Find all types that derive from BaseObjectPool Component[] pools = FindObjectsOfBaseType(typeof(Component), typeof(BaseObjectPool<>)); if (pools.Length == 0) { EditorGUILayout.HelpBox("No single prefab pools found in the scene.", MessageType.Info); return; } EditorGUILayout.LabelField("Single Prefab Pools", EditorStyles.boldLabel); foreach (var poolComponent in pools) { EditorGUILayout.LabelField($"Pool: {poolComponent.name} ({poolComponent.GetType().Name})", EditorStyles.boldLabel); EditorGUI.indentLevel++; // Get private field values using reflection Type poolType = poolComponent.GetType(); FieldInfo pooledObjectsField = poolType.GetField("pooledObjects", BindingFlags.NonPublic | BindingFlags.Instance); FieldInfo totalCreatedField = poolType.GetField("totalCreated", BindingFlags.NonPublic | BindingFlags.Instance); FieldInfo totalReturnedField = poolType.GetField("totalReturned", BindingFlags.NonPublic | BindingFlags.Instance); PropertyInfo maxPoolSizeProp = poolType.GetProperty("maxPoolSize") ?? poolType.GetField("maxPoolSize")?.DeclaringType.GetProperty("maxPoolSize"); PropertyInfo initialPoolSizeProp = poolType.GetProperty("initialPoolSize") ?? poolType.GetField("initialPoolSize")?.DeclaringType.GetProperty("initialPoolSize"); if (pooledObjectsField != null) { object pooledObjects = pooledObjectsField.GetValue(poolComponent); int count = 0; // Handle Stack if (pooledObjects is System.Collections.ICollection collection) { count = collection.Count; } int maxSize = 0; if (maxPoolSizeProp != null) { maxSize = (int)maxPoolSizeProp.GetValue(poolComponent); } else { FieldInfo maxPoolSizeField = poolType.GetField("maxPoolSize", BindingFlags.Public | BindingFlags.Instance); if (maxPoolSizeField != null) { maxSize = (int)maxPoolSizeField.GetValue(poolComponent); } } EditorGUILayout.LabelField($"Pooled Objects: {count}/{maxSize}"); int initialSize = 0; if (initialPoolSizeProp != null) { initialSize = (int)initialPoolSizeProp.GetValue(poolComponent); } else { FieldInfo initialPoolSizeField = poolType.GetField("initialPoolSize", BindingFlags.Public | BindingFlags.Instance); if (initialPoolSizeField != null) { initialSize = (int)initialPoolSizeField.GetValue(poolComponent); } } EditorGUILayout.LabelField($"Initial Pool Size: {initialSize}"); if (totalCreatedField != null && totalReturnedField != null) { int created = (int)totalCreatedField.GetValue(poolComponent); int returned = (int)totalReturnedField.GetValue(poolComponent); EditorGUILayout.LabelField($"Created: {created}, Returned: {returned}"); } } // Try to find active objects of the pool's type if (poolType.BaseType.IsGenericType) { Type elementType = poolType.BaseType.GetGenericArguments()[0]; // More accurately count only active objects in the current scene int activeCount = 0; // First, try to get a more accurate count from the current scene foreach (var obj in UnityEngine.Object.FindObjectsByType(elementType, FindObjectsSortMode.None)) { var comp = obj as Component; if (comp != null && comp.gameObject.activeInHierarchy) { activeCount++; } } EditorGUILayout.LabelField($"Active Objects (Current Scene): {activeCount}"); // Add a note about pooling status if (activeCount > 0) { int pooledCount = 0; if (pooledObjectsField != null) { object pooledObjects = pooledObjectsField.GetValue(poolComponent); if (pooledObjects is ICollection collection) { pooledCount = collection.Count; } } EditorGUILayout.LabelField($"Pooling Efficiency: {pooledCount} ready in pool, {activeCount} active"); } } EditorGUI.indentLevel--; EditorGUILayout.Space(); } } void DisplayMultiPrefabPoolInfo() { // Find all types that derive from MultiPrefabPool Component[] pools = FindObjectsOfBaseType(typeof(Component), typeof(MultiPrefabPool<>)); if (pools.Length == 0) { EditorGUILayout.HelpBox("No multi-prefab pools found in the scene.", MessageType.Info); return; } EditorGUILayout.LabelField("Multi-Prefab Pools", EditorStyles.boldLabel); foreach (var poolComponent in pools) { EditorGUILayout.LabelField($"Pool: {poolComponent.name} ({poolComponent.GetType().Name})", EditorStyles.boldLabel); EditorGUI.indentLevel++; // Get private field values using reflection Type poolType = poolComponent.GetType(); FieldInfo totalPooledCountField = poolType.GetField("totalPooledCount", BindingFlags.NonPublic | BindingFlags.Instance); FieldInfo pooledObjectsField = poolType.GetField("pooledObjects", BindingFlags.NonPublic | BindingFlags.Instance); FieldInfo prefabUsageField = poolType.GetField("prefabUsageCount", BindingFlags.NonPublic | BindingFlags.Instance); PropertyInfo totalMaxPoolSizeProp = poolType.GetProperty("totalMaxPoolSize") ?? poolType.GetField("totalMaxPoolSize")?.DeclaringType.GetProperty("totalMaxPoolSize"); if (totalPooledCountField != null && totalMaxPoolSizeProp != null) { int totalCount = (int)totalPooledCountField.GetValue(poolComponent); int maxSize = 0; if (totalMaxPoolSizeProp != null) { maxSize = (int)totalMaxPoolSizeProp.GetValue(poolComponent); } else { FieldInfo totalMaxPoolSizeField = poolType.GetField("totalMaxPoolSize", BindingFlags.Public | BindingFlags.Instance); if (totalMaxPoolSizeField != null) { maxSize = (int)totalMaxPoolSizeField.GetValue(poolComponent); } } EditorGUILayout.LabelField($"Total Pooled Objects: {totalCount}/{maxSize}"); } if (pooledObjectsField != null && prefabUsageField != null) { // This is more complex because we don't know the exact generic types // Just show basic information object pooledTiles = pooledObjectsField.GetValue(poolComponent); object usageCounts = prefabUsageField.GetValue(poolComponent); if (pooledTiles != null && pooledTiles is IDictionary poolDict) { EditorGUILayout.LabelField("Prefab Details:", EditorStyles.boldLabel); EditorGUI.indentLevel++; foreach (DictionaryEntry entry in poolDict) { int prefabIndex = Convert.ToInt32(entry.Key); object value = entry.Value; int count = 0; // Handle Stack if (value is ICollection collection) { count = collection.Count; } int usageCount = 0; if (usageCounts is IDictionary usageDict) { if (usageDict.Contains(entry.Key)) { usageCount = Convert.ToInt32(usageDict[entry.Key]); } } EditorGUILayout.LabelField($"Prefab {prefabIndex}: {count} pooled, {usageCount} usages"); } EditorGUI.indentLevel--; } } // Try to find active objects of the pool's type if (poolType.BaseType.IsGenericType) { Type elementType = poolType.BaseType.GetGenericArguments()[0]; int activeCount = 0; // Count active objects of the specific pool's component type foreach (var obj in UnityEngine.Object.FindObjectsByType(elementType, FindObjectsSortMode.None)) { var comp = obj as Component; if (comp != null && comp.gameObject.activeInHierarchy) { activeCount++; } } EditorGUILayout.LabelField($"Active Objects (Current Scene): {activeCount}"); // Add a note about pooling status if (activeCount > 0 && totalPooledCountField != null) { int pooledCount = (int)totalPooledCountField.GetValue(poolComponent); EditorGUILayout.LabelField($"Pooling Efficiency: {pooledCount} ready in pool, {activeCount} active"); } } EditorGUI.indentLevel--; EditorGUILayout.Space(); } } /// /// Finds all objects that derive from a generic base type /// private Component[] FindObjectsOfBaseType(Type baseComponentType, Type genericBaseType) { List results = new List(); // Find all components in the scene Component[] allComponents = UnityEngine.Object.FindObjectsByType(FindObjectsSortMode.None); foreach (var component in allComponents) { Type componentType = component.GetType(); // Check if this type derives from the generic base type while (componentType != null && componentType != typeof(object)) { if (componentType.IsGenericType && componentType.GetGenericTypeDefinition() == genericBaseType) { results.Add(component); break; } // Also check for non-generic derived types if (componentType.BaseType != null && componentType.BaseType.IsGenericType && componentType.BaseType.GetGenericTypeDefinition() == genericBaseType) { results.Add(component); break; } componentType = componentType.BaseType; } } return results.ToArray(); } } }