354 lines
12 KiB
C#
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();
|
|
}
|
|
}
|