Unity - 无限滚动

今天学习:Unity - UGUI - 无限滚动

目录

今天学习:Unity - UGUI - 无限滚动

版本:

一:思路分享(Share idea)

二:脚本编写(Scripts)

三:场景面板布置(Hierarchy)

四:运行结果(Running Result)

希望大家:点赞,留言,关注咯~       😘😘😘😘

唠家常

今日无推荐


版本:

1、Unity 2020.3.10f1

时隔多周,在这里分享出来一点最近的功能吧。

借助UGUI - ScrollView,进行更改,并且实现无限滑动!!!!!!!!

一:思路分享(Share idea)

       大概就是下边这个巨作的思路,希望对大家有个思想考量方向,不过不能因为我而放弃了自己的想法,适合自己的才是最好的。说不定你自己的思路比我的还好,不过功能要的快,所以我就百度了,发现早就有大佬们实现了,所以找了一篇下来,刚好满足需求。不过我还是要把自己思路分享出来!!!

画画技术水平有限,见谅。。。。。。。

1、上来获取最上一排的位置信息和宽高度;

2、设置一个触发高度/宽度位置;

3、当再移动的过程中每排最指定节点的高度大于/小于,指定触发点时,重新设置位置到第一排/最后一排的位置,反复如此。

4、在更改位置时,对要被更改的子节点进行设置相关索引或者其他一系列操作。

二:脚本编写(Scripts)

1、既然是要实现无限滑动,那我们就先来编写第一个相关脚本:InfinityGridLayoutGroup

using System.Collections;
using System.Collections.Generic;
using UnityEngine;
using UnityEngine.UI;

namespace ThisXHGame
{
    /// <summary>
    /// 无限滑动
    /// </summary>
    [RequireComponent(typeof(GridLayoutGroup))]
    [RequireComponent(typeof(ContentSizeFitter))]
    public class InfinityGridLayoutGroup : MonoBehaviour
    {
        /*  要确定当前所属canvas的rect下方scale缩放比例,要与之保持一样。  */
        float _scale = 0.061f;

        /* 实现无限滚动,需要的最少的child数量。
         * 屏幕上能看到的+一行看不到的,比如我在屏幕上能看到 4 行,每一行 3 个。
         * 则这个值为 4行 * 3个 + 1行 * 3个 = 15个。*/
        int childrenAmount = 0;

        #region Private Attribute
        ScrollRect scrollRect;
        RectTransform rectTransform;
        GridLayoutGroup gridLayoutGroup;
        ContentSizeFitter contentSizeFitter;
        List<RectTransform> children = new List<RectTransform>();

        int amount = 0;
        int constraintCount;
        int realIndex = -1;

        float childHeight;

        bool hasInit = false;
        Vector2 startPosition;
        Vector2 gridLayoutSize;
        Vector2 gridLayoutPos;
        Dictionary<Transform, Vector2> childsAnchoredPosition = new Dictionary<Transform, Vector2>();
        Dictionary<Transform, int> childsSiblingIndex = new Dictionary<Transform, int>();
        #endregion

        public delegate void UpdateChildrenCallbackDelegate(int index, Transform trans);
        public UpdateChildrenCallbackDelegate updateChildrenCallback = null;

        void Start() => childrenAmount = transform.childCount;

