433 lines
18 KiB
C#
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();
|
|
}
|
|
}
|
|
}
|