Skip to content

Reuse Editor in EditorWindow – Unity Editor

While working on my Ultimate Screenshot Tool (yes I’m plugging it again), I struggled with how to implement an EditorWindow without completely copy-pasting my Editor. In my mind, the benefits of direct, saved references to in scene GameObjects made an Editor the best choice choice for a screenshot tool. However, the convenience of grabbing a screenshot without adding anything to your scene could not be overlooked. Therefore, I decided to compromise and do both.

How?

Well, it wasn’t easy. My initial thought was to just wrap an editor window around my existing editor. However, there wasn’t any documentation around it, I knew others that had unsuccessfully tried to do so in the past, and my first attempt was a complete dead end. I opened the EditorWindow and everything was greyed out and un-editable. I started assuming there was a reason other people weren’t doing it and shelved the idea for a minute.

How really?

Re-doing everything! Well, not everything. It turns out that Unity doesn’t like PropertyFields that are created in an Editor wrapped in an EditorWindow. Who knew? Directly using the field and assigning directly to the SerializedProperty (rather than having the assignment be a side effect of the function) did work though, so I went with that. (I didn’t want to lose the functionality of having a general PropertyField like function, so I rolled my own. Check it out here.)

Just Get to the Code

using UnityEngine;
using UnityEditor;

public class CustomEditorGUITestScriptEditorWindow : EditorWindow
{
    CustomEditorGUITestScript customEditorGUITestScript;
    Editor customEditorGUITestScriptEditor;

    GameObject temp;
    Vector2 scrollPos;

    [MenuItem("Tools/Flexible Editor Fields/Editor Window", false, 0)]
    static void Init()
    {
        CustomEditorGUITestScriptEditorWindow editorWindow = (CustomEditorGUITestScriptEditorWindow)GetWindow(typeof(CustomEditorGUITestScriptEditorWindow));
        GUIContent titleContent = new GUIContent("CustomEditorGUI");
        editorWindow.autoRepaintOnSceneChange = true;
        editorWindow.titleContent = titleContent;
        editorWindow.Show();
    }

    void OnEnable()
    {
        if (temp == null)
            temp = new GameObject { hideFlags = HideFlags.HideAndDontSave };
        if (customEditorGUITestScript == null)
            customEditorGUITestScript = temp.AddComponent<CustomEditorGUITestScript>();
        if (customEditorGUITestScriptEditor == null)
            customEditorGUITestScriptEditor = Editor.CreateEditor(customEditorGUITestScript);
    }

    void OnDestroy()
    {
        DestroyImmediate(temp);
    }

    void OnGUI()
    {
        scrollPos = EditorGUILayout.BeginScrollView(scrollPos);
        customEditorGUITestScriptEditor.OnInspectorGUI();
        EditorGUILayout.Space();
        EditorGUILayout.Space();
        EditorGUILayout.Space();
        EditorGUILayout.EndScrollView();
    }
}

Yes, that is an exceedingly long class name. It was a temporary editor window used only for testing. A few key points:

Other Code

I noticed I was getting some null reference errors in my OnEnable functions of the child editors with the editor window after the code recompiled. Adding if(target == null) return; to the beginning of the editors used solved the issue. All the editors still worked fine afterwards. I believe this is a clean up issue.

What’s with the Temp?

MonoBehaviours are meant to be attached to GameObjects. They’re supposed to be init’d by adding them as a component. This creates a temporary GameObject that’s hidden and attaches the scripts to it.

Why DestroyImmediate?

GameObjects with HideFlags.HideAndDontSave set are not automatically destroyed. I have tested it. Without checking for null and destroying the previous version, you’d create a new GameObject each time you opened the EditorWindow. The objects will be destroyed when you close Unity, so it isn’t a huge problem, but it’s definitely worth the extra lines to take care of it properly.

Why All the Extra Spaces

Not 100% sure on that one, but I noticed that in my specific case the last variable was cut off and I couldn’t scroll down to it. The extra spaces created a buffer that allowed scrolling down to the final property.

Why *Something Else*?

The rest is fairly simple. We create an editor window in a fairly standard way, create references to our script and an editor for that script, and finally we call the OnInspectorGUI of that editor during our EditorWindow OnGUI. The scrollview is necessary as a scroll won’t be automatically created if our variables exceed the available space.

Summary

That’s all folks! I hope this saved you a bunch of time. It certainly helped me! (Although it did cost a bit of time figuring out how to do it properly.) Don’t forget to grab the scripts to create a replacement PropertyField function.

Published inCode ExamplesDevelopment Tips

Be First to Comment

    Leave a Reply

    Your email address will not be published. Required fields are marked *