Working generic object pooling, pool monitor editor tool and batch component adder editor tool
This commit is contained in:
@@ -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.
|
||||
/// </summary>
|
||||
public class TrenchTilePool : MonoBehaviour
|
||||
public class TrenchTilePool : MultiPrefabPool<Tile>
|
||||
{
|
||||
[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<int, Stack<GameObject>> pooledTiles = new Dictionary<int, Stack<GameObject>>();
|
||||
private Dictionary<int, int> prefabUsageCount = new Dictionary<int, int>();
|
||||
private List<GameObject> tilePrefabs;
|
||||
private int totalPooledCount = 0;
|
||||
|
||||
/// <summary>
|
||||
/// Initialize the pool with the tile prefabs
|
||||
/// Returns a tile to the pool
|
||||
/// </summary>
|
||||
/// <param name="prefabs">List of tile prefabs to use</param>
|
||||
public void Initialize(List<GameObject> prefabs)
|
||||
/// <param name="tile">The tile to return to the pool</param>
|
||||
/// <param name="prefabIndex">The index of the prefab this tile was created from</param>
|
||||
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<GameObject>();
|
||||
}
|
||||
|
||||
// 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<Tile>();
|
||||
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);
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
/// <summary>
|
||||
/// Creates a new tile instance and adds it to the pool
|
||||
/// </summary>
|
||||
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<GameObject>();
|
||||
}
|
||||
|
||||
pooledTiles[prefabIndex].Push(tile);
|
||||
totalPooledCount++;
|
||||
return tile;
|
||||
}
|
||||
|
||||
/// <summary>
|
||||
/// Gets a tile from the pool, or creates a new one if the pool is empty
|
||||
/// </summary>
|
||||
/// <returns>A tile instance ready to use</returns>
|
||||
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;
|
||||
}
|
||||
|
||||
/// <summary>
|
||||
/// Returns a tile to the pool
|
||||
/// </summary>
|
||||
/// <param name="tile">The tile to return to the pool</param>
|
||||
/// <param name="prefabIndex">The index of the prefab this tile was created from</param>
|
||||
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<GameObject>();
|
||||
}
|
||||
|
||||
pooledTiles[prefabIndex].Push(tile);
|
||||
totalPooledCount++;
|
||||
}
|
||||
else
|
||||
{
|
||||
Destroy(tile);
|
||||
}
|
||||
}
|
||||
|
||||
/// <summary>
|
||||
/// Trims the pool to remove excess objects
|
||||
/// Can be called periodically or when memory pressure is high
|
||||
/// </summary>
|
||||
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<KeyValuePair<int, int>> sortedUsage = new List<KeyValuePair<int, int>>(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;
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
/// <summary>
|
||||
/// Logs pool statistics to the console
|
||||
/// </summary>
|
||||
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
|
||||
}
|
||||
}
|
||||
|
||||
Reference in New Issue
Block a user