Working generic object pooling, pool monitor editor tool and batch component adder editor tool
This commit is contained in:
@@ -1,7 +1,10 @@
|
||||
using UnityEngine;
|
||||
using UnityEditor;
|
||||
using System.Collections.Generic;
|
||||
using Minigames.DivingForPictures;
|
||||
using System;
|
||||
using System.Reflection;
|
||||
using System.Collections;
|
||||
using Pooling;
|
||||
|
||||
namespace Editor.Utilities
|
||||
{
|
||||
@@ -11,8 +14,8 @@ namespace Editor.Utilities
|
||||
private bool autoRefresh = true;
|
||||
private float refreshInterval = 1.0f;
|
||||
private float lastRefreshTime;
|
||||
private bool showTrenchTiles = true;
|
||||
private bool showBubbles = true;
|
||||
private bool showSinglePrefabPools = true;
|
||||
private bool showMultiPrefabPools = true;
|
||||
|
||||
[MenuItem("Tools/Pool Monitor")]
|
||||
public static void ShowWindow()
|
||||
@@ -42,8 +45,8 @@ namespace Editor.Utilities
|
||||
|
||||
// Display toggles for showing different pool types
|
||||
EditorGUILayout.BeginHorizontal();
|
||||
showTrenchTiles = EditorGUILayout.ToggleLeft("Show Trench Tile Pools", showTrenchTiles, GUILayout.Width(200));
|
||||
showBubbles = EditorGUILayout.ToggleLeft("Show Bubble Pools", showBubbles, GUILayout.Width(200));
|
||||
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();
|
||||
@@ -81,25 +84,31 @@ namespace Editor.Utilities
|
||||
{
|
||||
if (!Application.isPlaying) return;
|
||||
|
||||
// Call LogPoolStats on all pool instances to update their stats in the console
|
||||
if (showTrenchTiles)
|
||||
// Find all pool types and call LogPoolStats
|
||||
if (showSinglePrefabPools)
|
||||
{
|
||||
TrenchTilePool[] tilePools = Object.FindObjectsByType<TrenchTilePool>(FindObjectsSortMode.None);
|
||||
foreach (var pool in tilePools)
|
||||
{
|
||||
pool.LogPoolStats();
|
||||
}
|
||||
}
|
||||
|
||||
if (showBubbles)
|
||||
{
|
||||
BubblePool[] bubblePools = Object.FindObjectsByType<BubblePool>(FindObjectsSortMode.None);
|
||||
foreach (var pool in bubblePools)
|
||||
// Find all types that derive from BaseObjectPool<T>
|
||||
foreach (var pool in FindObjectsOfBaseType(typeof(Component), typeof(BaseObjectPool<>)))
|
||||
{
|
||||
if (pool != null && pool.gameObject.activeInHierarchy)
|
||||
{
|
||||
// If BubblePool has a LogPoolStats method, call it
|
||||
var logMethod = typeof(BubblePool).GetMethod("LogPoolStats");
|
||||
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);
|
||||
@@ -112,133 +121,312 @@ namespace Editor.Utilities
|
||||
void DisplayPoolInfo()
|
||||
{
|
||||
EditorGUILayout.LabelField("Scene Statistics:", EditorStyles.boldLabel);
|
||||
EditorGUILayout.LabelField($"Total GameObjects: {Object.FindObjectsByType<GameObject>(FindObjectsSortMode.None).Length}");
|
||||
EditorGUILayout.LabelField($"Total GameObjects: {UnityEngine.Object.FindObjectsByType<GameObject>(FindObjectsSortMode.None).Length}");
|
||||
|
||||
EditorGUILayout.Space();
|
||||
|
||||
if (showTrenchTiles)
|
||||
if (showSinglePrefabPools)
|
||||
{
|
||||
DisplayTrenchTilePoolInfo();
|
||||
DisplaySinglePrefabPoolInfo();
|
||||
}
|
||||
|
||||
if (showBubbles)
|
||||
if (showMultiPrefabPools)
|
||||
{
|
||||
DisplayBubblePoolInfo();
|
||||
DisplayMultiPrefabPoolInfo();
|
||||
}
|
||||
}
|
||||
|
||||
void DisplayTrenchTilePoolInfo()
|
||||
void DisplaySinglePrefabPoolInfo()
|
||||
{
|
||||
TrenchTilePool[] pools = Object.FindObjectsByType<TrenchTilePool>(FindObjectsSortMode.None);
|
||||
// Find all types that derive from BaseObjectPool<T>
|
||||
Component[] pools = FindObjectsOfBaseType(typeof(Component), typeof(BaseObjectPool<>));
|
||||
|
||||
if (pools.Length == 0)
|
||||
{
|
||||
EditorGUILayout.HelpBox("No trench tile pools found in the scene.", MessageType.Info);
|
||||
EditorGUILayout.HelpBox("No single prefab pools found in the scene.", MessageType.Info);
|
||||
return;
|
||||
}
|
||||
else
|
||||
|
||||
EditorGUILayout.LabelField("Single Prefab Pools", EditorStyles.boldLabel);
|
||||
foreach (var poolComponent in pools)
|
||||
{
|
||||
EditorGUILayout.LabelField("Trench Tile Pools", EditorStyles.boldLabel);
|
||||
foreach (var pool 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)
|
||||
{
|
||||
EditorGUILayout.LabelField($"Pool: {pool.name}", EditorStyles.boldLabel);
|
||||
EditorGUI.indentLevel++;
|
||||
object pooledObjects = pooledObjectsField.GetValue(poolComponent);
|
||||
int count = 0;
|
||||
|
||||
// Get private field values using reflection
|
||||
System.Reflection.FieldInfo totalCountField = typeof(TrenchTilePool).GetField("totalPooledCount",
|
||||
System.Reflection.BindingFlags.NonPublic | System.Reflection.BindingFlags.Instance);
|
||||
|
||||
System.Reflection.FieldInfo pooledTilesField = typeof(TrenchTilePool).GetField("pooledTiles",
|
||||
System.Reflection.BindingFlags.NonPublic | System.Reflection.BindingFlags.Instance);
|
||||
|
||||
System.Reflection.FieldInfo prefabUsageField = typeof(TrenchTilePool).GetField("prefabUsageCount",
|
||||
System.Reflection.BindingFlags.NonPublic | System.Reflection.BindingFlags.Instance);
|
||||
|
||||
if (totalCountField != null)
|
||||
// Handle Stack<T>
|
||||
if (pooledObjects is System.Collections.ICollection collection)
|
||||
{
|
||||
int totalCount = (int)totalCountField.GetValue(pool);
|
||||
EditorGUILayout.LabelField($"Total Pooled Objects: {totalCount}/{pool.totalMaxPoolSize}");
|
||||
count = collection.Count;
|
||||
}
|
||||
|
||||
if (pooledTilesField != null && prefabUsageField != null)
|
||||
int maxSize = 0;
|
||||
if (maxPoolSizeProp != null)
|
||||
{
|
||||
var pooledTiles = pooledTilesField.GetValue(pool) as Dictionary<int, Stack<GameObject>>;
|
||||
var usageCounts = prefabUsageField.GetValue(pool) as Dictionary<int, int>;
|
||||
|
||||
if (pooledTiles != null)
|
||||
maxSize = (int)maxPoolSizeProp.GetValue(poolComponent);
|
||||
}
|
||||
else
|
||||
{
|
||||
FieldInfo maxPoolSizeField = poolType.GetField("maxPoolSize",
|
||||
BindingFlags.Public | BindingFlags.Instance);
|
||||
if (maxPoolSizeField != null)
|
||||
{
|
||||
EditorGUILayout.LabelField("Prefab Details:", EditorStyles.boldLabel);
|
||||
EditorGUI.indentLevel++;
|
||||
|
||||
foreach (var entry in pooledTiles)
|
||||
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)
|
||||
{
|
||||
int prefabIndex = entry.Key;
|
||||
Stack<GameObject> stack = entry.Value;
|
||||
int count = stack != null ? stack.Count : 0;
|
||||
|
||||
int usageCount = 0;
|
||||
if (usageCounts != null && usageCounts.TryGetValue(prefabIndex, out int usage))
|
||||
{
|
||||
usageCount = usage;
|
||||
}
|
||||
|
||||
EditorGUILayout.LabelField($"Prefab {prefabIndex}: {count} pooled, {usageCount} usages");
|
||||
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;
|
||||
}
|
||||
|
||||
EditorGUI.indentLevel--;
|
||||
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++;
|
||||
}
|
||||
}
|
||||
|
||||
EditorGUI.indentLevel--;
|
||||
EditorGUILayout.Space();
|
||||
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();
|
||||
}
|
||||
}
|
||||
|
||||
void DisplayBubblePoolInfo()
|
||||
/// <summary>
|
||||
/// Finds all objects that derive from a generic base type
|
||||
/// </summary>
|
||||
private Component[] FindObjectsOfBaseType(Type baseComponentType, Type genericBaseType)
|
||||
{
|
||||
BubblePool[] pools = Object.FindObjectsByType<BubblePool>(FindObjectsSortMode.None);
|
||||
if (pools.Length == 0)
|
||||
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)
|
||||
{
|
||||
EditorGUILayout.HelpBox("No bubble pools found in the scene.", MessageType.Info);
|
||||
}
|
||||
else
|
||||
{
|
||||
EditorGUILayout.LabelField("Bubble Pools", EditorStyles.boldLabel);
|
||||
foreach (var pool in pools)
|
||||
Type componentType = component.GetType();
|
||||
|
||||
// Check if this type derives from the generic base type
|
||||
while (componentType != null && componentType != typeof(object))
|
||||
{
|
||||
EditorGUILayout.LabelField($"Pool: {pool.name}", EditorStyles.boldLabel);
|
||||
EditorGUI.indentLevel++;
|
||||
|
||||
// Get private field values using reflection
|
||||
System.Reflection.FieldInfo pooledBubblesField = typeof(BubblePool).GetField("pooledBubbles",
|
||||
System.Reflection.BindingFlags.NonPublic | System.Reflection.BindingFlags.Instance);
|
||||
|
||||
if (pooledBubblesField != null)
|
||||
if (componentType.IsGenericType &&
|
||||
componentType.GetGenericTypeDefinition() == genericBaseType)
|
||||
{
|
||||
var pooledBubbles = pooledBubblesField.GetValue(pool) as Stack<Bubble>;
|
||||
int count = pooledBubbles != null ? pooledBubbles.Count : 0;
|
||||
|
||||
EditorGUILayout.LabelField($"Pooled Bubbles: {count}/{pool.maxPoolSize}");
|
||||
EditorGUILayout.LabelField($"Initial Pool Size: {pool.initialPoolSize}");
|
||||
results.Add(component);
|
||||
break;
|
||||
}
|
||||
|
||||
// Try to find active bubbles in the scene
|
||||
Bubble[] activeBubbles = Object.FindObjectsByType<Bubble>(FindObjectsSortMode.None);
|
||||
int activeBubbleCount = 0;
|
||||
|
||||
foreach (var bubble in activeBubbles)
|
||||
// Also check for non-generic derived types
|
||||
if (componentType.BaseType != null &&
|
||||
componentType.BaseType.IsGenericType &&
|
||||
componentType.BaseType.GetGenericTypeDefinition() == genericBaseType)
|
||||
{
|
||||
if (bubble.gameObject.activeInHierarchy)
|
||||
{
|
||||
activeBubbleCount++;
|
||||
}
|
||||
results.Add(component);
|
||||
break;
|
||||
}
|
||||
|
||||
EditorGUILayout.LabelField($"Active Bubbles: {activeBubbleCount}");
|
||||
|
||||
EditorGUI.indentLevel--;
|
||||
EditorGUILayout.Space();
|
||||
componentType = componentType.BaseType;
|
||||
}
|
||||
}
|
||||
|
||||
return results.ToArray();
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
Reference in New Issue
Block a user