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的锚点和中心点的设置。

RankView的结构
Content的设置
RankCellPrefab的设置

【资源】

RankList

Add a Comment

您的电子邮箱地址不会被公开。 必填项已用*标注