Rewriting everything from scratch, developed generic ObjectPool system

This commit is contained in:
Sinai
2021-04-26 19:56:21 +10:00
parent 5a0c2390ce
commit 9f8d53f55a
77 changed files with 4399 additions and 4316 deletions

View File

@ -1,83 +1,83 @@
using System;
using System.Collections.Generic;
using System.Linq;
using System.Text;
using UnityEngine;
using UnityEngine.UI;
//using System;
//using System.Collections.Generic;
//using System.Linq;
//using System.Text;
//using UnityEngine;
//using UnityEngine.UI;
namespace UnityExplorer.UI.Widgets
{
public class CellViewHolder : ICell
{
public CellViewHolder(GameObject uiRoot)
{
this.UIRoot = uiRoot;
this.Rect = uiRoot.GetComponent<RectTransform>();
m_enabled = uiRoot.activeSelf;
}
//namespace UnityExplorer.UI.Widgets
//{
// public class CellViewHolder : ICell
// {
// public CellViewHolder(GameObject uiRoot)
// {
// this.UIRoot = uiRoot;
// this.Rect = uiRoot.GetComponent<RectTransform>();
// m_enabled = uiRoot.activeSelf;
// }
public bool Enabled => m_enabled;
private bool m_enabled;
// public bool Enabled => m_enabled;
// private bool m_enabled;
public GameObject UIRoot { get; }
public RectTransform Rect { get; }
// public GameObject UIRoot { get; }
// public RectTransform Rect { get; }
private GameObject m_content;
// private GameObject m_content;
public GameObject SetContent(GameObject newContent)
{
var ret = m_content;
// public GameObject SetContent(GameObject newContent)
// {
// var ret = m_content;
if (ret && newContent && ret.ReferenceEqual(newContent))
return null;
// if (ret && newContent && ret.ReferenceEqual(newContent))
// return null;
newContent.transform.SetParent(this.UIRoot.transform, false);
(this as ICell).Enable();
// newContent.transform.SetParent(this.UIRoot.transform, false);
// (this as ICell).Enable();
m_content = newContent;
return ret;
}
// m_content = newContent;
// return ret;
// }
public GameObject DisableContent()
{
var ret = m_content;
(this as ICell).Disable();
return ret;
}
// public GameObject DisableContent()
// {
// var ret = m_content;
// (this as ICell).Disable();
// return ret;
// }
void ICell.Enable()
{
m_enabled = true;
UIRoot.SetActive(true);
}
// void ICell.Enable()
// {
// m_enabled = true;
// UIRoot.SetActive(true);
// }
void ICell.Disable()
{
m_enabled = false;
UIRoot.SetActive(false);
}
// void ICell.Disable()
// {
// m_enabled = false;
// UIRoot.SetActive(false);
// }
public static RectTransform CreatePrototypeCell(GameObject parent)
{
// using an image on the cell view holder is fine, we only need to make about 20-50 of these per pool.
var prototype = UIFactory.CreateVerticalGroup(parent, "PrototypeCell", true, true, true, true, 0, new Vector4(0, 0, 0, 0),
new Color(0.11f, 0.11f, 0.11f), TextAnchor.MiddleCenter);
// public static RectTransform CreatePrototypeCell(GameObject parent)
// {
// // using an image on the cell view holder is fine, we only need to make about 20-50 of these per pool.
// var prototype = UIFactory.CreateVerticalGroup(parent, "PrototypeCell", true, true, true, true, 0, new Vector4(0, 0, 0, 0),
// new Color(0.11f, 0.11f, 0.11f), TextAnchor.MiddleCenter);
var rect = prototype.GetComponent<RectTransform>();
rect.anchorMin = new Vector2(0, 1);
rect.anchorMax = new Vector2(0, 1);
rect.pivot = new Vector2(0.5f, 1);
rect.sizeDelta = new Vector2(100, 30);
// var rect = prototype.GetComponent<RectTransform>();
// rect.anchorMin = new Vector2(0, 1);
// rect.anchorMax = new Vector2(0, 1);
// rect.pivot = new Vector2(0.5f, 1);
// rect.sizeDelta = new Vector2(100, 30);
prototype.AddComponent<ContentSizeFitter>().verticalFit = ContentSizeFitter.FitMode.PreferredSize;
// prototype.AddComponent<ContentSizeFitter>().verticalFit = ContentSizeFitter.FitMode.PreferredSize;
var sepObj = UIFactory.CreateUIObject("separator", prototype);
sepObj.AddComponent<Image>().color = Color.black;
UIFactory.SetLayoutElement(sepObj, minHeight: 1, preferredHeight: 1, flexibleHeight: 0);
// var sepObj = UIFactory.CreateUIObject("separator", prototype);
// sepObj.AddComponent<Image>().color = Color.black;
// UIFactory.SetLayoutElement(sepObj, minHeight: 1, preferredHeight: 1, flexibleHeight: 0);
prototype.SetActive(false);
// prototype.SetActive(false);
return rect;
}
}
}
// return rect;
// }
// }
//}

