Unity scrollRect 无限滚动实现

首先是竖直下拉,效果如下:

效果图

其中的minAmount是显示最少需要的个数,个数是整个屏幕最多能放下的个数+1 比如效果中,最多能放下三组,所以设置的是4(这里因为显示效果一行三个为1组 使用1哥prefab)

然后在 scrollRect的 connect组建中 需要使用如下组件

image.png

当前是竖向下拉 所以在GridGroupConnect中需要把 constraint 按照如下设置 在content size fitter中设置为竖向为最佳尺寸

然后是横向翻页的效果

5555.gif

image.png

connect的设置如下

需要注意的是 当时为了方便计算 在使用时需要将

image.png

prefab 进行如下设置

image.png

prefab的所有父物体(到scrollrect为止 包括scrollrect)的pivot 也要试着为0 1 这样操作是为了计算式统一通过左上角进行计算 如果有其他需求可修改计算过程中的具体代码

代码如下:

using UnityEngine;
using System.Collections.Generic;
using UnityEngine.UI;
using System.Collections;
//lsh 
public class InfinityPackgeTool : MonoBehaviour
{

    public enum Axies
    {
        horizontal = 0,
        vertical = 1
    }

    public int minAmount;
    public ScrollRect scrollRect;
    public Transform itemPrefab;

    public delegate void UpdateChildrenCallBack(int index, Transform trans);
    public UpdateChildrenCallBack updateChildrenDelegate = null;

    private GameObject scrollContent;
    private GridLayoutGroup gridLayoutGroup;
    private ContentSizeFitter contentSizeFitter;

    private List<RectTransform> childTransList;
    private Dictionary<Transform, int> childStartIndexDic;
    private Dictionary<int, Vector2> childStartAnchordPosition;

    private Vector2 connectStartSize;
    private Vector2 connectStartAnchordPos;
    private int childItemNum;
    private int realEndIndex = -1;
    private int amount;
    private RectTransform rectTransform;
    private RectTransform scrollRectTrans;
    private Vector2 lastPosition;
    private Vector2 gridLayoutStartSize;
    private Axies axies;

    private bool isNeedAutoChangeConnectScale;
    private bool isRegistEvent = false;


    /// <summary>
    /// 
    /// </summary>
    /// <param name="num">item的数量</param>
    /// <param name="isNeedAutoChangeScale">是否需要自动修改大小 若为true则直接设置为最大大小并不自动更改 否则根据当前位置自动更改connect大小</param>
    public void SetAmount(int num,bool isNeedAutoChangeScale)
    {
        amount = num;
        isNeedAutoChangeConnectScale = isNeedAutoChangeScale;
        StartCoroutine(InitManager());
    }

