The Unity profiler revealed recently that Pawns for iPhone was spending 5% of its time in a UnityGUI script that I didn’t need any more. Removing it from my scene was easy enough, but it got me thinking. The script looked something like this:
void OnGUI()
{
if (showDialogBox)
{
// lots of code that never executed
}
}
Not much going on here! So it appeared that just having OnGUI in a script was expensive. This was worth knowing because I still have three other OnGUI scripts left in the scene. Each of them exits immediately if their dialog box is not showing. So in theory they should cost nothing during actual gameplay. But the profiler told a different story:
It turns out that there are some major setup costs associated with OnGUI. GUI.Repaint is listed as taking over 65% of the CPU! Of that, most of the time is spent in GUIUtility.BeginGUI. Note that almost no time at all is spent in my three OnGUI methods (TutorialController.OnGUI, DlgOKCancel.OnGUI, and DlgPuzzleFooter.OnGUI.) So the early exit clause was working, but the damage was done before OnGUI was even called.
To verify this I disabled the three objects in the inspector while the scene was running. The new profiler results are much healthier:
GUI.Repaint has been eliminated. The time is now mostly spent on rendering, physics, and in scripts.
Since disabling the scripts solved the problem, I did the obvious thing, which was to to disable the objects with the OnGUI scripts on them until needed:
footerDlg.gameObject.active = false;
But one trick is needed to reactivate them. Search functions like Find and FindObjectOfType do not return inactive objects. Instead, you must already have a reference to the object you want to activate.
One way is to start the scene with active = true. Your script can then Find the object, store a reference to it, and then set active = false until it is needed. (This is what I did because it worked well with the code I’d already written.)
void Awake()
{
// cache reference to footer dlg
footerDlg = (DlgPuzzleFooter) GameObject.FindObjectOfType(typeof(DlgPuzzleFooter));
if ( footerDlg == null )
{
Debug.Log("Error: DlgPuzzleFooter object is missing.");
}
else
{
// deactivate it until needed, for performance
footerDlg.gameObject.active = false;
}
}
A second way is simply to declare a public variable for the inactive object, and then use Unity’s Inspector to set the value at design-time.
A third approach is detailed in a post on UnityAnswers. It keeps a list of every object that gets deactivated.
The fact that OnGUI looks like any other Unity method is misleading. It’s an expensive operation, even if it returns immediately without drawing anything.
UnityGUI itself is not recommended where performance is important, particularly during gameplay on an iPhone or Android device. But it is a convenient way to define menus, dialog boxes, high score lists, and other parts of the user interface. By deactivating OnGUI scripts until they are needed, you can often avoid paying much for that convenience.