Sponsored By

Featured Blog | This community-written post highlights the best of what the game industry has to offer. Read more like it on the Game Developer Blogs.

Over the course of development, I'm constantly finding cool features of Unity and c# that I never new existed. I've found some that are really useful, so I'm going to start sharing them regularly here.

Mark Wahnish, Blogger

September 4, 2015

4 Min Read

This article was originally posted on our blog, here.

Corrections: commenters Szymon Gatner and Ashkan Saeedi Mazdeh helped me discover this method works on classes derived from Object, not just ScriptableObjects. This greatly expands the usefulness of this trick, in my opinion, because it is now not limited to the use cases for ScriptableObjects. The information below is still valid for ScriptableObjects, and also works for classes derived from Object, simply change the references to ScriptableObject in this post's code samples to Object, and it should work just the same. Thank you both!

Also, Thomas Viktil pointed out that instead of having a separate boolean tracking whether the cachedEditor variable it my ExampleMonobehaviourEditor.cs class needed to be reassigned, I could instead just check for null. D'oh! I've updated the code examples below to reflect this. Thanks Thomas!

 

Over the course of development, I'm constantly finding cool features of Unity and c# that I never new existed. I've found some that are really useful, so I'm going to start sharing them regularly here. This week's requires some prior knowledge. Definitely read up on ScriptableObjects and custom editors if you are unfamiliar. Without further ado:

Making a inspector for any ScriptableObject

Did you know any class deriving from ScriptableObject can be serialized and rendered in a custom Editor with a just a few lines of code? As it turns out, it is really easy:


// Creating an instance of a ScriptableObject
MyScriptableObject so = ScriptableObject.CreateInstance();

// Setting up the editor
Editor myEditor = Editor.CreateEditor(so);

// Drawing the editor
myEditor.OnInspectorGUI();

And that's pretty much (almost) it! Some stuff you should know:

  • In this example, myEditor.OnInspectorGUI() must be called in your EditorWindow or custom Editor's OnInspectorGUI() method.

  • Editor.CreateEditor() only needs to be called once, so you can absolutely call it once and cache the result. However, there is a catch: Editor.CreateEditor() throws an error when you call it from OnEnable. You will have to call it in your editor's OnInspectorGUI() method, and if you want to be efficient you'll have to write some logic to make sure it is only called once.

Below is a full code example. In it, we'll instance a ScriptableObject in a Monobehaviour. We'll then write a custom inspector that will display the variables in the Monobehaviour, including those in the ScriptableObject:

ExampleScriptableObject.cs


using UnityEngine;
using System.Collections;

[System.Serializable]
public class ExampleScriptableObject : ScriptableObject {
    // Some variables we also want displayed in the inspector
    public float scriptableObjectFloat = 42f;
    public int scriptableObjectInt = 42;
}

This will be the ScriptableObject we want to display in the inspector. It has two variables, scriptableObjectFloat and scriptableObjectInt, that will display in the inspector once we get the rest of our code working.

ExampleMonobehaviour.cs


using UnityEngine;
using System.Collections;
public class ExampleMonobehaviour : MonoBehaviour {

    // Example variable that will display in the inspector
    public string exampleMonobehaviourString = "foo";

    /* The scriptable object we want to display in the inspector. Note the
       [HideInInspector] tag; we don't actually need Unity to display the
       ScriptableObject, our code will handle that.*/

    [HideInInspector]
    public ExampleScriptableObject exampleScriptableObject 
         = ScriptableObject.CreateInstance();

}

This is the Monobehaviour that we will build a custom inspector for. It has a variable called "exampleMonobehaviourString" that will be displayed in the inspector alongside the variables contained in the ScriptableObject we made, which is instanced in this class as "exampleScriptableObject".

ExampleMonobehaviourEditor.cs


using UnityEngine;
using UnityEditor;
using System.Collections;

[CustomEditor(typeof(ExampleMonobehaviour))]
public class ExampleMonobehaviourEditor : Editor {

    // We'll cache the editor here
    private Editor cachedEditor;

    public void OnEnable() {
        /* Resetting cachedEditor. This will ensure it is written to
           The next time OnInspectorGUI is called
        */
        cachedEditor = null;
    }


    public override void OnInspectorGUI() {
        // Grabbing the object this inspector is editing.
        ExampleMonobehaviour editedMonobehaviour = (ExampleMonobehaviour)target;

        /* Checking if we need to get our Editor. Calling Editor.CreateEditor() 
           if needed */
        if (cachedEditor == null) {
            cachedEditor = 
                Editor.CreateEditor(editedMonobehaviour.exampleScriptableObject);
        }

        /* We want to show the other variables in our Monobehaviour as well, 
           so we'll call the superclasses' OnInspectorGUI(). Note this could 
           also be accomplished by a call to DrawDefaultInspector() */
        base.OnInspectorGUI();

        //Drawing our ScriptableObjects inspector
        cachedEditor.DrawDefaultInspector();
    }
}

 

Finally, the class that does the work! This displays our ScriptableObject's variables alongside those of ExampleMonobehaviour. It's surprisingly useful! Here's what it looks like in practice:

Very nice, isn’t it! Monobehaviour variables and ScriptableObject variables all displayed neatly in the inspector.Very nice, isn’t it! Monobehaviour variables and ScriptableObject variables all displayed neatly in the inspector.

And that’s it! Leave your questions and comments down below!

Cheers,

-Mark

@mark_at_abxy

Read more about:

Featured Blogs

About the Author(s)

Daily news, dev blogs, and stories from Game Developer straight to your inbox

You May Also Like