Unity无限滚动的滚动显示组件,可以 折叠/打开 节点的滚动显示组件。

最近在开发一个加载模型的功能,自然用到了选中物体的面板。

类似Unity的Hierarchy(层级视图),点击UI同时也选中物体,点击物体反过来也选中了UI。

加载模型的时候展示模型节点信息,最开始直接使用ScrollView组件。

每个Transform组件就是一个节点,这导致这个模型子物体有成千上万个的时候,节点生成的巨多,导致Drawcall巨多,卡成屎。

然后想到之前见过的无限滚动ScrollView,想过直接用别人造的轮子跑,但是找了好多跟我想要达到的功能并不一致。

得了,自己造吧。

先展示一个动图:

unity ui的层级怎么在3d物体之上_List

点选节点可以实现对应模型的操作。按住Ctrl可以多选。

 

首先要有个大致思路:

有两个问题需要解决:1.怎么能让节点列表折叠 / 打开显示?2.怎么让节点无限利用起来?

第一个问题:我这里用一个List储存一下所有的节点,再用一个List储存一下所有的可见节点。

/// <summary>
        /// 所有的节点 
        /// </summary>public List<NodeItemSerializable> allNodesInfo = new List<NodeItemSerializable>();

        /// <summary>
        /// 当前可以显示的节点
        /// </summary>
        List<NodeItemSerializable> canShowNodes = new List<NodeItemSerializable>();

每次当有折叠/打开的操作时候,把canShowNodes列表刷新一下就解决了这个问题。

 

第二个问题:卡顿,原因就是,节点太多,那么不生成看不到的节点就可了,每次动态调整场景中的可见节点。

至于动态的滑动:

当滑条向下滑的时候将顶部的物体超出视野的拿走,同时将底部的空缺使用顶部拿走的物体补上

当滑条向上滑的时候,反之。

 

先用一张图理解一下:

unity ui的层级怎么在3d物体之上_Stack_02

所以真正在场景中用户可见的节点列表就是上图黄色区域。 

向下滑动,

就从黄色区域第一个节点,塞进绿色区域最后一个位置,

再从红色区域拿第一个节点,塞到黄色区域最后一个位置。(脑海中浮现了人体蜈蚣,哈哈哈)

 

理解了图片,我们再来看动态更改列表的操作:

 

/// <summary>
        /// 当前显示的物体最后一个所引值
        /// </summary>
        public int curShowObjLastIndex;

这里用了一个int变量记录一下最后一个显示在列表中的节点索引值。

/// <summary>
        /// 在可显示物体之前的节点信息
        /// </summary>
        Stack<NodeItemSerializable> beforeNodes = new Stack<NodeItemSerializable>();
        /// <summary>
        /// 在可显示物体之后的节点信息
        /// </summary>
        Stack<NodeItemSerializable> afterNodes = new Stack<NodeItemSerializable>();

这两个变量用了Stack,利用栈的特性,先进先出,方便存取信息。

 

下面的offsetY就是获取的鼠标滑动操作: offsetY = -Input.GetAxis("Mouse ScrollWheel") * 1000;

if (offsetY > 0)
                    {
               //这里的 cueShowObjLastIndex 是在场景中节点的最后一位索引
                        if (curShowObjLastIndex >= canShowNodes.Count)
                        {
                            offsetY = 0;
                        }
                        //Debug.Log("向上拽,item向下移动,最上头的item给before,最下层从after取出");
                        float tempY = parent.anchoredPosition.y;
                        tempY += offsetY;
                        if (offsetY != 0 && tempY >= tempItemHeight && afterNodes.Count > 0)
                        {
                            beforeNodes.Push(canShowNodes[curShowObjLastIndex - allNodeObjs.Count]);
                            DestroyImmediate(allNodeObjs[0]);
                            allNodeObjs.RemoveAt(0);
                            GameObject tempAddGo = Instantiate(tempChildRect.gameObject, parent);
                            SetNodeObjInfo(tempAddGo, afterNodes.Pop());
                            allNodeObjs.Add(tempAddGo);
                            tempY = 0;
                            curShowObjLastIndex += 1;
                            SetScrollBarValue();
                        }
                        parent.anchoredPosition = new Vector2(parent.anchoredPosition.x, tempY);
                    }
                    else if (offsetY < 0)
                    {
                        //当前索引减去总显示物体数量==0就代表拉到最顶端了
                        if (curShowObjLastIndex - allNodeObjs.Count <= 0)
                        {
                            offsetY = 0;
                        }
                        //Debug.Log("向下拽" + offsetY);
                        float tempY = parent.anchoredPosition.y;
                        tempY += offsetY;

                        if (offsetY != 0 && tempY <= -tempItemHeight && beforeNodes.Count > 0)
                        {
                            afterNodes.Push(canShowNodes[curShowObjLastIndex - 1]);
                            DestroyImmediate(allNodeObjs[allNodeObjs.Count - 1]);
                            allNodeObjs.RemoveAt(allNodeObjs.Count - 1);
                            GameObject tempAddGo = Instantiate(tempChildRect.gameObject, parent);
                            tempAddGo.transform.SetAsFirstSibling();
                            SetNodeObjInfo(tempAddGo, beforeNodes.Pop());
                            allNodeObjs.Insert(0, tempAddGo);
                            tempY = 0;
                            curShowObjLastIndex -= 1;
                            SetScrollBarValue();
                        }
                        parent.anchoredPosition = new Vector2(parent.anchoredPosition.x, tempY);
                    }

 

这里不断的使用destroy和Instantiate,也会造成性能下降,可以搞个对象池解决。

 

源码:https://github.com/wtb521thl/InfiniteRollingScrollViewDemo

 

就这样。拜拜~