    IEnumerator InitManager()
    {
        if (!isRegistEvent)
        {
            for (int i = 0; i < minAmount; i++)
            {
                GameObject g = Instantiate(itemPrefab).gameObject;
                g.transform.SetParent(itemPrefab.transform.parent);
                g.transform.localScale = Vector3.one;
                g.SetActive(true);
            }
            Destroy(itemPrefab.gameObject);
        }
        yield return 0;//wait for a flame
        //first open

        if (!isRegistEvent)
        {
            isRegistEvent = true;

            scrollContent = scrollRect.content.gameObject;
            rectTransform = scrollContent.GetComponent<RectTransform>();
            gridLayoutGroup = scrollContent.GetComponent<GridLayoutGroup>();
            contentSizeFitter = scrollContent.GetComponent<ContentSizeFitter>();
            connectStartSize = scrollContent.GetComponent<RectTransform>().sizeDelta;
            lastPosition = rectTransform.anchoredPosition;
            gridLayoutStartSize = rectTransform.sizeDelta;
            connectStartAnchordPos = rectTransform.anchoredPosition;

            scrollRectTrans = scrollRect.GetComponent<RectTransform>();

            gridLayoutGroup.enabled = false;
            contentSizeFitter.enabled = false;
            axies = Axies.vertical;
            if (gridLayoutGroup.constraint == GridLayoutGroup.Constraint.FixedRowCount)
                axies = Axies.horizontal;

            childItemNum = scrollContent.transform.childCount;

            childTransList = new List<RectTransform>();
            childStartIndexDic = new Dictionary<Transform, int>();
            childStartAnchordPosition = new Dictionary<int, Vector2>();
            for (int index = 0; index < childItemNum; index++)
            {
                Transform t = scrollContent.transform.GetChild(index);
                childTransList.Add(t.GetComponent<RectTransform>());
                childStartIndexDic.Add(t, index);
                childStartAnchordPosition.Add(index, t.GetComponent<RectTransform>().anchoredPosition);
            }
            if(!isNeedAutoChangeConnectScale)
            {
                int pageNum = amount / gridLayoutGroup.constraintCount + ((amount % gridLayoutGroup.constraintCount == 0) ? 0 : 1);
                if (axies == Axies.horizontal)
                {
                    scrollContent.GetComponent<RectTransform>().sizeDelta = new Vector2(gridLayoutGroup.cellSize.x * pageNum + gridLayoutGroup.spacing.x * (pageNum - 1) + gridLayoutGroup.padding.left+gridLayoutGroup.padding.right, scrollContent.GetComponent<RectTransform>().sizeDelta.y);
                    //scrollRect.horizontalNormalizedPosition = 0;
                }
                else
                {
                    scrollContent.GetComponent<RectTransform>().sizeDelta = new Vector2(scrollContent.GetComponent<RectTransform>().sizeDelta.x, gridLayoutGroup.cellSize.y * pageNum + gridLayoutGroup.spacing.y * (pageNum - 1) + gridLayoutGroup.padding.top + gridLayoutGroup.padding.bottom);
                    //scrollRect.verticalNormalizedPosition = 1;
                }
            }
            scrollRect.onValueChanged.AddListener(UpdateChildren);
        }
        else
        {
            //reset all data
            if (isNeedAutoChangeConnectScale)
                scrollContent.GetComponent<RectTransform>().sizeDelta = connectStartSize;
            else
            {
                int pageNum = amount / gridLayoutGroup.constraintCount + ((amount % gridLayoutGroup.constraintCount == 0) ? 0 : 1);
                if (axies == Axies.horizontal)
                {
                    scrollContent.GetComponent<RectTransform>().sizeDelta = new Vector2(gridLayoutGroup.cellSize.x * pageNum + gridLayoutGroup.spacing.x * (pageNum - 1) + gridLayoutGroup.padding.left + gridLayoutGroup.padding.right, scrollContent.GetComponent<RectTransform>().sizeDelta.y);
                }
                else
                {
                    scrollContent.GetComponent<RectTransform>().sizeDelta = new Vector2(scrollContent.GetComponent<RectTransform>().sizeDelta.x, gridLayoutGroup.cellSize.y * pageNum + gridLayoutGroup.spacing.y * (pageNum - 1) + gridLayoutGroup.padding.top + gridLayoutGroup.padding.bottom);
                }
            }
            scrollContent.GetComponent<RectTransform>().anchoredPosition = connectStartAnchordPos;
            childTransList.Clear();
            foreach (var d in childStartIndexDic)
            {
                d.Key.SetSiblingIndex(d.Value);
                d.Key.GetComponent<RectTransform>().anchoredPosition = childStartAnchordPosition[d.Value];
                childTransList.Add(d.Key.GetComponent<RectTransform>());
            }
        }
        //init data
        int num = amount;
        if (childItemNum < amount)
            num = childItemNum;
        for (int index = 0; index < childItemNum; index++)
        {
            if (index < amount)
            {
                updateChildrenDelegate(index, childTransList[index]);
                childTransList[index].gameObject.SetActive(true);
            }
            else
            {
                childTransList[index].gameObject.SetActive(false);
            }

        }
        realEndIndex = childItemNum-1;
    }

