关闭

Unity 编辑器ScrollView滚动卡顿优化

标签: unity编辑器
2200人阅读 评论(0) 收藏 举报
分类:

原因

在使用 Unity 开发游戏的时候,经常会需要用到数据配置,方式可能是CSV、JSON等等。为了可以方便地查看修改数据,通常使用ScrollView实现在 Unity 编辑器里面以列表的形式查看数据。

当数据量大的时候,滚动视图会发现卡顿不断,测试代码如下:

using UnityEditor;
using UnityEngine;

public class TestListView : EditorWindow
{
    [MenuItem("Tool/Test")]
    private static void Init()
    {
        GetWindow<TestListView>();
    }

    private const int s_RowCount = 400;
    private const int s_ColCount = 30;

    private float m_RowHeight = 18f;
    private float m_ColWidth = 52f;
    private Vector2 m_ScrollPosition;

    void OnGUI()
    {
        OnDrawListView2();
    }

    private void OnDrawListView()
    {
        m_ScrollPosition = EditorGUILayout.BeginScrollView(m_ScrollPosition);
        for (int i = 0; i < s_RowCount; i++)
        {
            EditorGUILayout.BeginHorizontal();
            for (int j = 0; j < s_ColCount; j++)
            {
                EditorGUILayout.LabelField((i * 100 + j).ToString(), GUILayout.Width(m_ColWidth));
            }
            EditorGUILayout.EndHorizontal();
        }
        EditorGUILayout.EndScrollView();
    }
}

鼠标拖动滚动条滚动的时候,可以明显发现滚动条卡顿延迟跟着鼠标动:

解决

优化的思路就是只绘制当前可视的区域,自 Unity 5.6 开始已经提供TreeView控件,自带支持大数据集,
详见https://docs.unity3d.com/560/Documentation/ScriptReference/IMGUI.Controls.TreeView.BuildRoot.html

如果还没使用 Unity 5.6,那么可以参考它的实现方式。将Layout自动布局方式改成给定矩形来绘制,这样方便知道每行的高度和总的内容高度,再根据滚动条的坐标来计算获取当前显示的起始行和结束行,只绘制需要显示的行内容。

代码修改如下:

using UnityEditor;
using UnityEngine;

public class TestListView : EditorWindow
{
    [MenuItem("Tool/Test")]
    private static void Init()
    {
        GetWindow<TestListView>();
    }

    private const int s_RowCount = 400;
    private const int s_ColCount = 30;

    private float m_RowHeight = 18f;
    private float m_ColWidth = 52f;
    private Vector2 m_ScrollPosition;

    void OnGUI()
    {
        OnDrawListView2();
    }

    private void OnDrawListView2()
    {
        Rect totalRect = new Rect(0, 0, position.width, position.height);
        Rect contentRect = new Rect(0, 0, s_ColCount * m_ColWidth, s_RowCount * m_RowHeight);
        m_ScrollPosition = GUI.BeginScrollView(totalRect, m_ScrollPosition, contentRect);

        int num;
        int num2;
        GetFirstAndLastRowVisible(out num, out num2, totalRect.height);
        if (num2 >= 0)
        {
            int numVisibleRows = num2 - num + 1;
            IterateVisibleItems(num, numVisibleRows, contentRect.width, totalRect.height);
        }

        GUI.EndScrollView(true);
    }

    /// <summary>
    /// 获取可显示的起始行和结束行
    /// </summary>
    /// <param name="firstRowVisible">起始行</param>
    /// <param name="lastRowVisible">结束行</param>
    /// <param name="viewHeight">视图高度</param>
    private void GetFirstAndLastRowVisible(out int firstRowVisible, out int lastRowVisible, float viewHeight)
    {
        if (s_RowCount == 0)
        {
            firstRowVisible = lastRowVisible = -1;
        }
        else
        {
            float y = m_ScrollPosition.y;
            float height = viewHeight;
            firstRowVisible = (int)Mathf.Floor(y / m_RowHeight);
            lastRowVisible = firstRowVisible + (int)Mathf.Ceil(height / m_RowHeight);
            firstRowVisible = Mathf.Max(firstRowVisible, 0);
            lastRowVisible = Mathf.Min(lastRowVisible, s_RowCount - 1);
            if (firstRowVisible >= s_RowCount && firstRowVisible > 0)
            {
                m_ScrollPosition.y = 0f;
                GetFirstAndLastRowVisible(out firstRowVisible, out lastRowVisible, viewHeight);
            }
        }
    }

    /// <summary>
    /// 迭代绘制可显示的项
    /// </summary>
    /// <param name="firstRow">起始行</param>
    /// <param name="numVisibleRows">总可显示行数</param>
    /// <param name="rowWidth">每行的宽度</param>
    /// <param name="viewHeight">视图高度</param>
    private void IterateVisibleItems(int firstRow, int numVisibleRows, float rowWidth, float viewHeight)
    {
        int i = 0;
        while (i < numVisibleRows)
        {
            int num2 = firstRow + i;
            Rect rowRect = new Rect(0f, (float)num2 * m_RowHeight, rowWidth, m_RowHeight);
            float num3 = rowRect.y - m_ScrollPosition.y;
            if (num3 <= viewHeight)
            {
                Rect colRect = new Rect(rowRect);
                colRect.width = m_ColWidth;

                for (int j = 0; j < s_ColCount; j++)
                {
                    EditorGUI.LabelField(colRect, (num2 * 100 + j).ToString());
                    colRect.x += colRect.width;
                }
            }
            i++;
        }
    }
}

再次鼠标拖动滚动条滚动的时候,可以明显发现滚动条流畅许多:

编辑时的问题

