2018 年 4 月 3 日
Unity – 无限滚动排行榜
【需求】
大部分游戏中都需要一个排行榜,在Unity中用ScrollView 很容易实现这一功能。
一般排行榜都会多达上百条信息,如果一次性创建这么多的Item,会非常的损耗性能(用膝盖想也不应该这么做啊)。
所以我们只需要创建能看见的几个Item,让他们循环滚动,更新信息即可。
【实现】
1.初始化
计算可见的行数和Item数量;实例化并设置好初始位置;更新Content大小。
public void Init() { if (!m_finishInit) { m_visibleCellsRowCount = (int)(m_scrollRect.viewport.rect.height / (m_cellSize.y + m_cellOffSet.y)) + 1; m_visibleCellsTotalCount = (m_visibleCellsRowCount + 1) * m_columnsNumber; m_Cells = new LinkedList(); for (int i = 0; i < m_visibleCellsTotalCount; i++) { GameObject go = Instantiate(m_rankCellprefab.gameObject, m_scrollRect.content.transform) as GameObject; go.SetActive(true); m_Cells.AddLast(go); SetCellPosition(go, i); } } UpdateContentSize(); m_finishInit = true; }
1.1设置Item位置
按照序号设置Item的位置,并更新Data内容。
void SetCellPosition(GameObject go, int index) { int rowMod = index % m_columnsNumber; go.GetComponent().anchoredPosition = new Vector2(m_cellSize.x * rowMod + m_cellOffSet.x, -(index / m_columnsNumber) * (m_cellSize.y + m_cellOffSet.y)); go.GetComponent().UpdateCellData(index + 1, allData.IndexOf(index).ToString(), allData.Count - index); }
1.2更新Content大小
void UpdateContentSize() { int cellOneWayCount = (int)allData.Count / m_columnsNumber; m_scrollRect.content.sizeDelta = new Vector2(m_scrollRect.content.sizeDelta.x, cellOneWayCount * m_cellSize.y + (cellOneWayCount - 1) * m_cellOffSet.y); }
2.在Update中不断计算当前序号并更新Item
private void Update() { if (m_finishInit) { CalculateIndex(); UpdateCells(); } }
2.1计算当前可见的合理的最小序号
void CalculateIndex() { m_visibleFirstIndex = (int)(m_scrollRect.content.anchoredPosition.y / (m_cellSize.y + m_cellOffSet.y)); int maxRowCount = Mathf.CeilToInt(allData.Count / m_columnsNumber) - m_visibleCellsRowCount; m_visibleFirstIndex = Mathf.Clamp(m_visibleFirstIndex, 0, m_visibleFirstIndex - 1); }
2.2根据序号更新Item数量
void UpdateCells() { if (m_visiblePreFirstIndex != m_visibleFirstIndex) { bool scrollingPositive = m_visiblePreFirstIndex < m_visibleFirstIndex; int indexDelta = Mathf.Abs(m_visiblePreFirstIndex - m_visibleFirstIndex); int deltaSign = scrollingPositive ? +1 : -1; for (int i = 1; i <= indexDelta; i++) UpdateContent(m_visiblePreFirstIndex + i * deltaSign, scrollingPositive); m_visiblePreFirstIndex = m_visibleFirstIndex; } }
2.3滚动更新Item
void UpdateContent(int index, bool scrollingPositive) { if (scrollingPositive) { LinkedListNode cell = m_Cells.First; cell.Value.gameObject.SetActive(false); m_Cells.RemoveFirst(); SetCellPosition(cell.Value, index + m_visibleCellsRowCount); m_Cells.AddLast(cell); if (index + m_visibleCellsRowCount <= allData.Count - 1) cell.Value.gameObject.SetActive(true); } else { LinkedListNode cell = m_Cells.Last; cell.Value.gameObject.SetActive(false); m_Cells.RemoveLast(); SetCellPosition(cell.Value, index); m_Cells.AddFirst(cell); if (index >= 0) cell.Value.gameObject.SetActive(true); } }
【注意】
RankView中Content和CellPrefab的锚点和中心点的设置。
【资源】