UGUI制作类似Unity的Hierarchy(层级视图)

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

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

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

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

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

得了,自己造吧。

先展示一个动图:

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

 

首先要有个大致思路:

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

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

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

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

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

 

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

至于动态的滑动:

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

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

 

先用一张图理解一下:

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

向下滑动,

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

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

 

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

 

        /// <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

 

就这样。拜拜~

posted @ 2021-01-07 18:38  有只小耗子  阅读(328)  评论(0编辑  收藏  举报