有關Editor的記錄(一)

警醒與鞭策發表於2020-10-21

 

//ScrollView滾動卡頓優化

為什麼要對ScrollView滾動卡頓做優化?是因為在使用 Unity 開發遊戲的時候,經常會需要用到資料配置,方式可能是CSV、JSON等等。為了可以方便地檢視修改資料,通常使用實現ScrollView在 Unity 編輯器裡面以列表的形式檢視資料。

當資料量大的時候,滾動檢視會發現卡頓不斷,測試程式碼如下:

?

1

2

3

4

5

6

7

8

9

10

11

12

13

14

15

16

17

18

19

20

21

22

23

24

25

26

27

28

29

30

31

32

33

34

35

36

37

38

using UnityEditor;

using UnityEngine;

 

public class TestListView : EditorWindow

{

    [MenuItem("Tool/Test")]

    private static void Init()

    {

        GetWindow();

    }

 

    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自動佈局方式改成給定矩形來繪製,這樣方便知道每行的高度和總的內容高度,再根據滾動條的座標來計算獲取當前顯示的起始行和結束行,只繪製需要顯示的行內容。

程式碼修改如下:

?

1

2

3

4

5

6

7

8

9

10

11

12

13

14

15

16

17

18

19

20

21

22

23

24

25

26

27

28

29

30

31

32

33

34

35

36

37

38

39

40

41

42

43

44

45

46

47

48

49

50

51

52

53

54

55

56

57

58

59

60

61

62

63

64

65

66

67

68

69

70

71

72

73

74

75

76

77

78

79

80

81

82

83

84

85

86

87

88

89

90

91

92

93

94

95

96

97

98

99

using UnityEditor;

using UnityEngine;

 

public class TestListView : EditorWindow

{

    [MenuItem("Tool/Test")]

    private static void Init()

    {

        GetWindow();

    }

 

    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);

    }

 

    ///

    /// 獲取可顯示的起始行和結束行

    ///

    /// 起始行

    /// 結束行

    /// 檢視高度

    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);

            }

        }

    }

 

    ///

    /// 迭代繪製可顯示的項

    ///

    /// 起始行

    /// 總可顯示行數

    /// 每行的寬度

    /// 檢視高度

    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函式:

?

1

2

3

4

5

6

7

8

9

10

11

12

13

14

15

16

17

18

19

20

21

22

23

24

25

26

27

28

29

30

31

32

33

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來佔用不需要繪製的區域。

 

///

相關文章