using System.Diagnostics; using System; using System.Collections.Generic; using UnityEngine; namespace BF { public class BFScrollRectGrid : BFScrollRectBase { public BFCell bfCell; public float spacing; public float padding; public float topRecoveryOffset; public float downRecoveryOffset; public int preLineCount = 4; public BFUIDirection direction; Stack cellPool = new Stack(); List activeCells = new List(); float lineSize; float cellSize; int instantiateCount = 0; int totalCount = 0; int topFillCount; int bottomFillCount; void Awake() { lineSize = direction == BFUIDirection.Vertical ? bfCell.Height + spacing : bfCell.Width + spacing; cellSize = direction == BFUIDirection.Vertical ? bfCell.Width + padding : bfCell.Height + padding; if (bfCell.gameObject.activeSelf) { bfCell.gameObject.SetActive(false); } } int GetTotalLines(int totalCount) { if (totalCount % preLineCount == 0) { return totalCount / preLineCount; } return totalCount / preLineCount + 1; } public override void RefillCells(int totalCount, int targetIndex) { bottomFillCount = 0; topFillCount = 0; this.totalCount = totalCount; if (activeCells.Count != 0) { while (activeCells.Count != 0) { Pool(activeCells[0]); } UnityScrollRect.StopMovement(); } ContentTrans.anchoredPosition = Vector2.zero; if (direction == BFUIDirection.Vertical) { ContentTrans.sizeDelta = new Vector2(ContentTrans.sizeDelta.x, lineSize * GetTotalLines(totalCount) - spacing); } else { ContentTrans.sizeDelta = new Vector2(lineSize * GetTotalLines(totalCount) - spacing, ContentTrans.sizeDelta.y); } TryFullFill(); if (targetIndex > 0) { MoveToIndex(targetIndex); } } public override void SetTotalCount(int totalCount) { this.totalCount = totalCount; if (totalCount < activeCells.Count) { for (int i = activeCells.Count - 1; i > totalCount - 1; i--) { Pool(activeCells[i]); if (bottomFillCount > 0) { bottomFillCount--; } else { if (totalCount <= preLineCount) { topFillCount--; } else { bottomFillCount = preLineCount - 1; } } } } if (direction == BFUIDirection.Vertical) { ContentTrans.sizeDelta = new Vector2(ContentTrans.sizeDelta.x, lineSize * GetTotalLines(totalCount) - spacing); } else { ContentTrans.sizeDelta = new Vector2(lineSize * totalCount - spacing, ContentTrans.sizeDelta.y); } } public override void SetSelected(int index) { selectedIndex = index; int count = activeCells.Count; for (int i = 0; i < count; i++) { var cell = activeCells[i]; Selected?.Invoke(cell.dataIndex == index, cell.objIndex); } } public override void RefreshAll() { int count = activeCells.Count; for (int i = 0; i < count; i++) { var cell = activeCells[i]; RefreshAction?.Invoke(cell.dataIndex, cell.objIndex); } } public override void ClearCells() { while (activeCells.Count != 0) { Pool(activeCells[0]); } ContentTrans.anchoredPosition = Vector2.zero; bottomFillCount = 0; topFillCount = 0; } public override void AnchoredPositionChange() { } protected override void TryFullFill() { int i = 0; while (CheckBorder() && i < totalCount) { i++; } } bool CheckBorder() { if (activeCells.Count == 0) { TryStartAdd(); return false; } var cell = activeCells[0]; var delta = direction == BFUIDirection.Vertical ? cell.AnchoredPosition.y + ContentTrans.anchoredPosition.y : cell.AnchoredPosition.x + ContentTrans.anchoredPosition.x; if (delta < topRecoveryOffset && TryAddUp()) { return true; } if (topFillCount < preLineCount && TryAddUp()) { return true; } cell = activeCells[activeCells.Count - 1]; delta = direction == BFUIDirection.Vertical ? cell.AnchoredPosition.y - lineSize + ContentTrans.anchoredPosition.y : cell.AnchoredPosition.x - lineSize + ContentTrans.anchoredPosition.x; if (delta > -ViewPortTrans.sizeDelta.y - downRecoveryOffset && TryAddDown()) { return true; } if (bottomFillCount < preLineCount && TryAddDown()) { return true; } if (activeCells.Count <= preLineCount) { return false; } if (NeedCull(activeCells[0])) { CullLineUp(); return true; } if (NeedCull(activeCells[activeCells.Count - 1])) { CullLineDown(activeCells.Count - 1); return true; } return false; } void TryStartAdd() { if (totalCount == 0) { return; } var cell = GetCell(); cell.dataIndex = 1; cell.name = StringConst.GetScrollRectCellName(0); topFillCount = 1; cell.CachedRectTransform.anchoredPosition = Vector2.zero; activeCells.Add(cell); cell.gameObject.SetActive(true); RefreshAction?.Invoke(cell.dataIndex, cell.objIndex); } bool TryAddUp() { var topIndex = activeCells[0].dataIndex; if (topIndex == 1 && (topFillCount == preLineCount || activeCells.Count == totalCount)) { return false; } var cell = GetCell(); if (topFillCount == preLineCount) { topFillCount = 0; cell.dataIndex = topIndex - preLineCount; } else if (topFillCount == 0) { cell.dataIndex = topIndex - preLineCount; } else { cell.dataIndex = topIndex + topFillCount; } var x = direction == BFUIDirection.Vertical ? topFillCount * cellSize : -GetLineIndex(cell.dataIndex) * lineSize; var y = direction == BFUIDirection.Vertical ? -GetLineIndex(cell.dataIndex) * lineSize : topFillCount * cellSize; cell.CachedRectTransform.anchoredPosition = new Vector2(x, y); cell.gameObject.SetActive(true); cell.name = StringConst.GetScrollRectCellName(cell.dataIndex - 1); if (topFillCount == activeCells.Count) { activeCells.Add(cell); } else { activeCells.Insert(topFillCount, cell); } topFillCount++; RefreshAction?.Invoke(cell.dataIndex, cell.objIndex); return true; } bool TryAddDown() { var downIndex = activeCells[activeCells.Count - 1].dataIndex; if (downIndex == totalCount) { return false; } if (bottomFillCount == preLineCount) { bottomFillCount = 0; } var cell = GetCell(); cell.dataIndex = downIndex + 1; cell.name = StringConst.GetScrollRectCellName(cell.dataIndex - 1); var x = direction == BFUIDirection.Vertical ? bottomFillCount * cellSize : -GetLineIndex(cell.dataIndex) * lineSize; var y = direction == BFUIDirection.Vertical ? -GetLineIndex(cell.dataIndex) * lineSize : bottomFillCount * cellSize; cell.CachedRectTransform.anchoredPosition = new Vector2(x, y); activeCells.Add(cell); cell.gameObject.SetActive(true); bottomFillCount++; RefreshAction?.Invoke(cell.dataIndex, cell.objIndex); return true; } int GetLineIndex(int index) { if (index % preLineCount == 0) { return index / preLineCount - 1; } return index / preLineCount; } BFCell GetCell() { if (cellPool.Count > 0) { return cellPool.Pop(); } else { var t = Instantiate(bfCell.CachedRectTransform); t.SetParent(ContentTrans); t.localScale = Vector3.one; t.anchoredPosition3D = Vector3.zero; instantiateCount++; var newCell = t.GetComponent(); newCell.objIndex = instantiateCount; OnInstantiateCell?.Invoke(newCell.gameObject); return newCell; } } void Pool(BFCell cell) { activeCells.Remove(cell); cell.gameObject.SetActive(false); cellPool.Push(cell); } bool NeedCull(BFCell cell) { if (activeCells.IndexOf(cell) == 0) { var d1 = direction == BFUIDirection.Vertical ? cell.AnchoredPosition.y - lineSize + ContentTrans.anchoredPosition.y : cell.AnchoredPosition.x - lineSize + ContentTrans.anchoredPosition.x; if (d1 > 0.01 + topRecoveryOffset) { return true; } } var d2 = direction == BFUIDirection.Vertical ? cell.AnchoredPosition.y + ContentTrans.anchoredPosition.y : cell.AnchoredPosition.x + ContentTrans.anchoredPosition.x; var d3 = direction == BFUIDirection.Vertical ? -ViewPortTrans.sizeDelta.y : -ViewPortTrans.sizeDelta.x; if (d2 < d3 - (0.01 + downRecoveryOffset)) { return true; } if (cell.dataIndex > totalCount) { return true; } return false; } void CullLineUp() { for (int i = topFillCount - 1; i >= 0; i--) { Pool(activeCells[i]); topFillCount--; if (topFillCount == 0) { topFillCount = preLineCount; return; } } } void CullLineDown(int lastIndex) { for (int i = lastIndex; i >= 0; i--) { Pool(activeCells[i]); bottomFillCount--; if (bottomFillCount == 0) { bottomFillCount = preLineCount; return; } } } public override void MoveToIndex(int index) { if (index > totalCount) { return; } int line = (index - 1) / preLineCount; line = line >= 0 ? line : 0; if (direction == BFUIDirection.Vertical) { float moveHeight = Mathf.Min(lineSize * line, ContentTrans.sizeDelta.y - ViewPortTrans.sizeDelta.y); moveHeight = Mathf.Max(0, moveHeight); ContentTrans.anchoredPosition = new Vector2(ContentTrans.anchoredPosition.x, moveHeight); } else { float moveWidth = Mathf.Min(lineSize * line, ContentTrans.sizeDelta.x - ViewPortTrans.sizeDelta.x); moveWidth = Mathf.Max(0, moveWidth); ContentTrans.anchoredPosition = new Vector2(-moveWidth, ContentTrans.anchoredPosition.y); } } } }