    public void AcheivementRemovePhotoUpdate(int pageNum)
    {
        //移动到目标页数
        for (int index = 0; index <= pageNum; index++)
        {
            lastPosition.x = 0;
            UpdateChildren(Vector2.zero);
        }
    }

    private void UpdateChildren(Vector2 v)
    {
        if (scrollContent.transform.childCount < minAmount)
            return;

        Vector2 currentPos = rectTransform.anchoredPosition;
        //is horizontal slide
        if (axies == Axies.horizontal)
        {
            float offset = currentPos.x - lastPosition.x;
            //connct move direct : right ,right column move to left
            if (offset > 0)
            {
                //is slide to connect buttom,return
                if (realEndIndex <= childItemNum - 1)
                {
                    lastPosition = currentPos;
                    return;
                }
                
                float scrollRectRight = scrollRect.transform.TransformPoint(new Vector3(scrollRectTrans.rect.width + gridLayoutGroup.spacing.x, 0, 0)).x;

                //is last item left line x pos more than scrollLeft x pos plus gridGroupLayout spacing x pos
                Vector3 childLastColunmLeft = new Vector3(childTransList[childItemNum - 1].anchoredPosition.x + gridLayoutGroup.spacing.x - gridLayoutGroup.cellSize.x * childTransList[childItemNum - 1].pivot.x, 0, 0);//need to add spacing
                float childLeft =scrollContent.transform.TransformPoint(childLastColunmLeft).x;

                if (childLeft >= scrollRectRight)
                {
                    //right column move to left
                    for (int index = 0; index <gridLayoutGroup.constraintCount ; index++)
                    {
                        childTransList[childItemNum - 1 - index].anchoredPosition = new Vector2(childTransList[0].anchoredPosition.x - gridLayoutGroup.spacing.x - gridLayoutGroup.cellSize.x, childTransList[childItemNum - 1 - index].anchoredPosition.y);
                        childTransList[childItemNum - 1 - index].SetAsFirstSibling();
                        updateChildrenDelegate(realEndIndex - childItemNum - index, childTransList[childItemNum - 1 - index]);
                        childTransList[childItemNum - 1 - index].gameObject.SetActive(true);
                    }
                    if (isNeedAutoChangeConnectScale)//need to minus spacing
                        scrollContent.GetComponent<RectTransform>().sizeDelta -= new Vector2(gridLayoutGroup.spacing.x + gridLayoutGroup.cellSize.x, 0);

                    realEndIndex -= gridLayoutGroup.constraintCount;
                }

            }
            else//connect move direct : left,left column to right
            {
                if (realEndIndex >= amount-1)
                {
                    lastPosition = currentPos;
                    return;
                }
                float scrollRectLeft = scrollRect.transform.TransformPoint(Vector3.zero).x;

                Vector3 childStartColumnRight = new Vector3(childTransList[0].anchoredPosition.x + gridLayoutGroup.cellSize.x * (1 - childTransList[0].pivot.x), 0f, 0f);
                float childRight = scrollContent.transform.TransformPoint(childStartColumnRight).x;

                if (childRight <= scrollRectLeft)
                {
                    for (int index = 0; index < gridLayoutGroup.constraintCount; index++)
                    {
                        childTransList[index].anchoredPosition = new Vector2(childTransList[childItemNum - 1 - index].anchoredPosition.x + gridLayoutGroup.cellSize.x + gridLayoutGroup.spacing.x, childTransList[index].anchoredPosition.y);
                        childTransList[index].SetAsLastSibling();
                        realEndIndex++;
                        if (realEndIndex >= amount)
                        {
                            childTransList[index].gameObject.SetActive(false);
                        }
                        else
                        {
                            updateChildrenDelegate(realEndIndex, childTransList[index]);
                        }
                    }
                    if (isNeedAutoChangeConnectScale)//need to minus spacing
                        scrollContent.GetComponent<RectTransform>().sizeDelta += new Vector2(gridLayoutGroup.spacing.x + gridLayoutGroup.cellSize.x, 0);
                }
            }
        }
        else //same as horizontal
        {
            float offset = currentPos.y - lastPosition.y;
            if (offset > 0)//connect mode direct : up,top row move to buttom
            {
                if (realEndIndex >= amount - 1)
                {
                    lastPosition = currentPos;
                    return;
                }
                float scrollRectTop = scrollRect.transform.TransformPoint(Vector3.zero).y;
                float childTopRowButtom = scrollContent.transform.TransformPoint(new Vector3(0, childTransList[0].anchoredPosition.y - gridLayoutGroup.cellSize.y * (1-childTransList[0].pivot.y) - gridLayoutGroup.spacing.y, 0)).y;

                if (scrollRectTop < childTopRowButtom)
                {
                    for (int index = 0; index < gridLayoutGroup.constraintCount; index++)
                    {
                        childTransList[index].transform.SetAsLastSibling();
                        childTransList[index].anchoredPosition = new Vector2(childTransList[index].anchoredPosition.x, childTransList[childItemNum - 1 ].anchoredPosition.y - gridLayoutGroup.spacing.y - gridLayoutGroup.cellSize.y);
                        realEndIndex++;
                        if (realEndIndex >= amount)
                        {
                            childTransList[index].gameObject.SetActive(false);
                        }
                        else
                        {
                            updateChildrenDelegate(realEndIndex, childTransList[index]);
                        }
                    }

                    if(isNeedAutoChangeConnectScale)
                        scrollContent.GetComponent<RectTransform>().sizeDelta += new Vector2(0, gridLayoutGroup.cellSize.y + gridLayoutGroup.spacing.y);
                }
            }
            else//connect move direct down,buttom row move to top
            {
                if (realEndIndex <= childItemNum - 1)
                {
                    lastPosition = currentPos;
                    return;
                }
                float scrollRectButtom = scrollRect.transform.TransformPoint(new Vector3(0,-scrollRectTrans.rect.height,0)).y;
                float childButtomRowTop = scrollContent.transform.TransformPoint(new Vector3(0, childTransList[childItemNum - 1].anchoredPosition.y + gridLayoutGroup.cellSize.y * childTransList[childItemNum - 1].pivot.y, 0)).y;
                if (childButtomRowTop <= scrollRectButtom)
                {
                    for (int index = 0; index < gridLayoutGroup.constraintCount; index++)
                    {
                        childTransList[childItemNum - 1 - index].anchoredPosition = new Vector2(childTransList[childItemNum - 1 - index].anchoredPosition.x, childTransList[0].anchoredPosition.y + gridLayoutGroup.spacing.y + gridLayoutGroup.cellSize.y);
                        childTransList[childItemNum - 1 - index].SetAsFirstSibling();
                        childTransList[childItemNum - 1 - index].gameObject.SetActive(true);
                        updateChildrenDelegate(realEndIndex - childItemNum - index, childTransList[childItemNum - 1 - index]);
                    }
                    realEndIndex -= gridLayoutGroup.constraintCount;

                    if (isNeedAutoChangeConnectScale)
                        scrollContent.GetComponent<RectTransform>().sizeDelta -= new Vector2(0, gridLayoutGroup.cellSize.y + gridLayoutGroup.spacing.y);
                }
            }

        }
        lastPosition = currentPos;
        //update child list
        for (int index = 0; index < childItemNum; index++)
        {
            childTransList[index] = scrollContent.transform.GetChild(index).GetComponent<RectTransform>();
        }

    }

}

更多unity2018的功能介绍请到paws3d爪爪学院查找。链接https://www.paws3d.com/learn/,也可以加入unity学习讨论群935714213

近期更有资深开发人士直播分享unity开发经验,详情请进入官网或加入QQ群了解

猜你喜欢

转载自blog.csdn.net/qq_35037137/article/details/89923968