        IEnumerator InitChildren()
        {
            yield return 0;

            if (!hasInit)
            {
                //获取Grid的宽度;
                rectTransform = GetComponent<RectTransform>();

                gridLayoutGroup = GetComponent<GridLayoutGroup>();
                gridLayoutGroup.enabled = false;
                constraintCount = gridLayoutGroup.constraintCount;
                childHeight = gridLayoutGroup.cellSize.y;
                contentSizeFitter = GetComponent<ContentSizeFitter>();
                contentSizeFitter.enabled = false;

                gridLayoutPos = rectTransform.anchoredPosition;
                gridLayoutSize = rectTransform.sizeDelta;


                //注册ScrollRect滚动回调;
                scrollRect = transform.parent.GetComponent<ScrollRect>();
                scrollRect.onValueChanged.AddListener((data) => { ScrollCallback(data); });

                //获取所有child anchoredPosition 以及 SiblingIndex;
                for (int index = 0; index < childrenAmount; index++)
                {
                    Transform child = transform.GetChild(index);
                    RectTransform childRectTrans = child.GetComponent<RectTransform>();
                    childsAnchoredPosition.Add(child, childRectTrans.anchoredPosition);
                    childsSiblingIndex.Add(child, child.GetSiblingIndex());
                }
            }
            else
            {
                rectTransform.anchoredPosition = gridLayoutPos;
                rectTransform.sizeDelta = gridLayoutSize;

                children.Clear();

                realIndex = -1;

                //children重新设置上下顺序;
                foreach (var info in childsSiblingIndex)
                {
                    info.Key.SetSiblingIndex(info.Value);
                }

                //children重新设置anchoredPosition;
                for (int index = 0; index < childrenAmount; index++)
                {
                    Transform child = transform.GetChild(index);

                    RectTransform childRectTrans = child.GetComponent<RectTransform>();
                    if (childsAnchoredPosition.ContainsKey(child))
                    {
                        childRectTrans.anchoredPosition = childsAnchoredPosition[child];
                    }
                    else
                    {
                        Debug.LogError("Unity Error Log : childs Anchored Position are no contain " + child.name);
                    }
                }
            }

            //int needCount = (minAmount < amount) ? minAmount : amount;
            //获取所有child;
            for (int _idx = 0; _idx < childrenAmount; _idx++)
            {
                Transform child = transform.GetChild(_idx);
                child.gameObject.SetActive(true);

                RectTransform rect = child.GetComponent<RectTransform>();
                children.Add(rect);

                //初始化前面几个;
                if (_idx < amount)
                {
                    UpdateChildrenInfoCallback(_idx, child);
                }
            }

            startPosition = rectTransform.anchoredPosition;

            realIndex = children.Count - 1;

            //Debug.Log( scrollRect.transform.TransformPoint(Vector3.zero));
            //Debug.Log(transform.TransformPoint(children[0].localPosition));

            hasInit = true; 

            //如果需要显示的个数小于设定的个数;
            for (int index = 0; index < childrenAmount; index++)
            {
                children[index].gameObject.SetActive(index < amount);
            }

            if (gridLayoutGroup.constraint == GridLayoutGroup.Constraint.FixedColumnCount)
            {
                //如果小了一行,则需要把GridLayout的高度减去一行的高度;
                int row = (childrenAmount - amount) / constraintCount;
                //Debug.Log($"---------minAmount = {minAmount}----amount = {amount}-----constraintCount = {constraintCount}-------row = {row}--- ");
                if (row > 0)
                { 
                    rectTransform.sizeDelta -= new Vector2(0, (gridLayoutGroup.cellSize.y + gridLayoutGroup.spacing.y) * row);
                }
            }
            else
            {
                //如果小了一列,则需要把GridLayout的宽度减去一列的宽度;
                int column = (childrenAmount - amount) / constraintCount;
                if (column > 0)
                {
                    rectTransform.sizeDelta -= new Vector2((gridLayoutGroup.cellSize.x + gridLayoutGroup.spacing.x) * column, 0);
                }
            }
        }

        /// <summary>
        /// 设置总的个数;
        /// </summary>
        /// <param name="count">总个数</param>
        public void InitSetAmount(int count)
        {
            amount = count;
            StartCoroutine(InitChildren());
        }

        /// <summary>
        /// 滑动回调
        /// </summary>
        void ScrollCallback(Vector2 data)
        {
            if (data.y >= 1.0f)
                return;
            UpdateChildrenInfo();
        }

