Files
AppleHillsProduction/Assets/Editor/Utilities/PoolMonitorWindow.cs

433 lines
18 KiB
C#

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<PoolMonitorWindow>("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<T>
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<T>
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<GameObject>(FindObjectsSortMode.None).Length}");
EditorGUILayout.Space();
if (showSinglePrefabPools)
{
DisplaySinglePrefabPoolInfo();
}
if (showMultiPrefabPools)
{
DisplayMultiPrefabPoolInfo();
}
}
void DisplaySinglePrefabPoolInfo()
{
// Find all types that derive from BaseObjectPool<T>
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<T>
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<T>
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<T>
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();
}
}
/// <summary>
/// Finds all objects that derive from a generic base type
/// </summary>
private Component[] FindObjectsOfBaseType(Type baseComponentType, Type genericBaseType)
{
List<Component> results = new List<Component>();
// Find all components in the scene
Component[] allComponents = UnityEngine.Object.FindObjectsByType<Component>(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();
}
}
}