diff --git a/Assets/Editor/Utilities/BatchComponentAdder.cs b/Assets/Editor/Utilities/BatchComponentAdder.cs new file mode 100644 index 00000000..a4d79f00 --- /dev/null +++ b/Assets/Editor/Utilities/BatchComponentAdder.cs @@ -0,0 +1,227 @@ +using System; +using System.Collections.Generic; +using System.Linq; +using UnityEditor; +using UnityEngine; + +namespace Editor.Utilities +{ + public class BatchComponentAdder : EditorWindow + { + private Vector2 scrollPosition; + private List selectedPrefabs = new List(); + private string searchText = ""; + private List availableComponentTypes = new List(); + private List filteredComponentTypes = new List(); + private int selectedComponentIndex = -1; + private bool showScriptsOnly = true; + private bool showBuiltInComponents = false; + + [MenuItem("Tools/Batch Component Adder")] + public static void ShowWindow() + { + GetWindow("Batch Component Adder"); + } + + private void OnEnable() + { + // Get all component types when the window is opened + RefreshComponentTypes(); + } + + private void RefreshComponentTypes() + { + // Get all types that derive from Component + availableComponentTypes = AppDomain.CurrentDomain.GetAssemblies() + .SelectMany(assembly => assembly.GetTypes()) + .Where(type => type.IsSubclassOf(typeof(Component)) && !type.IsAbstract) + .OrderBy(type => type.Name) + .ToList(); + + // Apply initial filtering + FilterComponentTypes(); + } + + private void FilterComponentTypes() + { + filteredComponentTypes = availableComponentTypes + .Where(type => { + if (!showBuiltInComponents && type.Namespace != null && type.Namespace.StartsWith("UnityEngine")) + return false; + + if (showScriptsOnly && type.Namespace != null && type.Namespace.StartsWith("UnityEngine")) + return false; + + if (!string.IsNullOrEmpty(searchText)) + return type.Name.ToLower().Contains(searchText.ToLower()); + + return true; + }) + .ToList(); + + // Reset selection if it's no longer valid + if (selectedComponentIndex >= filteredComponentTypes.Count) + selectedComponentIndex = -1; + } + + private void OnGUI() + { + EditorGUILayout.BeginVertical(); + + EditorGUILayout.LabelField("Batch Component Adder", EditorStyles.boldLabel); + EditorGUILayout.HelpBox("Select multiple prefabs, choose a component type, and add it to the root of all selected prefabs.", MessageType.Info); + + EditorGUILayout.Space(); + + // Prefab selection section + EditorGUILayout.LabelField("Selected Prefabs", EditorStyles.boldLabel); + + if (GUILayout.Button("Add Selected Assets")) + { + AddSelectedAssets(); + } + + scrollPosition = EditorGUILayout.BeginScrollView(scrollPosition, GUILayout.Height(150)); + + for (int i = 0; i < selectedPrefabs.Count; i++) + { + EditorGUILayout.BeginHorizontal(); + + selectedPrefabs[i] = (GameObject)EditorGUILayout.ObjectField(selectedPrefabs[i], typeof(GameObject), false); + + if (GUILayout.Button("X", GUILayout.Width(20))) + { + selectedPrefabs.RemoveAt(i); + i--; + } + + EditorGUILayout.EndHorizontal(); + } + + EditorGUILayout.EndScrollView(); + + if (GUILayout.Button("Clear All")) + { + selectedPrefabs.Clear(); + } + + EditorGUILayout.Space(); + + // Component selection section + EditorGUILayout.LabelField("Component to Add", EditorStyles.boldLabel); + + EditorGUILayout.BeginHorizontal(); + showScriptsOnly = EditorGUILayout.Toggle("Scripts Only", showScriptsOnly); + showBuiltInComponents = EditorGUILayout.Toggle("Show Built-in Components", showBuiltInComponents); + EditorGUILayout.EndHorizontal(); + + string newSearchText = EditorGUILayout.TextField("Search", searchText); + if (newSearchText != searchText) + { + searchText = newSearchText; + FilterComponentTypes(); + } + + string[] componentNames = filteredComponentTypes.Select(t => t.Name).ToArray(); + + selectedComponentIndex = EditorGUILayout.Popup("Component Type", selectedComponentIndex, componentNames); + + EditorGUILayout.Space(); + + // Validate and add the component + GUI.enabled = selectedPrefabs.Count > 0 && selectedComponentIndex >= 0 && selectedComponentIndex < filteredComponentTypes.Count; + + if (GUILayout.Button("Add Component to Prefabs")) + { + AddComponentToPrefabs(); + } + + GUI.enabled = true; + + EditorGUILayout.EndVertical(); + } + + private void AddSelectedAssets() + { + UnityEngine.Object[] selectedObjects = Selection.objects; + + foreach (var obj in selectedObjects) + { + if (obj is GameObject go) + { + string path = AssetDatabase.GetAssetPath(go); + if (!string.IsNullOrEmpty(path) && path.EndsWith(".prefab")) + { + if (!selectedPrefabs.Contains(go)) + { + selectedPrefabs.Add(go); + } + } + } + } + } + + private void AddComponentToPrefabs() + { + if (selectedComponentIndex < 0 || selectedComponentIndex >= filteredComponentTypes.Count) + return; + + Type componentType = filteredComponentTypes[selectedComponentIndex]; + int successCount = 0; + List failedPrefabs = new List(); + + // For undo operations + Undo.RecordObjects(selectedPrefabs.ToArray(), "Add Component To Prefabs"); + + foreach (GameObject prefab in selectedPrefabs) + { + // Skip null entries + if (prefab == null) continue; + + try + { + // Open the prefab for editing + string prefabPath = AssetDatabase.GetAssetPath(prefab); + GameObject prefabRoot = PrefabUtility.LoadPrefabContents(prefabPath); + + // Check if the component already exists + if (prefabRoot.GetComponent(componentType) == null) + { + // Add the component + prefabRoot.AddComponent(componentType); + + // Save the prefab + PrefabUtility.SaveAsPrefabAsset(prefabRoot, prefabPath); + successCount++; + } + else + { + failedPrefabs.Add($"{prefab.name} (already has component)"); + } + + // Unload the prefab + PrefabUtility.UnloadPrefabContents(prefabRoot); + } + catch (Exception e) + { + Debug.LogError($"Error adding component to {prefab.name}: {e.Message}"); + failedPrefabs.Add($"{prefab.name} (error)"); + } + } + + // Show results + if (successCount > 0) + { + Debug.Log($"Successfully added {componentType.Name} to {successCount} prefabs."); + } + + if (failedPrefabs.Count > 0) + { + Debug.LogWarning($"Failed to add component to {failedPrefabs.Count} prefabs: {string.Join(", ", failedPrefabs)}"); + } + + // Refresh the asset database to show changes + AssetDatabase.Refresh(); + } + } +} diff --git a/Assets/Editor/Utilities/BatchComponentAdder.cs.meta b/Assets/Editor/Utilities/BatchComponentAdder.cs.meta new file mode 100644 index 00000000..c8684b4a --- /dev/null +++ b/Assets/Editor/Utilities/BatchComponentAdder.cs.meta @@ -0,0 +1,3 @@ +fileFormatVersion: 2 +guid: 34bcaf56206d4ec29cfa108c96622c37 +timeCreated: 1758027437 \ No newline at end of file diff --git a/Assets/Editor/Utilities/PoolMonitorWindow.cs b/Assets/Editor/Utilities/PoolMonitorWindow.cs index 6cf0e618..c50aae3a 100644 --- a/Assets/Editor/Utilities/PoolMonitorWindow.cs +++ b/Assets/Editor/Utilities/PoolMonitorWindow.cs @@ -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(FindObjectsSortMode.None); - foreach (var pool in tilePools) - { - pool.LogPoolStats(); - } - } - - if (showBubbles) - { - BubblePool[] bubblePools = Object.FindObjectsByType(FindObjectsSortMode.None); - foreach (var pool in bubblePools) + // Find all types that derive from BaseObjectPool + 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 + 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(FindObjectsSortMode.None).Length}"); + EditorGUILayout.LabelField($"Total GameObjects: {UnityEngine.Object.FindObjectsByType(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(FindObjectsSortMode.None); + // Find all types that derive from BaseObjectPool + 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 + 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>; - var usageCounts = prefabUsageField.GetValue(pool) as Dictionary; - - 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 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 + 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; } - 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() + /// + /// Finds all objects that derive from a generic base type + /// + private Component[] FindObjectsOfBaseType(Type baseComponentType, Type genericBaseType) { - BubblePool[] pools = Object.FindObjectsByType(FindObjectsSortMode.None); - if (pools.Length == 0) + List results = new List(); + + // Find all components in the scene + Component[] allComponents = UnityEngine.Object.FindObjectsByType(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; - 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(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(); } } } diff --git a/Assets/Prefabs/Minigames/DivingForPictures/Tile1.prefab b/Assets/Prefabs/Minigames/DivingForPictures/Tile1.prefab index e206dd86..0c86ad5c 100644 --- a/Assets/Prefabs/Minigames/DivingForPictures/Tile1.prefab +++ b/Assets/Prefabs/Minigames/DivingForPictures/Tile1.prefab @@ -183,6 +183,7 @@ GameObject: serializedVersion: 6 m_Component: - component: {fileID: 4925660644986369589} + - component: {fileID: 2488201930835981397} m_Layer: 0 m_Name: Tile1 m_TagString: Untagged @@ -207,3 +208,16 @@ Transform: - {fileID: 1003080013996268193} m_Father: {fileID: 0} m_LocalEulerAnglesHint: {x: 0, y: 0, z: 0} +--- !u!114 &2488201930835981397 +MonoBehaviour: + m_ObjectHideFlags: 0 + m_CorrespondingSourceObject: {fileID: 0} + m_PrefabInstance: {fileID: 0} + m_PrefabAsset: {fileID: 0} + m_GameObject: {fileID: 2956826569642009690} + m_Enabled: 1 + m_EditorHideFlags: 0 + m_Script: {fileID: 11500000, guid: 41def183b6714aca97663d74cc2d0678, type: 3} + m_Name: + m_EditorClassIdentifier: + tileIndex: 0 diff --git a/Assets/Prefabs/Minigames/DivingForPictures/Tile1_flipped.prefab b/Assets/Prefabs/Minigames/DivingForPictures/Tile1_flipped.prefab index accb5c42..0eba2c60 100644 --- a/Assets/Prefabs/Minigames/DivingForPictures/Tile1_flipped.prefab +++ b/Assets/Prefabs/Minigames/DivingForPictures/Tile1_flipped.prefab @@ -183,6 +183,7 @@ GameObject: serializedVersion: 6 m_Component: - component: {fileID: 4925660644986369589} + - component: {fileID: 7876353970701168068} m_Layer: 0 m_Name: Tile1_flipped m_TagString: Untagged @@ -207,3 +208,16 @@ Transform: - {fileID: 1003080013996268193} m_Father: {fileID: 0} m_LocalEulerAnglesHint: {x: 0, y: 0, z: 180} +--- !u!114 &7876353970701168068 +MonoBehaviour: + m_ObjectHideFlags: 0 + m_CorrespondingSourceObject: {fileID: 0} + m_PrefabInstance: {fileID: 0} + m_PrefabAsset: {fileID: 0} + m_GameObject: {fileID: 2956826569642009690} + m_Enabled: 1 + m_EditorHideFlags: 0 + m_Script: {fileID: 11500000, guid: 41def183b6714aca97663d74cc2d0678, type: 3} + m_Name: + m_EditorClassIdentifier: + tileIndex: 0 diff --git a/Assets/Prefabs/Minigames/DivingForPictures/Tile2.prefab b/Assets/Prefabs/Minigames/DivingForPictures/Tile2.prefab index cf78ee96..edf0dc6f 100644 --- a/Assets/Prefabs/Minigames/DivingForPictures/Tile2.prefab +++ b/Assets/Prefabs/Minigames/DivingForPictures/Tile2.prefab @@ -183,6 +183,7 @@ GameObject: serializedVersion: 6 m_Component: - component: {fileID: 4925660644986369589} + - component: {fileID: 2017387953723006367} m_Layer: 0 m_Name: Tile2 m_TagString: Untagged @@ -207,3 +208,16 @@ Transform: - {fileID: 1003080013996268193} m_Father: {fileID: 0} m_LocalEulerAnglesHint: {x: 0, y: 0, z: 0} +--- !u!114 &2017387953723006367 +MonoBehaviour: + m_ObjectHideFlags: 0 + m_CorrespondingSourceObject: {fileID: 0} + m_PrefabInstance: {fileID: 0} + m_PrefabAsset: {fileID: 0} + m_GameObject: {fileID: 2956826569642009690} + m_Enabled: 1 + m_EditorHideFlags: 0 + m_Script: {fileID: 11500000, guid: 41def183b6714aca97663d74cc2d0678, type: 3} + m_Name: + m_EditorClassIdentifier: + tileIndex: 0 diff --git a/Assets/Prefabs/Minigames/DivingForPictures/Tile2_flipped.prefab b/Assets/Prefabs/Minigames/DivingForPictures/Tile2_flipped.prefab index b3fffd4a..fe66e7f0 100644 --- a/Assets/Prefabs/Minigames/DivingForPictures/Tile2_flipped.prefab +++ b/Assets/Prefabs/Minigames/DivingForPictures/Tile2_flipped.prefab @@ -183,6 +183,7 @@ GameObject: serializedVersion: 6 m_Component: - component: {fileID: 4925660644986369589} + - component: {fileID: 451715946189956124} m_Layer: 0 m_Name: Tile2_flipped m_TagString: Untagged @@ -207,3 +208,16 @@ Transform: - {fileID: 1003080013996268193} m_Father: {fileID: 0} m_LocalEulerAnglesHint: {x: 0, y: 0, z: 180} +--- !u!114 &451715946189956124 +MonoBehaviour: + m_ObjectHideFlags: 0 + m_CorrespondingSourceObject: {fileID: 0} + m_PrefabInstance: {fileID: 0} + m_PrefabAsset: {fileID: 0} + m_GameObject: {fileID: 2956826569642009690} + m_Enabled: 1 + m_EditorHideFlags: 0 + m_Script: {fileID: 11500000, guid: 41def183b6714aca97663d74cc2d0678, type: 3} + m_Name: + m_EditorClassIdentifier: + tileIndex: 0 diff --git a/Assets/Prefabs/Minigames/DivingForPictures/Tile3.prefab b/Assets/Prefabs/Minigames/DivingForPictures/Tile3.prefab index 703bc8a9..39d42281 100644 --- a/Assets/Prefabs/Minigames/DivingForPictures/Tile3.prefab +++ b/Assets/Prefabs/Minigames/DivingForPictures/Tile3.prefab @@ -183,6 +183,7 @@ GameObject: serializedVersion: 6 m_Component: - component: {fileID: 4925660644986369589} + - component: {fileID: 8822397971507360111} m_Layer: 0 m_Name: Tile3 m_TagString: Untagged @@ -207,3 +208,16 @@ Transform: - {fileID: 1003080013996268193} m_Father: {fileID: 0} m_LocalEulerAnglesHint: {x: 0, y: 0, z: 0} +--- !u!114 &8822397971507360111 +MonoBehaviour: + m_ObjectHideFlags: 0 + m_CorrespondingSourceObject: {fileID: 0} + m_PrefabInstance: {fileID: 0} + m_PrefabAsset: {fileID: 0} + m_GameObject: {fileID: 2956826569642009690} + m_Enabled: 1 + m_EditorHideFlags: 0 + m_Script: {fileID: 11500000, guid: 41def183b6714aca97663d74cc2d0678, type: 3} + m_Name: + m_EditorClassIdentifier: + tileIndex: 0 diff --git a/Assets/Prefabs/Minigames/DivingForPictures/Tile3_flipped.prefab b/Assets/Prefabs/Minigames/DivingForPictures/Tile3_flipped.prefab index d33c5d92..687a609a 100644 --- a/Assets/Prefabs/Minigames/DivingForPictures/Tile3_flipped.prefab +++ b/Assets/Prefabs/Minigames/DivingForPictures/Tile3_flipped.prefab @@ -183,6 +183,7 @@ GameObject: serializedVersion: 6 m_Component: - component: {fileID: 4925660644986369589} + - component: {fileID: 2006557459409230470} m_Layer: 0 m_Name: Tile3_flipped m_TagString: Untagged @@ -207,3 +208,16 @@ Transform: - {fileID: 1003080013996268193} m_Father: {fileID: 0} m_LocalEulerAnglesHint: {x: 0, y: 0, z: 180} +--- !u!114 &2006557459409230470 +MonoBehaviour: + m_ObjectHideFlags: 0 + m_CorrespondingSourceObject: {fileID: 0} + m_PrefabInstance: {fileID: 0} + m_PrefabAsset: {fileID: 0} + m_GameObject: {fileID: 2956826569642009690} + m_Enabled: 1 + m_EditorHideFlags: 0 + m_Script: {fileID: 11500000, guid: 41def183b6714aca97663d74cc2d0678, type: 3} + m_Name: + m_EditorClassIdentifier: + tileIndex: 0 diff --git a/Assets/Scenes/MiniGames/DivingForPictures.unity b/Assets/Scenes/MiniGames/DivingForPictures.unity index c91a62a9..1870c8eb 100644 --- a/Assets/Scenes/MiniGames/DivingForPictures.unity +++ b/Assets/Scenes/MiniGames/DivingForPictures.unity @@ -1211,10 +1211,8 @@ MonoBehaviour: speedUpInterval: 0 maxMoveSpeed: 12 useObjectPooling: 1 - preInstantiateTiles: 0 - initialTilesPerPrefab: 1 maxPerPrefabPoolSize: 2 - totalMaxPoolSize: 10 + totalMaxPoolSize: 25 onTileSpawned: m_PersistentCalls: m_Calls: [] diff --git a/Assets/Scripts/Minigames/DivingForPictures/Bubble.cs b/Assets/Scripts/Minigames/DivingForPictures/Bubble.cs index 362d75b0..76a1c2fc 100644 --- a/Assets/Scripts/Minigames/DivingForPictures/Bubble.cs +++ b/Assets/Scripts/Minigames/DivingForPictures/Bubble.cs @@ -1,11 +1,12 @@ using UnityEngine; +using Pooling; namespace Minigames.DivingForPictures { /// /// Represents a single bubble, handling its movement, wobble effect, scaling, and sprite assignment. /// - public class Bubble : MonoBehaviour + public class Bubble : MonoBehaviour, IPoolableWithReference { public float speed = 1f; public float wobbleSpeed = 1f; @@ -87,6 +88,22 @@ namespace Minigames.DivingForPictures parentPool = pool; } + /// + /// Called when the object is retrieved from the pool. + /// + public void OnSpawn() + { + ResetState(); + } + + /// + /// Called when the object is returned to the pool. + /// + public void OnDespawn() + { + // Nothing to do here for now, but we could clean up resources + } + /// /// Sets the main sprite for the bubble. /// diff --git a/Assets/Scripts/Minigames/DivingForPictures/BubblePool.cs b/Assets/Scripts/Minigames/DivingForPictures/BubblePool.cs index 0112a12c..c9df15e7 100644 --- a/Assets/Scripts/Minigames/DivingForPictures/BubblePool.cs +++ b/Assets/Scripts/Minigames/DivingForPictures/BubblePool.cs @@ -1,82 +1,23 @@ -using System.Collections.Generic; -using UnityEngine; +using UnityEngine; +using Pooling; namespace Minigames.DivingForPictures { /// /// Manages a pool of bubble objects to reduce garbage collection overhead. /// - public class BubblePool : MonoBehaviour + public class BubblePool : BaseObjectPool { - [Tooltip("Initial number of bubbles to pre-instantiate")] - public int initialPoolSize = 10; - - [Tooltip("Maximum number of bubbles to keep in the pool")] - public int maxPoolSize = 30; - - private Stack pooledBubbles = new Stack(); - private Bubble bubblePrefab; - private int totalBubblesCreated = 0; - private int totalBubblesReturned = 0; - - /// - /// Initialize the pool with the bubble prefab - /// - /// The bubble prefab to use - public void Initialize(Bubble prefab) - { - bubblePrefab = prefab; - - // Pre-instantiate bubbles - for (int i = 0; i < initialPoolSize; i++) - { - CreateNewBubble(); - } - - Debug.Log($"BubblePool initialized with {initialPoolSize} bubbles"); - } - - /// - /// Creates a new bubble instance and adds it to the pool - /// - private Bubble CreateNewBubble() - { - if (bubblePrefab == null) - { - Debug.LogError("BubblePool: bubblePrefab is null! Call Initialize first."); - return null; - } - - Bubble bubble = Instantiate(bubblePrefab, transform); - bubble.gameObject.SetActive(false); - // Set the pool reference so the bubble knows where to return - bubble.SetPool(this); - pooledBubbles.Push(bubble); - totalBubblesCreated++; - return bubble; - } - /// /// Gets a bubble from the pool, or creates a new one if the pool is empty /// /// A bubble instance ready to use public Bubble GetBubble() { - Bubble bubble; + Bubble bubble = Get(); - if (pooledBubbles.Count > 0) - { - bubble = pooledBubbles.Pop(); - } - else - { - bubble = CreateNewBubble(); - } - - // Ensure the bubble has a reference to this pool + // Set reference to this pool so the bubble can return itself bubble.SetPool(this); - bubble.gameObject.SetActive(true); - bubble.ResetState(); return bubble; } @@ -87,31 +28,15 @@ namespace Minigames.DivingForPictures /// The bubble to return to the pool public void ReturnBubble(Bubble bubble) { - if (bubble == null) return; - - // Only add to pool if we're under the maximum size - if (pooledBubbles.Count < maxPoolSize) - { - // Deactivate and reparent - bubble.gameObject.SetActive(false); - bubble.transform.SetParent(transform); - - // Add to pool - pooledBubbles.Push(bubble); - totalBubblesReturned++; - } - else - { - Destroy(bubble.gameObject); - } + Return(bubble); } /// /// Logs pool statistics /// - public void LogPoolStats() + public override void LogPoolStats() { - Debug.Log($"[BubblePool] Pooled bubbles: {pooledBubbles.Count}/{maxPoolSize} (Created: {totalBubblesCreated}, Returned: {totalBubblesReturned})"); + Debug.Log($"[BubblePool] Pooled bubbles: {pooledObjects.Count}/{maxPoolSize} (Created: {totalCreated}, Returned: {totalReturned})"); } } } diff --git a/Assets/Scripts/Minigames/DivingForPictures/BubbleSpawner.cs b/Assets/Scripts/Minigames/DivingForPictures/BubbleSpawner.cs index afba3c80..31758f57 100644 --- a/Assets/Scripts/Minigames/DivingForPictures/BubbleSpawner.cs +++ b/Assets/Scripts/Minigames/DivingForPictures/BubbleSpawner.cs @@ -1,4 +1,5 @@ using UnityEngine; +using Pooling; namespace Minigames.DivingForPictures { @@ -43,6 +44,11 @@ namespace Minigames.DivingForPictures _bubblePool.initialPoolSize = initialPoolSize; _bubblePool.maxPoolSize = maxPoolSize; _bubblePool.Initialize(bubblePrefab); + + // Periodically check for pool statistics in debug builds + #if DEVELOPMENT_BUILD || UNITY_EDITOR + InvokeRepeating(nameof(LogPoolStats), 5f, 30f); + #endif } } @@ -112,5 +118,16 @@ namespace Minigames.DivingForPictures // Pass min/max scale for wobble clamping bubble.SetWobbleScaleLimits(wobbleMinScale, wobbleMaxScale); } + + /// + /// Logs the current pool statistics for debugging + /// + private void LogPoolStats() + { + if (_bubblePool != null) + { + _bubblePool.LogPoolStats(); + } + } } } \ No newline at end of file diff --git a/Assets/Scripts/Minigames/DivingForPictures/TrenchTilePool.cs b/Assets/Scripts/Minigames/DivingForPictures/TrenchTilePool.cs index 4f143eaa..2876925e 100644 --- a/Assets/Scripts/Minigames/DivingForPictures/TrenchTilePool.cs +++ b/Assets/Scripts/Minigames/DivingForPictures/TrenchTilePool.cs @@ -1,5 +1,6 @@ using System.Collections.Generic; using UnityEngine; +using Pooling; namespace Minigames.DivingForPictures { @@ -7,238 +8,38 @@ namespace Minigames.DivingForPictures /// Manages a pool of trench tile objects to reduce garbage collection overhead. /// Optimized for handling a large number of different prefab types. /// - public class TrenchTilePool : MonoBehaviour + public class TrenchTilePool : MultiPrefabPool { - [Tooltip("Whether to pre-instantiate tiles during initialization or create them on demand")] - public bool preInstantiateTiles = false; - - [Tooltip("Initial number of tiles to pre-instantiate per prefab (if preInstantiateTiles is true)")] - public int initialTilesPerPrefab = 2; - - [Tooltip("Maximum number of tiles to keep in the pool across all prefab types")] - public int totalMaxPoolSize = 50; - - [Tooltip("Maximum number of inactive instances to keep per prefab type")] - public int maxPerPrefabPoolSize = 5; - - [Tooltip("Maximum number of tiles to keep in the pool (legacy, use maxPerPrefabPoolSize instead)")] - public int maxPoolSize = 5; - - private Dictionary> pooledTiles = new Dictionary>(); - private Dictionary prefabUsageCount = new Dictionary(); - private List tilePrefabs; - private int totalPooledCount = 0; - /// - /// Initialize the pool with the tile prefabs + /// Returns a tile to the pool /// - /// List of tile prefabs to use - public void Initialize(List prefabs) + /// The tile to return to the pool + /// The index of the prefab this tile was created from + public void ReturnTile(GameObject tile, int prefabIndex) { - tilePrefabs = prefabs; - - // Initialize usage tracking - for (int i = 0; i < prefabs.Count; i++) + if (tile != null) { - prefabUsageCount[i] = 0; - pooledTiles[i] = new Stack(); - } - - // Pre-instantiate tiles only if enabled - if (preInstantiateTiles) - { - // Calculate how many to pre-instantiate based on available pool size - int totalToCreate = Mathf.Min(totalMaxPoolSize, prefabs.Count * initialTilesPerPrefab); - int perPrefab = Mathf.Max(1, totalToCreate / prefabs.Count); - - for (int i = 0; i < prefabs.Count; i++) + Tile tileComponent = tile.GetComponent(); + if (tileComponent != null) { - for (int j = 0; j < perPrefab; j++) - { - if (totalPooledCount >= totalMaxPoolSize) break; - CreateNewTile(i); - } + Return(tileComponent, prefabIndex); + } + else + { + Debug.LogWarning($"Attempted to return a GameObject without a Tile component: {tile.name}"); + Destroy(tile); } } } - /// - /// Creates a new tile instance and adds it to the pool - /// - private GameObject CreateNewTile(int prefabIndex) - { - if (tilePrefabs == null || prefabIndex >= tilePrefabs.Count) - { - Debug.LogError("TrenchTilePool: Invalid prefab index or tilePrefabs is null!"); - return null; - } - - GameObject prefab = tilePrefabs[prefabIndex]; - GameObject tile = Instantiate(prefab, transform); - tile.SetActive(false); - - if (!pooledTiles.ContainsKey(prefabIndex)) - { - pooledTiles[prefabIndex] = new Stack(); - } - - pooledTiles[prefabIndex].Push(tile); - totalPooledCount++; - return tile; - } - /// /// Gets a tile from the pool, or creates a new one if the pool is empty /// /// A tile instance ready to use public GameObject GetTile(int prefabIndex) { - GameObject tile; - - // Track usage frequency - if (prefabUsageCount.ContainsKey(prefabIndex)) - { - prefabUsageCount[prefabIndex]++; - } - else - { - prefabUsageCount[prefabIndex] = 1; - } - - if (pooledTiles.ContainsKey(prefabIndex) && pooledTiles[prefabIndex].Count > 0) - { - tile = pooledTiles[prefabIndex].Pop(); - totalPooledCount--; - } - else - { - // Create new tile without adding to pool - GameObject prefab = tilePrefabs[prefabIndex]; - tile = Instantiate(prefab, transform); - } - - tile.SetActive(true); - return tile; + Tile tileComponent = Get(prefabIndex); + return tileComponent.gameObject; } - - /// - /// Returns a tile to the pool - /// - /// The tile to return to the pool - /// The index of the prefab this tile was created from - public void ReturnTile(GameObject tile, int prefabIndex) - { - if (tile == null) return; - - // Check if we're under the maximum pool size for this prefab type - bool keepTile = totalPooledCount < totalMaxPoolSize; - - // Additional constraint: don't keep too many of any single prefab type - if (pooledTiles.ContainsKey(prefabIndex) && - pooledTiles[prefabIndex].Count >= maxPerPrefabPoolSize) - { - keepTile = false; - } - - if (keepTile) - { - tile.SetActive(false); - tile.transform.SetParent(transform); - - if (!pooledTiles.ContainsKey(prefabIndex)) - { - pooledTiles[prefabIndex] = new Stack(); - } - - pooledTiles[prefabIndex].Push(tile); - totalPooledCount++; - } - else - { - Destroy(tile); - } - } - - /// - /// Trims the pool to remove excess objects - /// Can be called periodically or when memory pressure is high - /// - public void TrimExcess() - { - // If we're under the limit, no need to trim - if (totalPooledCount <= totalMaxPoolSize) return; - - // Calculate how many to remove - int excessCount = totalPooledCount - totalMaxPoolSize; - - // Get prefab indices sorted by usage (least used first) - List> sortedUsage = new List>(prefabUsageCount); - sortedUsage.Sort((a, b) => a.Value.CompareTo(b.Value)); - - // Remove tiles from least used prefabs first - foreach (var usage in sortedUsage) - { - int prefabIndex = usage.Key; - if (!pooledTiles.ContainsKey(prefabIndex) || pooledTiles[prefabIndex].Count == 0) continue; - - // How many to remove from this prefab type - int toRemove = Mathf.Min(pooledTiles[prefabIndex].Count, excessCount); - - for (int i = 0; i < toRemove; i++) - { - if (pooledTiles[prefabIndex].Count == 0) break; - - GameObject tile = pooledTiles[prefabIndex].Pop(); - Destroy(tile); - totalPooledCount--; - excessCount--; - - if (excessCount <= 0) return; - } - } - } - - /// - /// Logs pool statistics to the console - /// - public void LogPoolStats() - { - Debug.Log($"[TrenchTilePool] Total pooled objects: {totalPooledCount}/{totalMaxPoolSize}"); - - string prefabDetails = ""; - int index = 0; - foreach (var entry in pooledTiles) - { - int prefabIndex = entry.Key; - int count = entry.Value.Count; - int usageCount = prefabUsageCount.ContainsKey(prefabIndex) ? prefabUsageCount[prefabIndex] : 0; - - string prefabName = prefabIndex < tilePrefabs.Count ? tilePrefabs[prefabIndex].name : "Unknown"; - prefabDetails += $"\n - {prefabName}: {count} pooled, {usageCount} usages"; - - // Limit the output to avoid too much text - if (++index >= 10 && pooledTiles.Count > 10) - { - prefabDetails += $"\n - ...and {pooledTiles.Count - 10} more prefab types"; - break; - } - } - - Debug.Log($"[TrenchTilePool] Pool details:{prefabDetails}"); - } - -#if UNITY_EDITOR - private float _lastLogTime = 0f; - - void Update() - { - // Log pool stats every 5 seconds if in the editor - if (Time.time - _lastLogTime > 5f) - { - LogPoolStats(); - _lastLogTime = Time.time; - } - } -#endif } } diff --git a/Assets/Scripts/Minigames/DivingForPictures/TrenchTileSpawner.cs b/Assets/Scripts/Minigames/DivingForPictures/TrenchTileSpawner.cs index 3c2cbfce..7d2bf387 100644 --- a/Assets/Scripts/Minigames/DivingForPictures/TrenchTileSpawner.cs +++ b/Assets/Scripts/Minigames/DivingForPictures/TrenchTileSpawner.cs @@ -2,6 +2,7 @@ using UnityEngine; using UnityEngine.Events; using UnityEngine.Serialization; +using Pooling; namespace Minigames.DivingForPictures { @@ -12,50 +13,105 @@ namespace Minigames.DivingForPictures { [Header("Tile Prefabs")] [Tooltip("List of possible trench tile prefabs.")] - public List tilePrefabs; + [SerializeField] private List tilePrefabs; [Header("Tile Settings")] - private Dictionary _tileHeights = new Dictionary(); - public int initialTileCount = 3; - public float tileSpawnBuffer = 1f; + [SerializeField] private int initialTileCount = 3; + [SerializeField] private float tileSpawnBuffer = 1f; [Header("Movement Settings")] - public float moveSpeed = 3f; - public float speedUpFactor = 0.2f; - public float speedUpInterval = 10f; - public float maxMoveSpeed = 12f; // Added a cap to the movement speed + [SerializeField] private float moveSpeed = 3f; + [SerializeField] private float speedUpFactor = 0.2f; + [SerializeField] private float speedUpInterval = 10f; + [SerializeField] private float maxMoveSpeed = 12f; [Header("Object Pooling")] - public bool useObjectPooling = true; - public bool preInstantiateTiles = false; // Added option to control pre-instantiation - public int initialTilesPerPrefab = 2; - public int maxPerPrefabPoolSize = 2; - public int totalMaxPoolSize = 10; // Total pool size across all prefab types + [SerializeField] private bool useObjectPooling = true; + [SerializeField] private int maxPerPrefabPoolSize = 2; + [SerializeField] private int totalMaxPoolSize = 10; - [FormerlySerializedAs("OnTileSpawned")] [Header("Events")] + [Header("Events")] + [FormerlySerializedAs("OnTileSpawned")] public UnityEvent onTileSpawned; + [FormerlySerializedAs("OnTileDestroyed")] public UnityEvent onTileDestroyed; + // Private fields + private readonly Dictionary _tileHeights = new Dictionary(); private readonly List _activeTiles = new List(); private readonly Dictionary _tileLastUsed = new Dictionary(); + private int _spawnCounter; private float _speedUpTimer; - private Camera _mainCamera; private float _screenBottom; private float _screenTop; private TrenchTilePool _tilePool; - private const float TileSpawnZ = -1f; // All spawned tiles should have z = -1 + private const float TileSpawnZ = -1f; + private const float DefaultTileHeight = 5f; - void Awake() + private void Awake() { _mainCamera = Camera.main; // Calculate tile heights for each prefab + CalculateTileHeights(); + + // Ensure all prefabs have Tile components + ValidateTilePrefabs(); + + if (useObjectPooling) + { + InitializeObjectPool(); + } + } + + // Validate that all prefabs have Tile components + private void ValidateTilePrefabs() + { + for (int i = 0; i < tilePrefabs.Count; i++) + { + if (tilePrefabs[i] == null) continue; + + // Check if the prefab has a Tile component + if (tilePrefabs[i].GetComponent() == null) + { + Debug.LogWarning($"Prefab {tilePrefabs[i].name} does not have a Tile component. Adding one automatically."); + // Add the Tile component if it doesn't exist + tilePrefabs[i].AddComponent(); + } + } + } + + private void Start() + { + CalculateScreenBounds(); + SpawnInitialTiles(); + } + + private void Update() + { + MoveTiles(); + HandleTileDestruction(); + HandleTileSpawning(); + HandleSpeedRamping(); + } + + /// + /// Calculate height values for all tile prefabs + /// + private void CalculateTileHeights() + { foreach (var prefab in tilePrefabs) { + if (prefab == null) + { + Debug.LogError("Null prefab found in tilePrefabs list!"); + continue; + } + Renderer renderer = prefab.GetComponentInChildren(); if (renderer != null) { @@ -64,31 +120,56 @@ namespace Minigames.DivingForPictures else { // Fallback in case no renderer is found - _tileHeights[prefab] = 5f; - Debug.LogWarning($"No renderer found in prefab {prefab.name}. Using default height of 5."); + _tileHeights[prefab] = DefaultTileHeight; + Debug.LogWarning($"No renderer found in prefab {prefab.name}. Using default height of {DefaultTileHeight}."); } } - - if (useObjectPooling) - { - // Create the tile pool - GameObject poolGO = new GameObject("TrenchTilePool"); - poolGO.transform.SetParent(transform); - _tilePool = poolGO.AddComponent(); - _tilePool.preInstantiateTiles = preInstantiateTiles; - _tilePool.initialTilesPerPrefab = initialTilesPerPrefab; - _tilePool.maxPerPrefabPoolSize = maxPerPrefabPoolSize; - _tilePool.totalMaxPoolSize = totalMaxPoolSize; - _tilePool.Initialize(tilePrefabs); - - // Periodically trim the pool to optimize memory usage - InvokeRepeating(nameof(TrimExcessPooledTiles), 10f, 30f); - } } - void Start() + /// + /// Initialize the object pool system + /// + private void InitializeObjectPool() + { + // Create the tile pool + GameObject poolGO = new GameObject("TrenchTilePool"); + poolGO.transform.SetParent(transform); + _tilePool = poolGO.AddComponent(); + + // Set up the pool configuration + _tilePool.maxPerPrefabPoolSize = maxPerPrefabPoolSize; + _tilePool.totalMaxPoolSize = totalMaxPoolSize; + + // Convert the GameObject list to a Tile list + List prefabTiles = new List(tilePrefabs.Count); + foreach (var prefab in tilePrefabs) + { + if (prefab != null) + { + Tile tileComponent = prefab.GetComponent(); + if (tileComponent != null) + { + prefabTiles.Add(tileComponent); + } + else + { + Debug.LogError($"Prefab {prefab.name} is missing a Tile component!"); + } + } + } + + // Initialize the pool with the tile component list + _tilePool.Initialize(prefabTiles); + + // Periodically trim the pool to optimize memory usage + InvokeRepeating(nameof(TrimExcessPooledTiles), 10f, 30f); + } + + /// + /// Spawn the initial set of tiles + /// + private void SpawnInitialTiles() { - CalculateScreenBounds(); for (int i = 0; i < initialTileCount; i++) { float y = _screenBottom; @@ -103,22 +184,30 @@ namespace Minigames.DivingForPictures } } - void Update() - { - MoveTiles(); - HandleTileDestruction(); - HandleTileSpawning(); - HandleSpeedRamping(); - } - + /// + /// Calculate the screen bounds in world space + /// private void CalculateScreenBounds() { + if (_mainCamera == null) + { + _mainCamera = Camera.main; + if (_mainCamera == null) + { + Debug.LogError("No main camera found!"); + return; + } + } + Vector3 bottom = _mainCamera.ViewportToWorldPoint(new Vector3(0.5f, 0f, _mainCamera.nearClipPlane)); Vector3 top = _mainCamera.ViewportToWorldPoint(new Vector3(0.5f, 1f, _mainCamera.nearClipPlane)); _screenBottom = bottom.y; _screenTop = top.y; } + /// + /// Move all active tiles upward + /// private void MoveTiles() { float moveDelta = moveSpeed * Time.deltaTime; @@ -131,10 +220,20 @@ namespace Minigames.DivingForPictures } } + /// + /// Check for tiles that have moved off screen and should be destroyed or returned to pool + /// private void HandleTileDestruction() { if (_activeTiles.Count == 0) return; + GameObject topTile = _activeTiles[0]; + if (topTile == null) + { + _activeTiles.RemoveAt(0); + return; + } + float tileHeight = GetTileHeight(topTile); if (topTile.transform.position.y - tileHeight / 2 > _screenTop + tileSpawnBuffer) { @@ -161,10 +260,20 @@ namespace Minigames.DivingForPictures } } + /// + /// Check if new tiles need to be spawned + /// private void HandleTileSpawning() { if (_activeTiles.Count == 0) return; + GameObject bottomTile = _activeTiles[^1]; + if (bottomTile == null) + { + _activeTiles.RemoveAt(_activeTiles.Count - 1); + return; + } + float tileHeight = GetTileHeight(bottomTile); float bottomEdge = bottomTile.transform.position.y - tileHeight / 2; if (bottomEdge > _screenBottom - tileSpawnBuffer) @@ -174,6 +283,9 @@ namespace Minigames.DivingForPictures } } + /// + /// Handle increasing the movement speed over time + /// private void HandleSpeedRamping() { _speedUpTimer += Time.deltaTime; @@ -184,15 +296,37 @@ namespace Minigames.DivingForPictures } } + /// + /// Spawn a new tile at the specified Y position + /// + /// The Y position to spawn at private void SpawnTileAtY(float y) { + if (tilePrefabs == null || tilePrefabs.Count == 0) + { + Debug.LogError("No tile prefabs available for spawning!"); + return; + } + int prefabIndex = GetWeightedRandomTileIndex(); GameObject prefab = tilePrefabs[prefabIndex]; + if (prefab == null) + { + Debug.LogError($"Tile prefab at index {prefabIndex} is null!"); + return; + } + GameObject tile; if (useObjectPooling && _tilePool != null) { tile = _tilePool.GetTile(prefabIndex); + if (tile == null) + { + Debug.LogError("Failed to get tile from pool!"); + return; + } + tile.transform.position = new Vector3(0f, y, TileSpawnZ); tile.transform.rotation = prefab.transform.rotation; tile.transform.SetParent(transform); @@ -208,27 +342,40 @@ namespace Minigames.DivingForPictures onTileSpawned?.Invoke(tile); } + /// + /// Gets a weighted random tile index, favoring tiles that haven't been used recently + /// + /// The selected prefab index private int GetWeightedRandomTileIndex() { - // Weight tiles not used recently higher - int n = tilePrefabs.Count; - List weights = new List(n); - for (int i = 0; i < n; i++) + int prefabCount = tilePrefabs.Count; + List weights = new List(prefabCount); + + for (int i = 0; i < prefabCount; i++) { - int lastUsed = _tileLastUsed.TryGetValue(i, out var value) ? value : -n; + int lastUsed = _tileLastUsed.TryGetValue(i, out var value) ? value : -prefabCount; int age = _spawnCounter - lastUsed; - float weight = Mathf.Clamp(age, 1, n * 2); // More unused = higher weight + float weight = Mathf.Clamp(age, 1, prefabCount * 2); // More unused = higher weight weights.Add(weight); } - float total = 0f; - foreach (var w in weights) total += w; - float r = Random.value * total; - for (int i = 0; i < n; i++) + + float totalWeight = 0f; + foreach (var weight in weights) { - if (r < weights[i]) return i; - r -= weights[i]; + totalWeight += weight; } - return Random.Range(0, n); // fallback + + float randomValue = Random.value * totalWeight; + for (int i = 0; i < prefabCount; i++) + { + if (randomValue < weights[i]) + { + return i; + } + randomValue -= weights[i]; + } + + return Random.Range(0, prefabCount); // fallback } /// @@ -238,9 +385,17 @@ namespace Minigames.DivingForPictures /// The height of the tile private float GetTileHeight(GameObject tile) { + if (tile == null) + { + Debug.LogWarning("Attempted to get height of null tile!"); + return DefaultTileHeight; + } + // Check if this is a known prefab foreach (var prefab in tilePrefabs) { + if (prefab == null) continue; + // Check if this tile was created from this prefab if (tile.name.StartsWith(prefab.name)) { @@ -259,7 +414,7 @@ namespace Minigames.DivingForPictures } // Fallback - return 5f; + return DefaultTileHeight; } /// @@ -269,8 +424,15 @@ namespace Minigames.DivingForPictures /// The index of the prefab or -1 if not found private int GetPrefabIndex(GameObject tile) { + if (tile == null || tilePrefabs == null) + { + return -1; + } + for (int i = 0; i < tilePrefabs.Count; i++) { + if (tilePrefabs[i] == null) continue; + if (tile.name.StartsWith(tilePrefabs[i].name)) { return i; @@ -291,14 +453,26 @@ namespace Minigames.DivingForPictures } #if UNITY_EDITOR - void OnDrawGizmosSelected() + private void OnDrawGizmosSelected() { + if (!Application.isPlaying) + { + // Only try to calculate this if _screenBottom hasn't been set by the game + Camera editorCam = Camera.main; + if (editorCam != null) + { + Vector3 bottom = editorCam.ViewportToWorldPoint(new Vector3(0.5f, 0f, editorCam.nearClipPlane)); + _screenBottom = bottom.y; + } + } + // Draw tile bounds for debugging Gizmos.color = Color.cyan; for (int i = 0; i < initialTileCount; i++) { - float height = 5f; - if (tilePrefabs.Count > 0 && _tileHeights.TryGetValue(tilePrefabs[0], out float h)) + float height = DefaultTileHeight; + if (tilePrefabs != null && tilePrefabs.Count > 0 && tilePrefabs[0] != null && + _tileHeights.TryGetValue(tilePrefabs[0], out float h)) { height = h; } diff --git a/Assets/Scripts/Pooling.meta b/Assets/Scripts/Pooling.meta new file mode 100644 index 00000000..4778d77f --- /dev/null +++ b/Assets/Scripts/Pooling.meta @@ -0,0 +1,3 @@ +fileFormatVersion: 2 +guid: a4883b41055949e99282264685fcd4f8 +timeCreated: 1758019051 \ No newline at end of file diff --git a/Assets/Scripts/Pooling/BaseObjectPool.cs b/Assets/Scripts/Pooling/BaseObjectPool.cs new file mode 100644 index 00000000..5ffea7c1 --- /dev/null +++ b/Assets/Scripts/Pooling/BaseObjectPool.cs @@ -0,0 +1,148 @@ +using System.Collections.Generic; +using UnityEngine; + +namespace Pooling +{ + /// + /// Abstract base class for object pools. + /// Provides common functionality for creating, retrieving, and returning pooled objects. + /// + /// The type of component to pool + public abstract class BaseObjectPool : MonoBehaviour where T : Component + { + [Tooltip("Initial number of objects to pre-instantiate")] + public int initialPoolSize = 10; + + [Tooltip("Maximum number of objects to keep in the pool")] + public int maxPoolSize = 30; + + protected Stack pooledObjects = new Stack(); + protected T prefab; + protected int totalCreated = 0; + protected int totalReturned = 0; + + /// + /// Initialize the pool with the specified prefab + /// + /// The prefab to use for this pool + public virtual void Initialize(T prefabToPool) + { + prefab = prefabToPool; + + // Pre-instantiate objects + for (int i = 0; i < initialPoolSize; i++) + { + CreateNew(); + } + + Debug.Log($"[{GetType().Name}] Initialized with {initialPoolSize} objects"); + } + + /// + /// Creates a new instance and adds it to the pool + /// + protected virtual T CreateNew() + { + if (prefab == null) + { + Debug.LogError($"[{GetType().Name}] Prefab is null! Call Initialize first."); + return null; + } + + T obj = Instantiate(prefab, transform); + obj.gameObject.SetActive(false); + + // Initialize IPoolable components if present + IPoolable poolable = obj.GetComponent(); + if (poolable != null) + { + poolable.OnDespawn(); + } + + pooledObjects.Push(obj); + totalCreated++; + return obj; + } + + /// + /// Gets an object from the pool, or creates a new one if the pool is empty + /// + /// An object ready to use + public virtual T Get() + { + T obj; + + if (pooledObjects.Count > 0) + { + obj = pooledObjects.Pop(); + } + else + { + obj = CreateNew(); + pooledObjects.Pop(); // Remove from pool since we're returning it + } + + obj.gameObject.SetActive(true); + + // Call OnSpawn for IPoolable components + IPoolable poolable = obj.GetComponent(); + if (poolable != null) + { + poolable.OnSpawn(); + } + + return obj; + } + + /// + /// Returns an object to the pool + /// + /// The object to return + public virtual void Return(T obj) + { + if (obj == null) return; + + // Call OnDespawn for IPoolable components + IPoolable poolable = obj.GetComponent(); + if (poolable != null) + { + poolable.OnDespawn(); + } + + // Only add to pool if we're under the maximum size + if (pooledObjects.Count < maxPoolSize) + { + obj.gameObject.SetActive(false); + obj.transform.SetParent(transform); + pooledObjects.Push(obj); + totalReturned++; + } + else + { + Destroy(obj.gameObject); + } + } + + /// + /// Logs pool statistics + /// + public virtual void LogPoolStats() + { + Debug.Log($"[{GetType().Name}] Pooled objects: {pooledObjects.Count}/{maxPoolSize} (Created: {totalCreated}, Returned: {totalReturned})"); + } + +#if UNITY_EDITOR + private float _lastLogTime = 0f; + + protected virtual void Update() + { + // Log pool stats every 5 seconds if in the editor + if (Time.time - _lastLogTime > 5f) + { + LogPoolStats(); + _lastLogTime = Time.time; + } + } +#endif + } +} diff --git a/Assets/Scripts/Pooling/BaseObjectPool.cs.meta b/Assets/Scripts/Pooling/BaseObjectPool.cs.meta new file mode 100644 index 00000000..ea523900 --- /dev/null +++ b/Assets/Scripts/Pooling/BaseObjectPool.cs.meta @@ -0,0 +1,3 @@ +fileFormatVersion: 2 +guid: 693c1b68b3cd4ad695dce000d53b04ee +timeCreated: 1758019542 \ No newline at end of file diff --git a/Assets/Scripts/Pooling/IPoolable.cs b/Assets/Scripts/Pooling/IPoolable.cs new file mode 100644 index 00000000..e2eabb7e --- /dev/null +++ b/Assets/Scripts/Pooling/IPoolable.cs @@ -0,0 +1,21 @@ +namespace Pooling +{ + /// + /// Interface for objects that can be pooled. + /// Implement this interface on any component that should be managed by an ObjectPool. + /// + public interface IPoolable + { + /// + /// Called when the object is retrieved from the pool. + /// Use this to reset the object's state. + /// + void OnSpawn(); + + /// + /// Called when the object is returned to the pool. + /// Use this to clean up any resources or state. + /// + void OnDespawn(); + } +} diff --git a/Assets/Scripts/Pooling/IPoolable.cs.meta b/Assets/Scripts/Pooling/IPoolable.cs.meta new file mode 100644 index 00000000..746f0cca --- /dev/null +++ b/Assets/Scripts/Pooling/IPoolable.cs.meta @@ -0,0 +1,3 @@ +fileFormatVersion: 2 +guid: 13d9095c12514097b9d5953c5b2a45a2 +timeCreated: 1758019051 \ No newline at end of file diff --git a/Assets/Scripts/Pooling/IPoolableWithReference.cs b/Assets/Scripts/Pooling/IPoolableWithReference.cs new file mode 100644 index 00000000..7e4939ac --- /dev/null +++ b/Assets/Scripts/Pooling/IPoolableWithReference.cs @@ -0,0 +1,18 @@ +using UnityEngine; + +namespace Pooling +{ + /// + /// Interface for poolable objects that need to reference their parent pool. + /// Implement this interface on objects that need to return themselves to their pool. + /// + /// The type of pool component + public interface IPoolableWithReference : IPoolable where T : Component + { + /// + /// Sets the parent pool for this object + /// + /// The pool this object belongs to + void SetPool(T pool); + } +} diff --git a/Assets/Scripts/Pooling/IPoolableWithReference.cs.meta b/Assets/Scripts/Pooling/IPoolableWithReference.cs.meta new file mode 100644 index 00000000..9892de2d --- /dev/null +++ b/Assets/Scripts/Pooling/IPoolableWithReference.cs.meta @@ -0,0 +1,3 @@ +fileFormatVersion: 2 +guid: 99a99ac50049492195a73883d9129ea8 +timeCreated: 1758019587 \ No newline at end of file diff --git a/Assets/Scripts/Pooling/MultiPrefabPool.cs b/Assets/Scripts/Pooling/MultiPrefabPool.cs new file mode 100644 index 00000000..978d1131 --- /dev/null +++ b/Assets/Scripts/Pooling/MultiPrefabPool.cs @@ -0,0 +1,266 @@ +using System.Collections.Generic; +using UnityEngine; + +namespace Pooling +{ + /// + /// Object pool that supports multiple prefab types with usage tracking and intelligent trimming. + /// + /// The type of component to pool + public abstract class MultiPrefabPool : MonoBehaviour where T : Component + { + [Tooltip("Whether to pre-instantiate objects during initialization or create them on demand")] + public bool preInstantiatePrefabs = false; + + [Tooltip("Initial number of objects to pre-instantiate per prefab (if preInstantiatePrefabs is true)")] + public int initialObjectsPerPrefab = 2; + + [Tooltip("Maximum number of objects to keep in the pool across all prefab types")] + public int totalMaxPoolSize = 50; + + [Tooltip("Maximum number of inactive instances to keep per prefab type")] + public int maxPerPrefabPoolSize = 5; + + protected Dictionary> pooledObjects = new Dictionary>(); + protected Dictionary prefabUsageCount = new Dictionary(); + protected List prefabs; + protected int totalPooledCount = 0; + + /// + /// Initialize the pool with the specified prefabs + /// + /// The list of prefabs to use for this pool + public virtual void Initialize(List prefabsToPool) + { + prefabs = prefabsToPool; + + // Initialize usage tracking + for (int i = 0; i < prefabs.Count; i++) + { + prefabUsageCount[i] = 0; + pooledObjects[i] = new Stack(); + } + + // Pre-instantiate objects only if enabled + if (preInstantiatePrefabs) + { + // Calculate how many to pre-instantiate based on available pool size + int totalToCreate = Mathf.Min(totalMaxPoolSize, prefabs.Count * initialObjectsPerPrefab); + int perPrefab = Mathf.Max(1, totalToCreate / prefabs.Count); + + for (int i = 0; i < prefabs.Count; i++) + { + for (int j = 0; j < perPrefab; j++) + { + if (totalPooledCount >= totalMaxPoolSize) break; + CreateNew(i); + } + } + } + + Debug.Log($"[{GetType().Name}] Initialized with {prefabs.Count} prefab types"); + } + + /// + /// Creates a new instance and adds it to the pool + /// + protected virtual T CreateNew(int prefabIndex) + { + if (prefabs == null || prefabIndex >= prefabs.Count) + { + Debug.LogError($"[{GetType().Name}] Invalid prefab index or prefabs list is null!"); + return null; + } + + T prefab = prefabs[prefabIndex]; + T obj = Instantiate(prefab, transform); + obj.gameObject.SetActive(false); + + // Initialize IPoolable components if present + IPoolable poolable = obj.GetComponent(); + if (poolable != null) + { + poolable.OnDespawn(); + } + + if (!pooledObjects.ContainsKey(prefabIndex)) + { + pooledObjects[prefabIndex] = new Stack(); + } + + pooledObjects[prefabIndex].Push(obj); + totalPooledCount++; + return obj; + } + + /// + /// Gets an object from the pool for the specified prefab index, or creates a new one if the pool is empty + /// + /// The index of the prefab to get from the pool + /// An object ready to use + public virtual T Get(int prefabIndex) + { + T obj; + + // Track usage frequency + if (prefabUsageCount.ContainsKey(prefabIndex)) + { + prefabUsageCount[prefabIndex]++; + } + else + { + prefabUsageCount[prefabIndex] = 1; + } + + if (pooledObjects.ContainsKey(prefabIndex) && pooledObjects[prefabIndex].Count > 0) + { + obj = pooledObjects[prefabIndex].Pop(); + totalPooledCount--; + } + else + { + // Create new object without adding to pool + T prefab = prefabs[prefabIndex]; + obj = Instantiate(prefab, transform); + } + + obj.gameObject.SetActive(true); + + // Call OnSpawn for IPoolable components + IPoolable poolable = obj.GetComponent(); + if (poolable != null) + { + poolable.OnSpawn(); + } + + return obj; + } + + /// + /// Returns an object to the pool + /// + /// The object to return + /// The index of the prefab this object was created from + public virtual void Return(T obj, int prefabIndex) + { + if (obj == null) return; + + // Call OnDespawn for IPoolable components + IPoolable poolable = obj.GetComponent(); + if (poolable != null) + { + poolable.OnDespawn(); + } + + // Check if we're under the maximum pool size for this prefab type + bool keepObject = totalPooledCount < totalMaxPoolSize; + + // Additional constraint: don't keep too many of any single prefab type + if (pooledObjects.ContainsKey(prefabIndex) && + pooledObjects[prefabIndex].Count >= maxPerPrefabPoolSize) + { + keepObject = false; + } + + if (keepObject) + { + obj.gameObject.SetActive(false); + obj.transform.SetParent(transform); + + if (!pooledObjects.ContainsKey(prefabIndex)) + { + pooledObjects[prefabIndex] = new Stack(); + } + + pooledObjects[prefabIndex].Push(obj); + totalPooledCount++; + } + else + { + Destroy(obj.gameObject); + } + } + + /// + /// Trims the pool to remove excess objects + /// Can be called periodically or when memory pressure is high + /// + public virtual void TrimExcess() + { + // If we're under the limit, no need to trim + if (totalPooledCount <= totalMaxPoolSize) return; + + // Calculate how many to remove + int excessCount = totalPooledCount - totalMaxPoolSize; + + // Get prefab indices sorted by usage (least used first) + List> sortedUsage = new List>(prefabUsageCount); + sortedUsage.Sort((a, b) => a.Value.CompareTo(b.Value)); + + // Remove objects from least used prefabs first + foreach (var usage in sortedUsage) + { + int prefabIndex = usage.Key; + if (!pooledObjects.ContainsKey(prefabIndex) || pooledObjects[prefabIndex].Count == 0) continue; + + // How many to remove from this prefab type + int toRemove = Mathf.Min(pooledObjects[prefabIndex].Count, excessCount); + + for (int i = 0; i < toRemove; i++) + { + if (pooledObjects[prefabIndex].Count == 0) break; + + T obj = pooledObjects[prefabIndex].Pop(); + Destroy(obj.gameObject); + totalPooledCount--; + excessCount--; + + if (excessCount <= 0) return; + } + } + } + + /// + /// Logs pool statistics to the console + /// + public virtual void LogPoolStats() + { + Debug.Log($"[{GetType().Name}] Total pooled objects: {totalPooledCount}/{totalMaxPoolSize}"); + + string prefabDetails = ""; + int index = 0; + foreach (var entry in pooledObjects) + { + int prefabIndex = entry.Key; + int count = entry.Value.Count; + int usageCount = prefabUsageCount.ContainsKey(prefabIndex) ? prefabUsageCount[prefabIndex] : 0; + + string prefabName = prefabIndex < prefabs.Count ? prefabs[prefabIndex].name : "Unknown"; + prefabDetails += $"\n - {prefabName}: {count} pooled, {usageCount} usages"; + + // Limit the output to avoid too much text + if (++index >= 10 && pooledObjects.Count > 10) + { + prefabDetails += $"\n - ...and {pooledObjects.Count - 10} more prefab types"; + break; + } + } + + Debug.Log($"[{GetType().Name}] Pool details:{prefabDetails}"); + } + +#if UNITY_EDITOR + private float _lastLogTime = 0f; + + protected virtual void Update() + { + // Log pool stats every 5 seconds if in the editor + if (Time.time - _lastLogTime > 5f) + { + LogPoolStats(); + _lastLogTime = Time.time; + } + } +#endif + } +} diff --git a/Assets/Scripts/Pooling/MultiPrefabPool.cs.meta b/Assets/Scripts/Pooling/MultiPrefabPool.cs.meta new file mode 100644 index 00000000..afaaa9e5 --- /dev/null +++ b/Assets/Scripts/Pooling/MultiPrefabPool.cs.meta @@ -0,0 +1,3 @@ +fileFormatVersion: 2 +guid: 344a70ff5291463bbd636705705ace79 +timeCreated: 1758019578 \ No newline at end of file diff --git a/Assets/Scripts/Tile.cs b/Assets/Scripts/Tile.cs new file mode 100644 index 00000000..14844d35 --- /dev/null +++ b/Assets/Scripts/Tile.cs @@ -0,0 +1,19 @@ +using UnityEngine; + +/// +/// A simple marker component to identify game objects as tiles. +/// This allows the pool system to specifically track and manage tile objects. +/// +public class Tile : MonoBehaviour +{ + // This is primarily a marker component, but we could add tile-specific properties here if needed + + // Optional: Add properties that might be useful for all tiles + [SerializeField] private int tileIndex; + + public int TileIndex + { + get => tileIndex; + set => tileIndex = value; + } +} diff --git a/Assets/Scripts/Tile.cs.meta b/Assets/Scripts/Tile.cs.meta new file mode 100644 index 00000000..d1434779 --- /dev/null +++ b/Assets/Scripts/Tile.cs.meta @@ -0,0 +1,3 @@ +fileFormatVersion: 2 +guid: 41def183b6714aca97663d74cc2d0678 +timeCreated: 1758027131 \ No newline at end of file