        /// <summary>
        /// 子物体的更改
        /// </summary>
        void UpdateChildrenInfo()
        {
            if (childrenAmount < transform.childCount)
                return;

            Vector2 currentPos = rectTransform.anchoredPosition;

            if (gridLayoutGroup.constraint == GridLayoutGroup.Constraint.FixedColumnCount)
            {
                float offsetY = currentPos.y - startPosition.y;

                //Debug.Log("offsetY is " + (offsetY > 0.0f));
                if (offsetY > 0)
                {
                    //向上拉,向下扩展;
                    {
                        if (realIndex >= amount - 1)
                        {
                            startPosition = currentPos;
                            return;
                        }

                        float scrollRectUp = scrollRect.transform.TransformPoint(Vector3.zero).y;

                        Vector3 childBottomLeft = new Vector3(children[0].anchoredPosition.x, children[0].anchoredPosition.y - gridLayoutGroup.cellSize.y, 0f);
                        float childBottom = transform.TransformPoint(childBottomLeft).y;

                        if (childBottom >= scrollRectUp + childHeight * _scale)
                        {
                            //移动到底部;
                            for (int index = 0; index < constraintCount; index++)
                            {
                                children[index].SetAsLastSibling();

                                children[index].anchoredPosition = new Vector2(children[index].anchoredPosition.x, children[children.Count - 1].anchoredPosition.y - gridLayoutGroup.cellSize.y - gridLayoutGroup.spacing.y);

                                realIndex++;

                                if (realIndex > amount - 1)
                                {
                                    children[index].gameObject.SetActive(false);
                                }
                                else
                                {
                                    UpdateChildrenInfoCallback(realIndex, children[index]);
                                }
                            }

                            //GridLayoutGroup 底部加长;
                            rectTransform.sizeDelta += new Vector2(0, gridLayoutGroup.cellSize.y + gridLayoutGroup.spacing.y);

                            //更新child;
                            for (int index = 0; index < children.Count; index++)
                            {
                                children[index] = transform.GetChild(index).GetComponent<RectTransform>();
                            }
                        }
                    }
                }
                else
                {
                    //向下拉,下面收缩;
                    if (realIndex + 1 <= children.Count)
                    {
                        startPosition = currentPos;
                        return;
                    }
                    RectTransform scrollRectTransform = scrollRect.GetComponent<RectTransform>();
                    Vector3 scrollRectAnchorBottom = new Vector3(0, -scrollRectTransform.rect.height - gridLayoutGroup.spacing.y, 0f);
                    float scrollRectBottom = scrollRect.transform.TransformPoint(scrollRectAnchorBottom).y;

                    Vector3 childUpLeft = new Vector3(children[children.Count - 1].anchoredPosition.x, children[children.Count - 1].anchoredPosition.y, 0f);

                    float childUp = transform.TransformPoint(childUpLeft).y;

                    if (childUp < scrollRectBottom)
                    {
                        //把底部的一行 移动到顶部
                        for (int index = 0; index < constraintCount; index++)
                        {
                            children[children.Count - 1 - index].SetAsFirstSibling();

                            children[children.Count - 1 - index].anchoredPosition = new Vector2(children[children.Count - 1 - index].anchoredPosition.x, children[0].anchoredPosition.y + gridLayoutGroup.cellSize.y + gridLayoutGroup.spacing.y);

                            children[children.Count - 1 - index].gameObject.SetActive(true);

                            UpdateChildrenInfoCallback(realIndex - children.Count - index, children[children.Count - 1 - index]);
                        }

                        realIndex -= constraintCount;

                        //GridLayoutGroup 底部缩短;
                        rectTransform.sizeDelta -= new Vector2(0, gridLayoutGroup.cellSize.y + gridLayoutGroup.spacing.y);

                        //更新child;
                        for (int index = 0; index < children.Count; index++)
                        {
                            children[index] = transform.GetChild(index).GetComponent<RectTransform>();
                        }
                    }
                }
            }
            else
            {
                float offsetX = currentPos.x - startPosition.x;

                if (offsetX < 0)
                {
                    //向左拉,向右扩展;
                    {
                        if (realIndex >= amount - 1)
                        {
                            startPosition = currentPos;
                            return;
                        }

                        float scrollRectLeft = scrollRect.transform.TransformPoint(Vector3.zero).x;

                        Vector3 childBottomRight = new Vector3(children[0].anchoredPosition.x + gridLayoutGroup.cellSize.x, children[0].anchoredPosition.y, 0f);
                        float childRight = transform.TransformPoint(childBottomRight).x;

                        if (childRight <= scrollRectLeft)
                        {
                            //移动到右边;
                            for (int index = 0; index < constraintCount; index++)
                            {
                                children[index].SetAsLastSibling();

                                children[index].anchoredPosition = new Vector2(children[children.Count - 1].anchoredPosition.x + gridLayoutGroup.cellSize.x + gridLayoutGroup.spacing.x, children[index].anchoredPosition.y);

                                realIndex++;

                                if (realIndex > amount - 1)
                                {
                                    children[index].gameObject.SetActive(false);
                                }
                                else
                                {
                                    UpdateChildrenInfoCallback(realIndex, children[index]);
                                }
                            }

                            //GridLayoutGroup 右侧加长;
                            rectTransform.sizeDelta += new Vector2(gridLayoutGroup.cellSize.x + gridLayoutGroup.spacing.x, 0);

                            //更新child;
                            for (int index = 0; index < children.Count; index++)
                            {
                                children[index] = transform.GetChild(index).GetComponent<RectTransform>();
                            }
                        }
                    }
                }
                else
                {
                    //向右拉,右边收缩;
                    if (realIndex + 1 <= children.Count)
                    {
                        startPosition = currentPos;
                        return;
                    }
                    RectTransform scrollRectTransform = scrollRect.GetComponent<RectTransform>();
                    Vector3 scrollRectAnchorRight = new Vector3(scrollRectTransform.rect.width + gridLayoutGroup.spacing.x, 0, 0f);
                    float scrollRectRight = scrollRect.transform.TransformPoint(scrollRectAnchorRight).x;

                    Vector3 childUpLeft = new Vector3(children[children.Count - 1].anchoredPosition.x, children[children.Count - 1].anchoredPosition.y, 0f);

                    float childLeft = transform.TransformPoint(childUpLeft).x;

                    if (childLeft >= scrollRectRight)
                    {
                        //把右边的一行 移动到左边;
                        for (int index = 0; index < constraintCount; index++)
                        {
                            children[children.Count - 1 - index].SetAsFirstSibling();

                            children[children.Count - 1 - index].anchoredPosition = new Vector2(children[0].anchoredPosition.x - gridLayoutGroup.cellSize.x - gridLayoutGroup.spacing.x, children[children.Count - 1 - index].anchoredPosition.y);

                            children[children.Count - 1 - index].gameObject.SetActive(true);

                            UpdateChildrenInfoCallback(realIndex - children.Count - index, children[children.Count - 1 - index]);
                        }

                        //GridLayoutGroup 右侧缩短;
                        rectTransform.sizeDelta -= new Vector2(gridLayoutGroup.cellSize.x + gridLayoutGroup.spacing.x, 0);

                        //更新child;
                        for (int index = 0; index < children.Count; index++)
                        {
                            children[index] = transform.GetChild(index).GetComponent<RectTransform>();
                        }

                        realIndex -= constraintCount;
                    }
                }
            }

            startPosition = currentPos;
        }

