Files
XRLib/Assets/Scripts/UVC/UI/Window/PropertyWindow/Pool/PropertyUIPool.cs
UVCLimHun 6b78b68229 merge
merge and property
2025-12-24 17:36:01 +09:00

354 lines
12 KiB
C#

using System.Collections.Generic;
using UnityEngine;
namespace UVC.UI.Window.PropertyWindow
{
/// <summary>
/// PropertyUI 오브젝트들을 풀링하여 재사용하는 시스템입니다.
/// 매번 Instantiate/Destroy 대신 풀에서 가져오고 반환하여 성능을 향상시킵니다.
/// </summary>
public class PropertyUIPool : MonoBehaviour
{
[Header("Pool Settings")]
[SerializeField] private int _defaultPoolSize = 5;
[SerializeField] private Transform _poolContainer;
[Header("Prefabs")]
[SerializeField] private GameObject _groupPrefab;
[SerializeField] private GameObject _stringPropertyPrefab;
[SerializeField] private GameObject _numberPropertyPrefab;
[SerializeField] private GameObject _boolPropertyPrefab;
[SerializeField] private GameObject _vector2PropertyPrefab;
[SerializeField] private GameObject _vector3PropertyPrefab;
[SerializeField] private GameObject _colorPropertyPrefab;
[SerializeField] private GameObject _datePropertyPrefab;
[SerializeField] private GameObject _dateTimePropertyPrefab;
[SerializeField] private GameObject _enumPropertyPrefab;
[SerializeField] private GameObject _listPropertyPrefab;
[SerializeField] private GameObject _radioGroupPropertyPrefab;
[SerializeField] private GameObject _numberRangePropertyPrefab;
[SerializeField] private GameObject _dateRangePropertyPrefab;
[SerializeField] private GameObject _dateTimeRangePropertyPrefab;
/// <summary>
/// PropertyType별 UI 오브젝트 풀
/// </summary>
private readonly Dictionary<PropertyType, Queue<GameObject>> _itemPools = new Dictionary<PropertyType, Queue<GameObject>>();
/// <summary>
/// 그룹 UI 오브젝트 풀
/// </summary>
private readonly Queue<PropertyGroupView> _groupPool = new Queue<PropertyGroupView>();
/// <summary>
/// PropertyType별 프리팹 매핑
/// </summary>
private Dictionary<PropertyType, GameObject> _prefabMap;
private bool _isInitialized = false;
private void Awake()
{
Initialize();
}
/// <summary>
/// 풀을 초기화합니다.
/// </summary>
public void Initialize()
{
if (_isInitialized) return;
// 풀 컨테이너 생성 (없으면)
if (_poolContainer == null)
{
var containerObj = new GameObject("PropertyUIPool_Container");
containerObj.transform.SetParent(transform);
containerObj.SetActive(false);
_poolContainer = containerObj.transform;
}
// 프리팹 맵 초기화
InitializePrefabMap();
// 각 타입별 풀 초기화
foreach (PropertyType type in System.Enum.GetValues(typeof(PropertyType)))
{
if (!_itemPools.ContainsKey(type))
{
_itemPools[type] = new Queue<GameObject>();
}
}
_isInitialized = true;
}
/// <summary>
/// PropertyType별 프리팹 매핑을 초기화합니다.
/// </summary>
private void InitializePrefabMap()
{
_prefabMap = new Dictionary<PropertyType, GameObject>
{
{ PropertyType.String, _stringPropertyPrefab },
{ PropertyType.Int, _numberPropertyPrefab },
{ PropertyType.Float, _numberPropertyPrefab },
{ PropertyType.Bool, _boolPropertyPrefab },
{ PropertyType.Vector2, _vector2PropertyPrefab },
{ PropertyType.Vector3, _vector3PropertyPrefab },
{ PropertyType.Color, _colorPropertyPrefab },
{ PropertyType.Date, _datePropertyPrefab },
{ PropertyType.DateTime, _dateTimePropertyPrefab },
{ PropertyType.Enum, _enumPropertyPrefab },
{ PropertyType.DropdownList, _listPropertyPrefab },
{ PropertyType.RadioGroup, _radioGroupPropertyPrefab },
{ PropertyType.IntRange, _numberRangePropertyPrefab },
{ PropertyType.FloatRange, _numberRangePropertyPrefab },
{ PropertyType.DateRange, _dateRangePropertyPrefab },
{ PropertyType.DateTimeRange, _dateTimeRangePropertyPrefab }
};
}
#region Item Pool Methods
/// <summary>
/// 풀에서 PropertyUI 오브젝트를 가져옵니다.
/// 풀이 비어있으면 새로 생성합니다.
/// </summary>
/// <param name="type">속성 타입</param>
/// <returns>UI GameObject 또는 프리팹이 없으면 null</returns>
public GameObject GetItemUI(PropertyType type)
{
if (!_isInitialized) Initialize();
// 풀에서 가져오기 시도
if (_itemPools.TryGetValue(type, out var pool) && pool.Count > 0)
{
var obj = pool.Dequeue();
obj.SetActive(true);
return obj;
}
// 풀이 비어있으면 새로 생성
return CreateItemUI(type);
}
/// <summary>
/// PropertyUI 오브젝트를 풀에 반환합니다.
/// </summary>
/// <param name="type">속성 타입</param>
/// <param name="obj">반환할 오브젝트</param>
public void ReturnItemUI(PropertyType type, GameObject obj)
{
if (obj == null) return;
// UI 컴포넌트 정리
var propertyUI = obj.GetComponent<IPropertyUI>();
if (propertyUI is IPoolable poolable)
{
poolable.OnReturnToPool();
}
obj.SetActive(false);
obj.transform.SetParent(_poolContainer, false);
if (_itemPools.TryGetValue(type, out var pool))
{
pool.Enqueue(obj);
}
}
/// <summary>
/// 새로운 PropertyUI 오브젝트를 생성합니다.
/// </summary>
private GameObject CreateItemUI(PropertyType type)
{
if (_prefabMap == null || !_prefabMap.TryGetValue(type, out var prefab) || prefab == null)
{
Debug.LogWarning($"[PropertyUIPool] '{type}' 타입에 대한 프리팹이 설정되지 않았습니다.");
return null;
}
var obj = Instantiate(prefab);
return obj;
}
#endregion
#region Group Pool Methods
/// <summary>
/// 풀에서 PropertyGroupView를 가져옵니다.
/// </summary>
/// <returns>PropertyGroupView 또는 프리팹이 없으면 null</returns>
public PropertyGroupView GetGroupUI()
{
if (!_isInitialized) Initialize();
// 풀에서 가져오기 시도
if (_groupPool.Count > 0)
{
var groupView = _groupPool.Dequeue();
groupView.gameObject.SetActive(true);
return groupView;
}
// 풀이 비어있으면 새로 생성
return CreateGroupUI();
}
/// <summary>
/// PropertyGroupView를 풀에 반환합니다.
/// </summary>
/// <param name="groupView">반환할 그룹 뷰</param>
public void ReturnGroupUI(PropertyGroupView groupView)
{
if (groupView == null) return;
groupView.Reset();
groupView.gameObject.SetActive(false);
groupView.transform.SetParent(_poolContainer, false);
_groupPool.Enqueue(groupView);
}
/// <summary>
/// 새로운 PropertyGroupView를 생성합니다.
/// </summary>
private PropertyGroupView CreateGroupUI()
{
if (_groupPrefab == null)
{
Debug.LogWarning("[PropertyUIPool] 그룹 프리팹이 설정되지 않았습니다.");
return null;
}
var obj = Instantiate(_groupPrefab);
return obj.GetComponent<PropertyGroupView>();
}
#endregion
#region Pool Management
/// <summary>
/// 특정 타입의 UI를 미리 생성하여 풀에 추가합니다.
/// </summary>
/// <param name="type">속성 타입</param>
/// <param name="count">미리 생성할 개수</param>
public void Prewarm(PropertyType type, int count)
{
if (!_isInitialized) Initialize();
for (int i = 0; i < count; i++)
{
var obj = CreateItemUI(type);
if (obj != null)
{
ReturnItemUI(type, obj);
}
}
}
/// <summary>
/// 그룹 UI를 미리 생성하여 풀에 추가합니다.
/// </summary>
/// <param name="count">미리 생성할 개수</param>
public void PrewarmGroups(int count)
{
if (!_isInitialized) Initialize();
for (int i = 0; i < count; i++)
{
var groupView = CreateGroupUI();
if (groupView != null)
{
ReturnGroupUI(groupView);
}
}
}
/// <summary>
/// 모든 풀을 비우고 오브젝트를 파괴합니다.
/// </summary>
public void Clear()
{
// 아이템 풀 정리
foreach (var pool in _itemPools.Values)
{
while (pool.Count > 0)
{
var obj = pool.Dequeue();
if (obj != null)
{
Destroy(obj);
}
}
}
_itemPools.Clear();
// 그룹 풀 정리
while (_groupPool.Count > 0)
{
var groupView = _groupPool.Dequeue();
if (groupView != null)
{
Destroy(groupView.gameObject);
}
}
// 풀 컨테이너의 모든 자식 삭제
if (_poolContainer != null)
{
foreach (Transform child in _poolContainer)
{
Destroy(child.gameObject);
}
}
}
/// <summary>
/// 현재 풀 상태를 반환합니다 (디버그용).
/// </summary>
public string GetPoolStatus()
{
var status = new System.Text.StringBuilder();
status.AppendLine("[PropertyUIPool Status]");
status.AppendLine($"Groups in pool: {_groupPool.Count}");
foreach (var kvp in _itemPools)
{
if (kvp.Value.Count > 0)
{
status.AppendLine($"{kvp.Key}: {kvp.Value.Count}");
}
}
return status.ToString();
}
#endregion
private void OnDestroy()
{
Clear();
}
}
/// <summary>
/// 풀링 가능한 UI 컴포넌트가 구현해야 하는 인터페이스입니다.
/// </summary>
public interface IPoolable
{
/// <summary>
/// 풀에서 가져올 때 호출됩니다.
/// </summary>
void OnGetFromPool();
/// <summary>
/// 풀에 반환될 때 호출됩니다.
/// 이벤트 해제 및 상태 초기화를 수행해야 합니다.
/// </summary>
void OnReturnToPool();
}
}