FishPlayer

一个喜欢摸鱼的废物

0%

UnityEditor Scrollview Pagination(分页)

前言

之前在做CloneConsole的时候,会在每次绘制的时候尝试绘制所有的log,然后再交给UnityEditor自己去处理显示的逻辑。因为我们会堆积几百甚至几千条log,这样的做法无疑会带来性能问题,结果就是console卡得不能用。

思路

其实思路也很简单,只需要画那些会显示出来的item就行了。
在这之前需要算出哪些item是可以被显示出来的

  1. 算出所有的item加起来有多高
  2. 用当前滑条的位置来推算出当前需要绘制的第一条item的索引
  3. 绘制item
  4. 绘制滑条并计算其下一次的位置

其实这个思路应该也能应用到UGUI上,下次我也要试试。

上代码

在一个新窗口中尝试画一画就好。
首先要准备好用来测试的一些数据。

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21

namespace TempDraw
{
[System.Serializable]
public struct TempDrawData
{
public int IconTag;
public string TempMessage;
}

public class TempDrawWindowTester : MonoBehaviour
{
private TempDrawWindowTester _instance = null;
public TempDrawWindowTester Instance => _instance;

List<TempDrawData> m_data = null;

// used to give out the data
public static event System.Action<List<TempDrawData>> OnDataSpread;
}
}

把测试用的数据都存在这个Mono里,再用它的静态事件把数据传递到窗口中。
代码里的 Button attribute 是用来在editor上创建按钮的。

测试用的数据准备好了,我们来来进行绘制。

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
100
101
102
103
104
105
106
107
108
109
110
111
112
113
114
115
116
117
118
119
120
121
122
123
124
125
126
127
128
129
130
131
132
133

using System.Collections.Generic;
using UnityEditor;
using UnityEngine;

namespace TempDraw
{
public class TempDrawWindow : EditorWindow
{
public static string WINDOW_NAME = "Temp Draw Window";
private static TempDrawWindow _instance = null;
public static TempDrawWindow Instance => _instance;

private List<TempDrawData> m_data = null;
private float m_scrollPosition = 0.0f;

#region temp element
private const float ELEMENT_HEIGHT = 20.0f;
private const float ELEMENT_ICON_SIZE = 20.0f;

#endregion

#region icons
private Texture2D m_infoIconSmall = null;
private Texture2D m_warningIconSmall = null;
#endregion

[MenuItem("Window/Temp Draw Window")]
private static void OpenWindow()
{
TempDrawWindow window = GetWindow<TempDrawWindow>();
Texture2D icon = EditorGUIUtility.Load("icons/UnityEditor.ConsoleWindow.png") as Texture2D;
window.titleContent = new GUIContent(WINDOW_NAME, icon);
}

public void SetData(List<TempDrawData> data)
{
if (null == m_data)
m_data = new List<TempDrawData>();
else
m_data.Clear();

m_data.AddRange(data);
}

private void DrawTempElement(Rect elementRect, int dataIndex)
{
Rect iconRect = new Rect(elementRect.x, elementRect.y, ELEMENT_ICON_SIZE, elementRect.height);
if (m_data[dataIndex].IconTag == 0)
GUI.Label(iconRect, m_infoIconSmall);
else
GUI.Label(iconRect, m_warningIconSmall);

Rect labelRect = new Rect(elementRect.x + ELEMENT_ICON_SIZE, elementRect.y, elementRect.width - ELEMENT_ICON_SIZE, elementRect.height);
GUI.Label(labelRect, $"Tag: {m_data[dataIndex].IconTag} ; Message: {m_data[dataIndex].TempMessage} ;");
}

private void DrawTempRect()
{
// only use half space of this shit window
Rect viewportRect = new Rect(0.0f, 0.0f, this.position.width, this.position.height * 0.5f);

float scrollbarWidth = GUI.skin.verticalScrollbar.fixedWidth;
Rect scrollbarRect = new Rect(viewportRect.x + viewportRect.width - scrollbarWidth, viewportRect.y, scrollbarWidth, viewportRect.height);
Rect currentRect = new Rect(0.0f, 0.0f, viewportRect.width - scrollbarWidth, viewportRect.height);
float viewportHeight = viewportRect.height;
int elementCount = m_data.Count;

GUI.BeginClip(currentRect); // to clip the overflow stuff
int indexOffset = Mathf.FloorToInt(m_scrollPosition / ELEMENT_HEIGHT);
int showCount = Mathf.CeilToInt(currentRect.height / ELEMENT_HEIGHT);
showCount = showCount > elementCount ? elementCount : showCount;
float startPosY = (indexOffset * ELEMENT_HEIGHT) - m_scrollPosition;

for (int i = 0; i < showCount; i++)
{
Rect elementRect = new Rect(0, 0 + startPosY + i * ELEMENT_HEIGHT, currentRect.width, ELEMENT_HEIGHT);
DrawTempElement(elementRect, indexOffset + i);
}
GUI.EndClip();

// do stuff for scroller
float fullElementHeight = elementCount * ELEMENT_HEIGHT;
m_scrollPosition = Mathf.Max(0, GUI.VerticalScrollbar(scrollbarRect, m_scrollPosition, currentRect.height, 0, Mathf.Max(fullElementHeight, currentRect.height)));

int controlId = GUIUtility.GetControlID(FocusType.Passive);
float scrollSensitivity = ELEMENT_HEIGHT;
float maxScrollPos = (fullElementHeight > currentRect.height) ? (fullElementHeight - currentRect.height) : 0;

if (EventType.ScrollWheel == Event.current.GetTypeForControl(controlId))
{
m_scrollPosition = Mathf.Clamp(m_scrollPosition + Event.current.delta.y * scrollSensitivity, 0, maxScrollPos);
Event.current.Use();
}

}

private void GetAsset()
{
m_infoIconSmall = EditorGUIUtility.Load("icons/console.infoicon.sml.png") as Texture2D;
m_warningIconSmall = EditorGUIUtility.Load("icons/console.warnicon.sml.png") as Texture2D;
}

#region life cycle

private void Awake()
{
_instance = this;
m_data = new List<TempDrawData>();
}

private void OnEnable()
{
TempDrawWindowTester.OnDataSpread -= SetData;
TempDrawWindowTester.OnDataSpread += SetData;
GetAsset();
}

private void OnGUI()
{
DrawTempRect();
}

private void OnDestroy()
{
TempDrawWindowTester.OnDataSpread -= SetData;
_instance = null;
}

#endregion

}
}

效果如图