View File

@ -15,11 +15,11 @@ namespace UnityExplorer.UI.Widgets
public static implicit operator float(DataViewInfo it) => it.height;
}
public class DataHeightCache
public class DataHeightCache<T> where T : ICell
{
private ScrollPool ScrollPool { get; }
private ScrollPool<T> ScrollPool { get; }
public DataHeightCache(ScrollPool scrollPool)
public DataHeightCache(ScrollPool<T> scrollPool)
{
ScrollPool = scrollPool;
}
@ -38,7 +38,7 @@ namespace UnityExplorer.UI.Widgets
public float TotalHeight => totalHeight;
private float totalHeight;
public float DefaultHeight => m_defaultHeight ?? (float)(m_defaultHeight = ScrollPool.PrototypeCell.rect.height);
public float DefaultHeight => m_defaultHeight ?? (float)(m_defaultHeight = ScrollPool.PrototypeHeight);
private float? m_defaultHeight;
/// <summary>
@ -100,8 +100,16 @@ namespace UnityExplorer.UI.Widgets
return;
var val = heightCache[heightCache.Count - 1];
totalHeight -= val;
totalHeight -= val;
heightCache.RemoveAt(heightCache.Count - 1);
int idx = heightCache.Count;
if (idx > 0)
{
while (rangeCache[rangeCache.Count - 1] == idx)
rangeCache.RemoveAt(rangeCache.Count - 1);
}
}
/// <summary>Get the data index at the specific position of the total height cache.</summary>

View File