因为不是绘制全部控件,那么当使用编辑框的时候,弹出的编辑控件不会跟随着滚动,如下所示:

那么就当滚动的时候,结束当前正在编辑的项吧,修改OnDrawListView2函数:

    private void OnDrawListView2()
    {
        Rect totalRect = new Rect(0, 0, position.width, position.height);
        Rect contentRect = new Rect(0, 0, s_ColCount * m_ColWidth, s_RowCount * m_RowHeight);

        // 鼠标滚动的时候,结束当前正在编辑的项
        if (Event.current.type == EventType.ScrollWheel)
        {
            if (totalRect.Contains(Event.current.mousePosition))
            {
                GUIUtility.keyboardControl = 0;
            }
        }

        EditorGUI.BeginChangeCheck();
        m_ScrollPosition = GUI.BeginScrollView(totalRect, m_ScrollPosition, contentRect);
        if (EditorGUI.EndChangeCheck())
        {
            // 滚动条滚动的时候,结束当前正在编辑的项
            GUIUtility.keyboardControl = 0;
        }

        int num;
        int num2;
        GetFirstAndLastRowVisible(out num, out num2, totalRect.height);
        if (num2 >= 0)
        {
            int numVisibleRows = num2 - num + 1;
            IterateVisibleItems(num, numVisibleRows, contentRect.width, totalRect.height);
        }

        GUI.EndScrollView(true);
    }

自动布局

如果还是想使用自动布局方式来绘制项的话,那么可以使用GUILayout.Space来占用不需要绘制的区域。

0
0
查看评论

UGUI ScrollRect滚动优化:无限循环利用Item

暑假的时候参照NGUI的循环ScrollView的思路写的,当时给上传到了资源下载频道,因为平时自己也要在CSDN下东西,有些资源要积分才能下载,所以就挂了3分,想着供自己以后下载可以用。到现在也过了将近半年的时间了,去看了下也有近100次的下载量了。 自己平时上来CSDN比较少,写文章就更少了,再...
  • mutou_222
  • mutou_222
  • 2016-01-04 16:15
  • 9226

ScrollView 最优使用方案

Purpose: 通过采用Grid Layout Group组件的各个参数, 自动控制ScrollView下的Item对象, 利用对象池技术,最优化实现滚动显示信息的功能。 详细使用方法请看源代码。 Author...
  • zhaoguanghui2012
  • zhaoguanghui2012
  • 2016-11-20 20:25
  • 1101

UGUI ScrollView 优化重复利用

  • 2018-01-16 16:51
  • 14KB
  • 下载

Unity3D ScrollView滑动优化

unity中 scrollview在游戏任务,物品列表中是很常见的使用,但是在使用过程中会出现很多问题,当有1000条item同时一起加载进场景中会瞬间造成unity死机。这里对scrollview做了一个简单的优化,会根据需要动态的平滑加载item直接附上工程链接 http://pan.baidu...
  • Truck_Truck
  • Truck_Truck
  • 2017-03-03 18:32
  • 997

UGUI提高<一> Scroll View参数详解

原创 最近用UGUI的时候老感觉有的参数不记得什么作用了,找这个找那个的,记性真不行,还是要写在博客上,不记得可以来看看。现在越来越觉得写博客真的非常有必要。 先创建一个UGUI整合好的Scroll View。 Scroll View    滑动视图  相关参...
  • u012322710
  • u012322710
  • 2016-11-17 14:51
  • 4660

[UnityUI]NGUI性能优化之ScrollView

使用UIWrapContent优化ScrollView 如上图,虽然显示20个数据,但实际的GameObject只有6个,5个用于显示,1个用于缓存。NGUI中的UIWrapContent.cs通用性不是很好,需要修改一下: //-------------------------...
  • lyh916
  • lyh916
  • 2016-05-03 21:06
  • 2361

【Unity】优化UGUI 滚动条ScrollRect(高效复用)

最近忙于性能优化,深切体会到二八法则真是指导高(tou)效(lan)工作的有力武器。这个礼拜花了几天解决了一个实际问题:UGUI的ScrollRect加载太多物体的时候,第一次弹出界面会非常卡顿,而且不在界面里的内容依然会参与绘制(毫无意义的浪费…)。 ChangeLog v1.03 终于支...
  • swj524152416
  • swj524152416
  • 2016-12-06 10:28
  • 9192

Unity中使用UGUI与ScrollView的练习

一、相关组件 ScrollRect Mask Grid Layout Group Scrollbar 二、步骤 1、创建一个Panel,命名为ScrollRect,添加 ScrollRect组件 2、在ScrollRect下创建一个子Panel,命名为Grid,添加Grid Layout Gr...
  • a6627651
  • a6627651
  • 2015-05-06 20:39
  • 51025

Unity5.4.1 Scroll_View的简单使用

unity4.5.1 Scroll_View,如下图所示 ViewPort Content 用来存放内容 ScrollBar Horizontal 水平的滚轮 ScrollBar Vertical 垂直的滚轮 Scrollview的一些大小属性ContentContent的大小决定了可以显示内容的...
  • qq_26437925
  • qq_26437925
  • 2017-01-04 22:20
  • 6868

Unity3d UGUI 滚动视图ScrollView的使用教程

Unity3d中,UGUI提供了 Scroll Rect、Grid Layout Group、Mask这三个组件,我们用来实现滚动视图,这里用一个简易的背包作为示例。
  • cp790621656
  • cp790621656
  • 2015-04-12 02:59
  • 21109
    个人资料
    • 访问:3556393次
    • 积分:36086
    • 等级:
    • 排名:第136名
    • 原创:402篇
    • 转载:88篇
    • 译文:5篇
    • 评论:3425条
    个人说明
    联系方式:
    文章存档
    最新评论