        /// <summary>
        /// 更新回调
        /// </summary>
        /// <param name="index">当前索引</param>
        /// <param name="trans">当前物体</param>
        void UpdateChildrenInfoCallback(int index, Transform trans)
        {
            updateChildrenCallback?.Invoke(index, trans);
        }
    }
}

2、那我们有了相关脚本,我们就需要去编写相关控制脚本咯:TheInfinityScrollController


using UnityEngine;
using UnityEngine.UI;

namespace ThisXHGame
{
    /// <summary>
    /// 无限滑动控制器
    /// </summary>
    public class TheInfinityScrollController : MonoBehaviour
    {
        InfinityGridLayoutGroup infinityGridLayoutGroup;

        void Start()
        {
            初始化数据列表;
            infinityGridLayoutGroup = 
GameObject.FindObjectOfType<InfinityGridLayoutGroup>();

            infinityGridLayoutGroup.updateChildrenCallback = UpdateChildrenCallback;
            for (int i = 0; i < infinityGridLayoutGroup.transform.childCount; i++)
            {
                Transform child = infinityGridLayoutGroup.transform.GetChild(i);
                child.GetComponent<Button>().onClick.AddListener(() =>
                {
                    OnClickButtonWithIndex(child.GetComponentInChildren<Text>());
                });
            }

            infinityGridLayoutGroup.InitSetAmount(100);
        }


        /// <summary>
        /// 通过当前缩略图索引从ios相册获取原图
        /// </summary>
        void OnClickButtonWithIndex(Text tex)
        {
            Debug.Log($" Unity log:     index is {tex.text} in your click 
button...");
        }

        /// <summary>
        /// 上下翻滚更新函数
        /// </summary>
        void UpdateChildrenCallback(int indx, Transform trans)
        {
            Text tex = trans.Find("Text").GetComponent<Text>();
            tex.text = indx.ToString();
        }
    }
}

至此,我们全部代码已经编写完了。

三:场景面板布置(Hierarchy)

1、那既然是借助了ScrollView,那我们就先看相关设置。这里我们把ScrollView下的ViewportScrollbar HorizontalScrollbar Vertical全部删除,因为我们不需要他们。

2、既然Viewport已经删除,那我们接着是整Content,首先增加脚本InfinityGridLayoutGroup,接着要注意:在GridLayoutGroup下的Constraint要设置为FixedColumnCount。★★★★★

3、好,我们接下来就是设置他的子节点了,具体要设置多少个,之前代码里有说明,不过这里还是再说一下。

        /*
         * 实现无限滚动,需要的最少的child数量。
         * 屏幕上能看到的+一行看不到的,比如我在屏幕上能看到 4 行,每一行 3 个。
         * 则这个值为 4行 * 3个 + 1行 * 3个 = 15个。
         */

那么根据这个结果,我们就创建15个子节点。

四:运行结果(Running Result)

1、刚运行

2、运行中

至此,圆满结束。

希望大家:点赞,留言,关注咯~       😘😘😘😘

唠家常

  • 小黑的今日分享结束啦,小伙伴们你们get到了么,你们有没有更好的办法呢,可以评论区留言分享,也可以加小黑的QQ:841298494,大家一起进步。

今日有推荐

参考:https://blog.csdn.net/huutu/article/details/51549762

  • 9
    点赞
  • 27
    收藏
    觉得还不错? 一键收藏
  • 打赏
    打赏
  • 6
    评论
评论 6
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包

打赏作者

青衫磊落长歌行

觉得小黑这篇文章不赖,打赏哟~

¥1 ¥2 ¥4 ¥6 ¥10 ¥20
扫码支付:¥1
获取中
扫码支付

您的余额不足,请更换扫码支付或充值

打赏作者

实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值