换成不触发active的对象池队列模式

This commit is contained in:
2026-07-28 10:27:45 +08:00
parent 28a850e342
commit b6557db618
4 changed files with 168 additions and 139 deletions
@@ -37,10 +37,6 @@ namespace XericUI.XTable.Rendering.Elements
#region 字段
private Transform m_Parent;
private Transform m_PoolRoot;
/// <summary>池根节点(所有回收对象均挂在此节点下,节点本身处于 inactive)</summary>
public Transform PoolRoot => m_PoolRoot;
/// <summary>运行时的默认样式命名空间(编辑器下为 null,不绑定)</summary>
private string m_DefaultStyleNamespace;
@@ -48,14 +44,20 @@ namespace XericUI.XTable.Rendering.Elements
/// <summary>所有已绑定的 StyleBoundElement 回调集合(用于样式变更时通知外部)</summary>
public event System.Action OnStyleChangeDetected;
private Stack<PooledImage> m_ImagePool = new Stack<PooledImage>();
private Stack<PooledText> m_TextPool = new Stack<PooledText>();
private Queue<PooledImage> m_ImagePool = new Queue<PooledImage>();
private Queue<PooledText> m_TextPool = new Queue<PooledText>();
private List<PooledImage> m_ActiveImages = new List<PooledImage>();
private List<PooledText> m_ActiveTexts = new List<PooledText>();
/// <summary>HideFlags 应用于所有动态创建的对象(不保存、不在 Hierarchy 显示)</summary>
private const HideFlags POOL_FLAGS = HideFlags.DontSave | HideFlags.HideInHierarchy;
/// <summary>渲染项目总数上限,超出后 GetImage/GetText 返回 null 并发出警告</summary>
private const int MAX_POOL_ITEMS = 256;
private int m_ImageCreatedCount;
private int m_TextCreatedCount;
#endregion
#region 构造函数与析构
@@ -64,12 +66,6 @@ namespace XericUI.XTable.Rendering.Elements
{
m_Parent = parent;
m_DefaultStyleNamespace = defaultStyleNamespace;
GameObject poolRoot = new GameObject("_CellPool");
poolRoot.hideFlags = POOL_FLAGS;
poolRoot.transform.SetParent(parent, false);
poolRoot.SetActive(false);
m_PoolRoot = poolRoot.transform;
}
/// <summary>彻底销毁所有池对象(包括活跃和休眠的)</summary>
@@ -91,18 +87,11 @@ namespace XericUI.XTable.Rendering.Elements
// 杀死池中对象
while (m_ImagePool.Count > 0)
{
KillObject(m_ImagePool.Pop().Root);
KillObject(m_ImagePool.Dequeue().Root);
}
while (m_TextPool.Count > 0)
{
KillObject(m_TextPool.Pop().Root);
}
// 杀死池根
if (m_PoolRoot != null)
{
KillObject(m_PoolRoot.gameObject);
m_PoolRoot = null;
KillObject(m_TextPool.Dequeue().Root);
}
}
@@ -132,14 +121,18 @@ namespace XericUI.XTable.Rendering.Elements
PooledImage item;
if (m_ImagePool.Count > 0)
{
item = m_ImagePool.Pop();
item.Root.SetActive(true);
item.Root.transform.SetParent(m_Parent, false);
item = m_ImagePool.Dequeue();
ResetImageDefaults(item);
}
else if (m_ImageCreatedCount < MAX_POOL_ITEMS)
{
item = CreateImage(name);
m_ImageCreatedCount++;
}
else
{
item = CreateImage(name);
Debug.LogWarning($"[XericUIActionTable] Image 池已达上限 {MAX_POOL_ITEMS},无法分配新对象");
return null;
}
BindStyle(item.Style, styleNamespace);
m_ActiveImages.Add(item);
@@ -151,14 +144,18 @@ namespace XericUI.XTable.Rendering.Elements
PooledText item;
if (m_TextPool.Count > 0)
{
item = m_TextPool.Pop();
item.Root.SetActive(true);
item.Root.transform.SetParent(m_Parent, false);
item = m_TextPool.Dequeue();
ResetTextDefaults(item);
}
else if (m_TextCreatedCount < MAX_POOL_ITEMS)
{
item = CreateText(name);
m_TextCreatedCount++;
}
else
{
item = CreateText(name);
Debug.LogWarning($"[XericUIActionTable] Text 池已达上限 {MAX_POOL_ITEMS},无法分配新对象");
return null;
}
BindStyle(item.Style, styleNamespace);
m_ActiveTexts.Add(item);
@@ -214,18 +211,13 @@ namespace XericUI.XTable.Rendering.Elements
private void ReturnImage(PooledImage item)
{
item.Style?.Unbind();
item.Root.SetActive(false);
item.Root.transform.SetParent(m_PoolRoot, false);
m_ImagePool.Push(item);
m_ImagePool.Enqueue(item);
}
private void ReturnText(PooledText item)
{
item.Style?.Unbind();
item.Text.text = "";
item.Root.SetActive(false);
item.Root.transform.SetParent(m_PoolRoot, false);
m_TextPool.Push(item);
m_TextPool.Enqueue(item);
}
#endregion
@@ -251,7 +243,7 @@ namespace XericUI.XTable.Rendering.Elements
item.Rect.pivot = PIVOT_TOPLEFT;
item.Rect.anchoredPosition = Vector2.zero;
item.Rect.sizeDelta = Vector2.zero;
item.Text.text = "";
item.Text.SetText(string.Empty);
item.Text.color = Color.black;
item.Text.fontSize = 14;
item.Text.alignment = TextAlignmentOptions.Midline;
@@ -1,13 +1,12 @@
using System.Collections.Generic;
using UnityEngine;
using UnityEngine.Pool;
namespace XericUI.XTable.Rendering.Elements
{
/// <summary>
/// 预制体对象池——按原型分组管理,每个预制体对应一个独立的对象池。
/// 使用 Stack + HashSet 模式与 <see cref="CellAssembler"/> 保持一致。
/// 使用 Queue + HashSet 模式,FIFO 保证对象按回收顺序均匀复用。
/// </summary>
public class PrefabPrototypePool
{
@@ -16,8 +15,8 @@ namespace XericUI.XTable.Rendering.Elements
private class PrefabPool
{
public GameObject Prototype;
/// <summary>休眠实例栈(池满时先归还的优先复用)</summary>
public Stack<GameObject> Available = new Stack<GameObject>();
/// <summary>休眠实例队列(FIFO,先归还的优先复用)</summary>
public Queue<GameObject> Available = new Queue<GameObject>();
/// <summary>活跃实例集合(O(1) 查找 + 防止重复归还)</summary>
public HashSet<GameObject> Active = new HashSet<GameObject>();
}
@@ -28,10 +27,10 @@ namespace XericUI.XTable.Rendering.Elements
private Dictionary<GameObject, PrefabPool> m_Pools = new Dictionary<GameObject, PrefabPool>();
private Transform m_Parent;
private Transform m_PoolRoot;
/// <summary>池根节点(所有回收对象均挂在此节点下,节点本身处于 inactive)</summary>
public Transform PoolRoot => m_PoolRoot;
/// <summary>渲染项目总数上限</summary>
private const int MAX_PREFAB_ITEMS = 256;
private int m_TotalCreated;
private const HideFlags POOL_FLAGS = HideFlags.DontSave | HideFlags.HideInHierarchy;
@@ -45,15 +44,9 @@ namespace XericUI.XTable.Rendering.Elements
public PrefabPrototypePool(Transform parent)
{
m_Parent = parent;
GameObject poolRoot = new GameObject("_PrefabPoolRoot");
poolRoot.hideFlags = POOL_FLAGS;
poolRoot.transform.SetParent(parent, false);
poolRoot.SetActive(false);
m_PoolRoot = poolRoot.transform;
}
/// <summary>彻底销毁所有池对象(活跃 + 休眠 + 池根)</summary>
/// <summary>彻底销毁所有池对象(活跃 + 休眠)</summary>
public void DestroyAll()
{
foreach (var kv in m_Pools)
@@ -65,15 +58,9 @@ namespace XericUI.XTable.Rendering.Elements
pool.Active.Clear();
// 销毁休眠实例
while (pool.Available.Count > 0)
KillObject(pool.Available.Pop());
KillObject(pool.Available.Dequeue());
}
m_Pools.Clear();
if (m_PoolRoot != null)
{
KillObject(m_PoolRoot.gameObject);
m_PoolRoot = null;
}
}
private static void KillObject(GameObject go)
@@ -89,7 +76,7 @@ namespace XericUI.XTable.Rendering.Elements
#region 池操作
/// <summary>从池中获取实例(池空则 Instantiate 新的)</summary>
/// <summary>从池中获取实例(FIFO 复用;池空且未达上限则 Instantiate;已达上限返回 null)</summary>
public GameObject Acquire(GameObject prefab, int row, int col)
{
if (prefab == null) return null;
@@ -99,14 +86,18 @@ namespace XericUI.XTable.Rendering.Elements
GameObject instance;
if (pool.Available.Count > 0)
{
instance = pool.Available.Pop();
instance.SetActive(true);
instance.transform.SetParent(m_Parent, false);
instance = pool.Available.Dequeue();
}
else
else if (m_TotalCreated < MAX_PREFAB_ITEMS)
{
instance = Object.Instantiate(prefab, m_Parent);
instance.hideFlags = POOL_FLAGS;
m_TotalCreated++;
}
else
{
Debug.LogWarning($"[XericUIActionTable] Prefab 池已达上限 {MAX_PREFAB_ITEMS},无法分配新实例");
return null;
}
instance.name = $"_prefab_{row}_{col}";
@@ -122,7 +113,7 @@ namespace XericUI.XTable.Rendering.Elements
return instance;
}
/// <summary>将指定实例归还对象池(Deactivate + 移入 PoolRoot)</summary>
/// <summary>将指定实例归还对象池(不入池根,原地保留避免 OnEnable/OnDisable)</summary>
public void Release(GameObject instance)
{
if (instance == null) return;
@@ -132,9 +123,7 @@ namespace XericUI.XTable.Rendering.Elements
{
if (kv.Value.Active.Remove(instance))
{
instance.SetActive(false);
instance.transform.SetParent(m_PoolRoot, false);
kv.Value.Available.Push(instance);
kv.Value.Available.Enqueue(instance);
return;
}
}
@@ -143,7 +132,7 @@ namespace XericUI.XTable.Rendering.Elements
KillObject(instance);
}
/// <summary>将所有活跃实例回收至池</summary>
/// <summary>将所有活跃实例回收至池(原地保留,仅从活跃集移除并入队)</summary>
public void ReturnAll()
{
foreach (var kv in m_Pools)
@@ -151,9 +140,7 @@ namespace XericUI.XTable.Rendering.Elements
var pool = kv.Value;
foreach (var go in pool.Active)
{
go.SetActive(false);
go.transform.SetParent(m_PoolRoot, false);
pool.Available.Push(go);
pool.Available.Enqueue(go);
}
pool.Active.Clear();
}