135 lines
4.4 KiB
C#
135 lines
4.4 KiB
C#
using System;
|
|
using System.Collections.Generic;
|
|
using UnityEngine;
|
|
using Zenject;
|
|
|
|
namespace FlappyBird.Core
|
|
{
|
|
public class ObjectPoolManager : IObjectPoolManager, IInitializable
|
|
{
|
|
private readonly DiContainer _container;
|
|
|
|
private readonly Dictionary<Type, Queue<Component>> _pools = new Dictionary<Type, Queue<Component>>();
|
|
private readonly Dictionary<Type, GameObject> _prefabs = new Dictionary<Type, GameObject>();
|
|
private readonly Dictionary<Type, Transform> _poolRoots = new Dictionary<Type, Transform>();
|
|
|
|
private Transform _mainPoolRoot;
|
|
|
|
[Inject]
|
|
public ObjectPoolManager(DiContainer container)
|
|
{
|
|
_container = container;
|
|
}
|
|
|
|
/// <summary>
|
|
/// Initializes the main root transform to keep the scene hierarchy clean.
|
|
/// </summary>
|
|
public void Initialize()
|
|
{
|
|
_mainPoolRoot = new GameObject("ObjectPools").transform;
|
|
UnityEngine.Object.DontDestroyOnLoad(_mainPoolRoot);
|
|
}
|
|
|
|
public void RegisterPool<T>(GameObject prefab, int initialSize = 0) where T : Component, IPoolable
|
|
{
|
|
Type type = typeof(T);
|
|
|
|
if (_pools.ContainsKey(type))
|
|
{
|
|
Debug.LogWarning($"[ObjectPoolManager] Pool for type {type.Name} is already registered.");
|
|
return;
|
|
}
|
|
|
|
_prefabs[type] = prefab;
|
|
_pools[type] = new Queue<Component>();
|
|
|
|
// Create a specific parent transform for this type
|
|
GameObject rootGo = new GameObject($"{type.Name}_Pool");
|
|
|
|
// Check if prefab is a UI element (RectTransform)
|
|
if (prefab.transform is RectTransform)
|
|
{
|
|
// UI elements need to stay under a Canvas even when pooled,
|
|
// or be moved to a hidden Canvas to stay "initialized"
|
|
rootGo.AddComponent<RectTransform>();
|
|
}
|
|
|
|
Transform typeRoot = rootGo.transform;
|
|
typeRoot.SetParent(_mainPoolRoot);
|
|
_poolRoots[type] = typeRoot;
|
|
|
|
// Pre-warm the pool
|
|
for (int i = 0; i < initialSize; i++)
|
|
{
|
|
T instance = CreateInstance<T>();
|
|
instance.gameObject.SetActive(false);
|
|
_pools[type].Enqueue(instance);
|
|
}
|
|
}
|
|
|
|
public T Get<T>() where T : Component, IPoolable
|
|
{
|
|
Type type = typeof(T);
|
|
|
|
if (!_pools.TryGetValue(type, out Queue<Component> pool))
|
|
{
|
|
throw new InvalidOperationException($"Pool for type {type.Name} is not registered.");
|
|
}
|
|
|
|
T instance = (pool.Count == 0) ? CreateInstance<T>() : pool.Dequeue() as T;
|
|
|
|
instance.gameObject.SetActive(true);
|
|
instance.OnSpawned();
|
|
|
|
return instance;
|
|
}
|
|
|
|
/// <summary>
|
|
/// Returns an item to its respective pool.
|
|
/// Automatically handles UI parenting back to the pool root.
|
|
/// </summary>
|
|
public void Return<T>(T item) where T : Component, IPoolable
|
|
{
|
|
if (item == null) return;
|
|
|
|
Type type = typeof(T);
|
|
|
|
if (!_pools.TryGetValue(type, out Queue<Component> pool))
|
|
{
|
|
// Fallback for cases where T might be an interface or base class
|
|
type = item.GetType();
|
|
if (!_pools.TryGetValue(type, out pool))
|
|
{
|
|
Debug.LogError($"[ObjectPoolManager] No pool registered for {type.Name}");
|
|
return;
|
|
}
|
|
}
|
|
|
|
item.OnDespawned();
|
|
|
|
// Re-parent to the specific pool root to keep Hierarchy clean
|
|
item.transform.SetParent(_poolRoots[type], false);
|
|
item.gameObject.SetActive(false);
|
|
|
|
pool.Enqueue(item);
|
|
}
|
|
|
|
private T CreateInstance<T>() where T : Component, IPoolable
|
|
{
|
|
Type type = typeof(T);
|
|
GameObject prefab = _prefabs[type];
|
|
Transform root = _poolRoots[type];
|
|
|
|
// Zenject InstantiatePrefab ensures [Inject] works inside the prefab (needed for ScorePopupView)
|
|
GameObject instance = _container.InstantiatePrefab(prefab, root);
|
|
T component = instance.GetComponent<T>();
|
|
|
|
if (component == null)
|
|
{
|
|
component = instance.AddComponent<T>();
|
|
}
|
|
|
|
return component;
|
|
}
|
|
}
|
|
} |