@ -3,10 +3,11 @@ using System.Collections.Generic;
using System.Linq;
using System.Text;
using UnityEngine;
using UnityExplorer.UI.ObjectPool;
namespace UnityExplorer.UI.Widgets
{
public interface ICell
public interface ICell : IPooledObject
{
bool Enabled { get; }

View File

@ -6,15 +6,15 @@ using UnityEngine;
namespace UnityExplorer.UI.Widgets
{
public interface IPoolDataSource
public interface IPoolDataSource<T> where T : ICell
{
int ItemCount { get; }
void SetCell(ICell cell, int index);
void DisableCell(ICell cell, int index);
int GetRealIndexOfTempIndex(int tempIndex);
ICell CreateCell(RectTransform cellTransform);
void OnCellBorrowed(T cell);
void OnCellReturned(T cell);
void SetCell(T cell, int index);
void DisableCell(T cell, int index);
}
}

View File

@ -7,6 +7,7 @@ using UnityEngine;
using UnityEngine.UI;
using UnityExplorer.Core.Input;
using UnityExplorer.UI.Models;
using UnityExplorer.UI.ObjectPool;
namespace UnityExplorer.UI.Widgets
{
@ -15,20 +16,31 @@ namespace UnityExplorer.UI.Widgets
public int cellIndex, dataIndex;
}
//public abstract class ScrollPool : UIBehaviourModel
//{
// public abstract IPoolDataSource DataSource { get; set; }
// public abstract RectTransform PrototypeCell { get; }
//
// public abstract void Initialize(IPoolDataSource dataSource);
//
//}
/// <summary>
/// An object-pooled ScrollRect, attempts to support content of any size and provide a scrollbar for it.
/// </summary>
public class ScrollPool : UIBehaviourModel
public class ScrollPool<T> : UIBehaviourModel where T : ICell
{
public ScrollPool(ScrollRect scrollRect)
{
this.ScrollRect = scrollRect;
}
public IPoolDataSource DataSource;
public RectTransform PrototypeCell;
public IPoolDataSource<T> DataSource { get; set; }
private float PrototypeHeight => PrototypeCell.rect.height;
public float PrototypeHeight => _protoHeight ?? (float)(_protoHeight = Pool<T>.Instance.DefaultHeight);
private float? _protoHeight;
//private float PrototypeHeight => DefaultHeight.rect.height;
public int ExtraPoolCells => 6;
public float RecycleThreshold => PrototypeHeight * ExtraPoolCells;
@ -64,9 +76,9 @@ namespace UnityExplorer.UI.Widgets
private int bottomDataIndex;
private int TopDataIndex => Math.Max(0, bottomDataIndex - CellPool.Count + 1);
private readonly List<ICell> CellPool = new List<ICell>();
private readonly List<T> CellPool = new List<T>();
internal DataHeightCache HeightCache;
internal DataHeightCache<T> HeightCache;
private float TotalDataHeight => HeightCache.TotalHeight + contentLayout.padding.top + contentLayout.padding.bottom;
@ -99,8 +111,16 @@ namespace UnityExplorer.UI.Widgets
private float prevContentHeight;
public void SetUninitialized()
{
m_initialized = false;
}
public override void Update()
{
if (!m_initialized || !ScrollRect || DataSource == null)
return;
if (writingLocked && timeofLastWriteLock < Time.time)
writingLocked = false;
@ -125,6 +145,8 @@ namespace UnityExplorer.UI.Widgets
writingLocked = false;
Content.anchoredPosition = Vector2.zero;
UpdateSliderHandle(true);
m_initialized = true;
}
public void RefreshAndJumpToTop()
@ -137,22 +159,10 @@ namespace UnityExplorer.UI.Widgets
public void RecreateHeightCache()
{
//if (tempHeightCache == null)
// tempHeightCache = HeightCache;
HeightCache = new DataHeightCache(this);
HeightCache = new DataHeightCache<T>(this);
CheckDataSourceCountChange(out _);
}
//public void DisableTempCache()
//{
// if (tempHeightCache == null)
// return;
// HeightCache = tempHeightCache;
// tempHeightCache = null;
//}
public void RefreshCells(bool reloadData)
{
RefreshCells(reloadData, true);
@ -160,15 +170,14 @@ namespace UnityExplorer.UI.Widgets
// Initialize
public void Initialize(IPoolDataSource dataSource, RectTransform prototypeCell)
private bool m_initialized;
public void Initialize(IPoolDataSource<T> dataSource)
{
if (!prototypeCell)
throw new Exception("No prototype cell set, cannot initialize");
// Ensure the pool for the cell type is initialized.
Pool<T>.GetPool();
this.PrototypeCell = prototypeCell;
PrototypeCell.transform.SetParent(Viewport, false);
HeightCache = new DataHeightCache(this);
HeightCache = new DataHeightCache<T>(this);
DataSource = dataSource;
this.contentLayout = ScrollRect.content.GetComponent<VerticalLayoutGroup>();
@ -200,6 +209,8 @@ namespace UnityExplorer.UI.Widgets
// add onValueChanged listener after setup
ScrollRect.onValueChanged.AddListener(OnValueChangedListener);
m_initialized = true;
}
private void SetScrollBounds()
@ -209,14 +220,22 @@ namespace UnityExplorer.UI.Widgets
// Cell pool
private void CreateCellPool(bool andResetDataIndex = true)
public void ReturnCells()
{
if (CellPool.Any())
{
foreach (var cell in CellPool)
GameObject.Destroy(cell.Rect.gameObject);
{
DataSource.OnCellReturned(cell);
Pool<T>.Return(cell);
}
CellPool.Clear();
}
}
private void CreateCellPool(bool andResetDataIndex = true)
{
ReturnCells();
float currentPoolCoverage = 0f;
float requiredCoverage = ScrollRect.viewport.rect.height + RecycleThreshold;
@ -230,11 +249,17 @@ namespace UnityExplorer.UI.Widgets
{
bottomPoolIndex++;
//Instantiate and add to Pool
RectTransform rect = GameObject.Instantiate(PrototypeCell.gameObject).GetComponent<RectTransform>();
rect.gameObject.SetActive(true);
rect.name = $"Cell_{CellPool.Count}";
var cell = DataSource.CreateCell(rect);
////Instantiate and add to Pool
//RectTransform rect = GameObject.Instantiate(PrototypeCell.gameObject).GetComponent<RectTransform>();
//rect.gameObject.SetActive(true);
//rect.name = $"Cell_{CellPool.Count}";
//var cell = DataSource.CreateCell(rect);
//CellPool.Add(cell);
//rect.SetParent(ScrollRect.content, false);
var cell = Pool<T>.Borrow();
DataSource.OnCellBorrowed(cell);
var rect = cell.Rect;
CellPool.Add(cell);
rect.SetParent(ScrollRect.content, false);
@ -400,7 +425,7 @@ namespace UnityExplorer.UI.Widgets
ScrollRect.UpdatePrevData();
}
private void SetCell(ICell cachedCell, int dataIndex)
private void SetCell(T cachedCell, int dataIndex)
{
cachedCell.Enable();
DataSource.SetCell(cachedCell, dataIndex);