How to expose a field of type Interface in the inspector?

Hi,

I've found this post:

http://forum.unity3d.com/threads/49524-Exposing-fields-with-Interface-type-%28C-%29-SOLVED

but I must be missing something since I get errors!

I have an interface ISwitchableObject. Objects like lamps implement the interace. Then I have a Switch object that has a reference to an instance of an ISwitchableObject like this:

public class Switch : MonoBehaviour 
{

public ISwitchableObject _switchable;

}

I want to expose the _switchable field in the inspector so I can drag drop lamps and such to it. I've tried doing the below but it gives me errors (says I have invalid arguments). What am I doing wrong?

Thanks

[CustomEditor (typeof (Switch))]
public class SwitchInspector : Editor
{
   void OnInspectorGUI ()
   {
      Switch s = (Switch)target;
      s._switchable = EditorGUILayout.ObjectField ("switchable", s._switchable, typeof (ISwitchableObject));
      DrawDefaultInspector ();
   }
}

This might be irrelevant by now but jsut if someone still have the same issues:

Just add the Attributes:

[SerializeField, SerializeReference]
private IState currentState;  

That just worked for me, Cheers!
I hope this will help someone.
@mat

Unity, by itself, does not expose fields that are of an interface type. It is possible to manually enable this functionality by implementing a custom inspector each time as Mike 3 has pointed out, but even then the reference would not be serialized (“remembered” between sessions and entering/exiting playmode).

It is possible however to create a serializable container object that wraps around a Component field (which is serialized) and casts to the desired interface type through a generic property. And with the introduction of custom property drawers into Unity, you can effectively expose a serialized interface field in your scripts without having to write a custom inspector / property drawer each time.

I’ve taken this approach and released an asset that does exactly that here, for anyone who’s interested.

I’ve answered this in-depth here: (SOLUTION) How to serialize interfaces, generics, auto-properties and abstract System.Object classes? - Questions & Answers - Unity Discussions

Checkout ShowEmAll: Serialize interfaces, generics, auto-properties, dictionaries, abstracts, etc.

One way to do it is using a Component variable for what is displayed in the inspector, and a second variable of the interface that is set using the first on Start or Awake. Using the OnValidate method, you can have it check if the Component variable is of the interface, i.e. if (component is interface). If it fails that, you have it set the component to null.
Example

public Component movement;
    protected iMovement motor;
    protected void OnValidate()
    {
    	if (!(movement is iMovement))
    		movement = null;
    }
    protected override void Awake()
    {
    	if (movement != null)
    		motor = (iMovement)movement;
    }

This is not a perfect solution. You have to set the component to the exact component you want to use.

My simple realization with containers:

    [Serializable]
    public abstract class ATypedContainer
    {
        [CanBeNull]
        [SerializeField]
        protected MonoBehaviour obj = default;

        public void Validate()
        {
            OnValidate();
        }

        protected abstract void OnValidate();
    }

    [Serializable]
    public class TypedContainer<T> : ATypedContainer where T : class
    {
        [CanBeNull]
        public T Value => obj as T;

        protected override void OnValidate()
        {
            if (Value == null)
                obj = null;
        }
    }

    [CustomPropertyDrawer(typeof(ATypedContainer), true)]
    public class TypedContainerClassDrawer : PropertyDrawer
    {
        const string OBJ_FIELD_NAME = "obj";

        public override void OnGUI(Rect position, SerializedProperty property, GUIContent label)
        {
            var tc = fieldInfo.GetValue(property.serializedObject.targetObject) as ATypedContainer;
            tc?.Validate();

            var objProp = property.FindPropertyRelative(OBJ_FIELD_NAME);
            if (objProp == null)
                throw new InvalidCastException($"Can't find {OBJ_FIELD_NAME} field in {property.type}");

            EditorGUI.PropertyField(position, objProp, label);
        }
    }


    //TEST

    public interface ITest {}

    //some implementation b/c unity don't like generics
    [Serializable]
    public class TestContainer : TypedContainer<ITest> {}

    pubic class TestImpl : MonoBehaviour, ITest
    {
          //something
    }

    pubic class OwnerMB : MonoBehaviour
    {
         public TestContainer test;

         void Start() 
         {
                Debug.Log(test);
         }
    }

You can try to put any object in this field, but only valid is apply

A bit of a hacky solution but you can make a serialized field of type game object, then use OnValidate to make sure it’s the right interface type

[SerializeField] GameObject iSomeInterfaceGameObject = default; 
ISomeInterface someInterface;

void OnValidate() {
    if (iSomeInterfaceGameObject != null) {
        ISomeInterface checkImplementsInterface = iSomeInterfaceGameObject.GetComponent< ISomeInterface >();
        if (checkImplementsInterface == null) 
            Debug.LogError($"Object does not implement the interface {typeof(ISomeInterface)}", this);
    }
}

void Start() {
       someInterface = iSomeInterfaceGameObject.GetComponent< ISomeInterface >();
}

I don't think there is any way to do it by interface alone (I've run through all the methods involving generics and the like in my head and you'd end up making a custom inspector for each type that way) - you can't pass an interface to something requiring a UnityEngine.Object

The only thing I can think of is making an abstract base class which inherits from both UnityEngine.Object and ISwitchableObject. This then lets you add either monobehaviour scripts, or very basic classes which aren't attached to gameobjects

Note:

The solution on the page doesn't compile, with the same error you're getting.

There’s an answer that no one has suggested yet:

Instead of declaring an interface like so

public interface IDoable {
    void DoIt();
}
public class Doer : MonoBehaviour, IDoable {
    public void DoIt();
}

public class DoableContainer : MonoBehaviour {
    public IDoable doable;
}

just do

public abstract class ADoable : MonoBehaviour{
    virtual void DoIt();
}
public class Doer : ADoable {
    public void DoIt();
}

public class DoableContainer : MonoBehaviour {
    public ADoable doable;
}

Your Doer can only implement one “interface”, and all your implementation of Doable have to inherit from the same class (here MonoBehaviour), but it works, is displayed, and is saved with little additional cost;

You can drag and drop your gameobject (gobj) and then use gobj.GetComponent in Awake,Start,OnEnable

Also not a perfect solution, but may be useful to someone.

You can add a [RequireComponent(typeof(MyInterface))] to your component and then get the component on Start:

[RequireComponent(typeof(MyInterface))]
public class Example : MonoBehaviour
{
    private MyInterface interface;

    void Start()
    {
        interface = GetComponent<MyInterface>();
    }
}

This way you somewhat ensure that some component will implement the interface.


Although this solution is not good if you have multiple components implementing this interface on the same Game Object. But in my opinion it is better than just simply getting the component on Start, gives a bit more context on what the component requires and what it does.

Most convenient way is to use type selectors in the editor. Either use Odin inspector, or Tri-Inspector or write your own attribute for it