I’m trying to draw a scroll area with buttons inside. There are currently 380 buttons, but only 5 or 6 are displayed at one time. This is my code:
if (matches != null)
{
GUILayout.BeginArea(new Rect(Screen.width/2 - 165, 216, 330, 120), "");
scrollPos = GUILayout.BeginScrollView(scrollPos, false, true);
GUILayout.BeginVertical(GUILayout.Width(304));
foreach(string match in matches)
{
if (GUILayout.Button(match))
{
currentMatch = dataSource.GetMatch(match);
}
}
GUILayout.EndVertical();
GUILayout.EndScrollView();
GUILayout.EndArea();
}
Anyway, when this code is enabled, the framerate drops from ~100fps to under 30fps. Does anyone have any ideas on why this is? Am I asking too much of Unity to display this many buttons? I just assumed they got clipped and ignored if they weren’t in the scroll area. Cheers!
So basically what you’re saying is the Unity GUI code sucks at culling
The obvious fix for this would be to figure out what stuff isn’t going to be visible and not even try to draw it. (Clearly it would be even better if Unity figured this out and that drawing a completely offscreen widget was essentially free.)
It might not be the drawing itself causing the slowdown, but layouting. It’s not cheap, and most likely the efficiency is based on the square of the number of objects in question, so bigger numbers mean much bigger delays in layout times. And since this has to happen before anything can be culled, being visible or not will have no effect on this time.
As suggested, try to rewrite your code so that only the visible ones are drawn. maybe instead of the standard scroll pane you could show a vertical slider that determines which button you start rendering at and just draw the next 6.
Thanks for the suggestions! I tried rewriting it to not use GUILayout at all:
if (matches != null)
{
scrollPos = GUI.BeginScrollView(new Rect(Screen.width/2 - 165, 216, 330, 120), scrollPos, new Rect(0, 0, 304, 7600));
Rect r = new Rect(0, 0, 304, 20);
foreach (string match in matches)
{
if (GUI.Button(r, match))
{
currentMatch = dataSource.GetMatch(match);
}
r.y += 20;
}
GUI.EndScrollView();
}
however this offers only a few frames per second improvement.
I’ll have a go at implementing the other method suggested, whereby I draw 6 buttons, starting from an index based on a slider. That should be much quicker to draw, though unfortunately it won’t have the pretty smooth scrolling functionality
Podperson is right about the culling, you need to roll this yourself.
Here’s a simple version, it’s similar to how Unity does the project and hierarchy scroller.
It has 2000 buttons and won’t go above 25 draw calls.
C#
private Vector2 listScroller;
private GUIContent[] bunchOfButtons;
void Start()
{
bunchOfButtons = new GUIContent[2000];
for (int i = 0; i < bunchOfButtons.Length; i++)
{
bunchOfButtons[i] = new GUIContent("Button" + i.ToString());
}
}
private void OnGUI()
{
GUI.BeginGroup(new Rect(0, 0, 200, 300), "", "box");
DoScrollArea(new Rect(0, 0, 200, 300), bunchOfButtons, 32);
GUI.EndGroup();
}
private void DoScrollArea(Rect position, GUIContent[] buttons, int buttonHeight)
{
float height = 0; int index = 0;
if (buttons.Length > 0)
height = ((buttons.Length - 1) * buttonHeight);
listScroller = GUI.BeginScrollView(position, listScroller, new Rect(0, 0, position.width - 20, height + buttonHeight));
for (index = 0; index < buttons.Length; index++)
if (((index + 1) * buttonHeight) > listScroller.y) break;
for (; index < buttons.Length (index * buttonHeight) < listScroller.y + position.height; index++)
GUI.Button(new Rect(0, index * buttonHeight, position.width - 16, buttonHeight), buttons[index]);
GUI.EndScrollView();
}
Ah I see, its good to know that we can’t be naïve about the GUI draw calls and culling. Perhaps that should be mentioned in the documentation somewhere? (Or did I just miss it?)
Anyway, that code works perfectly for me, and the fps stays at around 100 too - thanks! Maybe it should go onto the wiki, in case someone else runs into this problem?
Out of interested I’ve noticed that you can have a normal gui scroll area with 1000’s of labels in it in the Editor and it runs pretty well, ok it’s actually pretty awful going from 120fps to 40fps but display the same thing in the web browser and bang performance hit central, down to like 5fps.
Anyway thanks for the code will try it out and hopefully it will fix my problem too.