Elegant Editor-Only Script Execution in Unity3d

Lately I’ve been doing a lot of Unity programming. I love how easy it is to develop editor tools for my designers and how seamlessly these tools tend to integrate with the runtime environment. My favourite feature is the [ExecuteInEditMode] attribute because it lets me write all kinds of funky real-time level generation tools. (Like extruding 3D meshes from 2D primitives!) But what this attribute sorely lacks is the ability to have code execute ONLY in edit mode, where performance isn’t such a big deal. By default code placed in the Update() method will execute both when the scene changes in edit mode AND during every frame of play mode, making it unwieldy for expensive operations like realtime mesh generation (or even a large number of tiny operations, like setting a renderer’s sorting layer).

Step 1: Execute Only In Edit Mode

Various hacks around the internet purport to solve this problem. Some suggest using conditional compilation directives like #if UNITY_EDITOR. This is helpful for platform-specific builds, but unfortunately code placed within these directives will still execute in the editor’s play mode (which is, of course, where 98% of testing happens). Another approach involves checking a global variable like Application.isPlaying before doing anything editor-specific, but this is less than ideal should you have hundreds of active scripts in your scene (the performance cost of calling Update() on that many components, even if that function contains only one boolean comparison, can add up surprisingly fast). I’ve also found, although I haven’t verified it experimentally, that isPlaying becomes rather unreliable when it mixes with big scenes, custom inspectors and ExecuteInEditMode scripts.

I now use a solution that makes use of both conditional compile tags and a little-known global variable in the EditorApplication class:

Here’s how this works. In edit mode, isPlayingOrWillChangePlaymode will always return false; thus, the editor-only code located within that else statement will execute every time Update() is called (generally every time the scene changes). In play mode isPlayingOrWillChangePlaymode will always return true, causing the MonoBehaviour to disable itself on the very first Update() and cease hogging performance beyond the first frame. (This is especially helpful when you’re trying to use the profiler to isolate script bottlenecks.) In compiled versions of the game the Update() method won’t even exist, saving us the trouble of having to disable it. The result of all this is that you can run horrifyingly-expensive setup code in edit mode that costs virtually no performance during play, which arguably is the entire point of an IDE like Unity.

Now, I know what some of you are thinking: ‘Why not do away with all this ExecuteInEditMode voodoo and simply write a custom inspector? It will never run in play mode and you can use SerializedProperties and GUILayout and all that magic.’ Well, here’s the thing: Custom inspectors act only on currently-selected Unity object(s). Suppose you had a bunch of prefab instances, some of which had overridden properties, and you wanted every single one of them to regenerate their content each time you modified the parent prefab? What if some of your generator scripts modify other generator scripts, and you needed those ones to be responsible for their own regeneration even when not a single one of them is selected (and therefore their custom inspector code is completely unloaded)? What if those scripts used prefabs? ExecuteInEditMode provides an understandable, reliable, and relatively efficient way to let scripts respond to any change in state from anywhere in the IDE. Inspectors will never do that for you.

Read more…