Brief Introduction to Custom Editors in Unity

Brief intoduction to extending unity's editor with custom editor scripts

Unity has the ability to extend the editor with custom editor scripts on a per monobehavior class basis. This is just a quick intro into writing an editor script that gives the option to save/load an object to a text file from the inspector in unity.

Example Editor Inspector

Classes

We will need a total of 3 classes to complete this example:
1. Item Monobehavior class that we want to have a custom editor for.
2. ItemEditor class that will control the custom editor and Save/Load the object data.
3. ItemViewerWindow class to create a window that displays our saved data.

Item Class

For this example we are going to make an item script that has two attributes name and cost.

using UnityEngine;  
using System.Collections;

public class Item : MonoBehaviour {  
    public string name;
    public int cost;

    //  Method to Serialize the object for saving to files. Should probably be JSON or XML in a real use case
    public string ToString(){
        return name+","+cost;
    }

    //  Method to consume serialized Data
    public void ConsumeData(string serialized){
        name = serialized.Split(',')[0];
        cost = int.Parse(serialized.Split(',')[1]);
    }
}

The ToString() and ConsumeData() methods are there so that we can set and retrieve object data in a format that can be saved to a file. For the sake of simplicity I choose to use plain strings as the format, in real usage you should save the attributes in a more standard format like JSON, XML, YAML, etc.

ItemEditor Class

The ItemEditor Class is the custom editor script that controls how the Item MonoBehavior will be exposed to the Editor.

using UnityEngine;  
using System.Collections;

//Required for Editor scripts
using UnityEditor;

//Used for File IO
using System.IO;

[CustomEditor(typeof(Item))]
public class ItemEditor : Editor {

    private string name;

    //Override Inspector GUI to Render custom editor
    public override void OnInspectorGUI(){  
        Item item = (Item) target;
        Rect box = EditorGUILayout.BeginVertical(GUILayout.Height(200));

        //Shows Default fields for editing
        DrawDefaultInspector ();    

        //Draw a Button to Save the Object to a File
        if(GUI.Button(new Rect(box.x,box.y+60,100,40),"SaveToFile")){

            //Set the object to dirty to make sure attributes asjusted by scripts are preserved on the object
            EditorUtility.SetDirty(target);
            Save(item.ToString());
        }

        //Draw a Button to Read from file
        if(GUI.Button(new Rect(box.x,box.y+120,100,40),"ReadFromFile")){
            //Have object load its attributes from the file
            item.ConsumeData(Load());

            //Mark as dirty so it is reflected in the inspector
            EditorUtility.SetDirty(target);
        }


        EditorGUILayout.EndVertical();
    }

    //Load Obejct data from file
    public string Load(){
        return File.ReadAllText("Assets/Resources/Data.txt");
    }

    public void Save(string data){
        /*
            Write File out using standard C# file IO because writing isn't supported by unity's Textasset class
            Saved file must have .txt extension because Unity...
        */
        File.WriteAllText("Assets/Resources/Data.txt", data);
        //Call this to tell unity to reimport updated assets
        AssetDatabase.Refresh();
    }
}

To make a script act as a custom editor for a Monobehavior, it must have the
[CustomEditor(typeof(ClassName))] attribute, extend the Editor class, and also be placed in the Assets/Editor folder. For this example, we override the
OnInspectorGUI() function which gives access to a variable target that is the current object being edited and also allows the custom GUI to be drawn in it.

I won't go into much detail for the GUI drawing other than that to draw the default inspector values for the object you can call the DrawDefaultInspector (); method and from within the OnInspectorGUI method you can call any normal GUI functions and access/modify the object information via the target variable. For writing to files you must use the standard C# System.IO; libraries because unity does not allow writing to files via the TextAsset class.

If you drag the item class onto a gameobject and view it in the inspector, you should see the normal Monobehavior attributes along with a Save/Load buttons. You can see it update the attributes from the file by saving the item data, modifying it in the inspector, and reading it back.

ItemViewWindow Class

The ItemViewWindow Class is a simple class that will open a window to show the file data. You can open it by going to Window->Item Viewer. It fairly self explanatory as it just reads and shows the data from the file. You can go on to use the full set of GUI functions in it similar to the Item Editor, but it is not tied to a particular Class.

using UnityEngine;  
using System.Collections;

//Required for Editor scripts
using UnityEditor;

//Used for File IO
using System.IO;

//Makes a Window Menu to View The Item Data Go to Window->Item Viewer to open it
public class ItemViewerWindow : EditorWindow {  
    //Attribute to Make this a menu item
    [MenuItem("Window/Item Viewer")]
    public static void ShowWindow()
    {
        //Create instance of window
        EditorWindow.GetWindow(typeof(ItemViewerWindow));
    }

    void OnGUI()
    {
          Rect box = EditorGUILayout.BeginVertical(GUILayout.Height(200));
        GUILayout.Label ("FileData", EditorStyles.boldLabel);

        //Show File data as Label
        GUILayout.Label(File.ReadAllText("Assets/Resources/Data.txt"));
        EditorGUILayout.EndVertical();
    }
}

Gotchas

There are a few Gotchas to keep in mind when making editor scripts:

  1. All Custom Editor scripts must be in a folder called Editor under the assets directory.
  2. If any attributes have been modified by a script and you want them to be preserved you must call EditorUtility.SetDirty(object); on the object.
  3. If any files are modified via script wether its a Text file or a prefab, you must call
    AssetDatabase.Refresh(); to tell unity to import the updated assets.
  4. Any file you wish to read and write from must have a .txt extension in unity, so no json/xml or anything that makes sense.

Where to go from here

Its not entirley usefull to save and load object data from a custom inspector, however being able to programmatically expose attributes and custom fields through the inspector gives enough flexbility to add a custom processes and fields through the editor.

As an example from my own project, I was able to expose dynamic icon and prefab fields in my Item editor. Using the custom inspector I could inject the icon and Prefab fields with options that were loaded dynamically from files elsewhere. I also was able to add a recipe editor window that acted as a in editor form for adding recipes to my data file. This has the benefit of being able to store recipes in data without ever having to hand edit the JSON file.
Example Use

Files

Source Code

Unity Package

Additional Resources:

Unity's Guide to extending the Editor

Editor Class Reference