Initial Commit

This commit is contained in:
2026-03-16 14:38:46 +02:00
commit b8f7327a21
2327 changed files with 253610 additions and 0 deletions
+135
View File
@@ -0,0 +1,135 @@
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;
}
}
}