Assigning Interface in Unity Inspector

How many times were you interested in assigning Interfaces into the Inspector in Unity? I was pretty often, but Unity doesn’t support that by default.

So if we can program, why don’t we add that by ourselves?

Ready? πŸ€“

Let’s start with the problem 😀

As I already mentioned, Unity doesn’t support displaying C# interfaces in the Inspector. They probably have a good reason for that, but at some point, you fall in love with them, and you want to have them everywhere! πŸ”₯

Unfortunately, we can’t just create a field with interface type…

using UnityEngine;

public class ExampleScript : MonoBehaviour
{
    public IExampleInterface interfaceReference;
}
You can’t assign a reference to the interface

Workaround πŸ› 

So because of that, we need to use what Unity is giving us (serialized fields) and make them work as interfaces.

First of all, the base class for all of the objects that can be referenced is “Object” class. We will use it as a base for our references in the Inspector.

using UnityEngine;

public class ExampleScript : MonoBehaviour
{
    public Object objectReference;
}
You can assign Unity Objects in Inspector!

Interfaces πŸ‘¨β€πŸ’»

To make this idea work, we need three things: Interface, Attribute, and Drawer.

Let’s start with the Interface.

/// <summary>
/// Empty interface to mark valid reference.
/// </summary>
public interface IExampleInterface { }

Yeah, I know it’s pretty empty, but for our example purpose, it’s sufficient.

Next on our list is the Attribute. Recently, I made a post about creating ReadOnly attribute if you’re interested.

using UnityEngine;

/// <summary>
/// Attribute that require implementation of the provided interface.
/// </summary>
public class RequireInterfaceAttribute : PropertyAttribute
{
    // Interface type.
    public System.Type requiredType { get; private set; }

    /// <summary>
    /// Requiring implementation of the <see cref="T:RequireInterfaceAttribute"/> interface.
    /// </summary>
    /// <param name="type">Interface type.</param>
    public RequireInterfaceAttribute(System.Type type)
    {
        this.requiredType = type;
    }
}

This attribute will have a type of Interface that it’s looking for.

And finally, we can get to the Drawer. This one will be a little bit more complex.

using UnityEngine;
using UnityEditor;
using System.Linq;

/// <summary>
/// Drawer for the RequireInterface attribute.
/// </summary>
[CustomPropertyDrawer(typeof(RequireInterfaceAttribute))]
public class RequireInterfaceDrawer : PropertyDrawer
{
    /// <summary>
    /// Overrides GUI drawing for the attribute.
    /// </summary>
    /// <param name="position">Position.</param>
    /// <param name="property">Property.</param>
    /// <param name="label">Label.</param>
    public override void OnGUI(Rect position, SerializedProperty property, GUIContent label)
    {
        // Check if this is reference type property.
        if (property.propertyType == SerializedPropertyType.ObjectReference)
        {
            // Get attribute parameters.
            var requiredAttribute = this.attribute as RequireInterfaceAttribute;

            // Begin drawing property field.
            EditorGUI.BeginProperty(position, label, property);

            // Draw property field.
            property.objectReferenceValue = EditorGUI.ObjectField(position, label, property.objectReferenceValue, requiredAttribute.requiredType, true);

            // Finish drawing property field.
            EditorGUI.EndProperty();
        }
        else
        {
            // If field is not reference, show error message.
            // Save previous color and change GUI to red.
            var previousColor = GUI.color;
            GUI.color = Color.red;

            // Display label with error message.
            EditorGUI.LabelField(position, label, new GUIContent("Property is not a reference type"));

            // Revert color change.
            GUI.color = previousColor;
        }
    }
}

Our Attribute can only work with the reference type of properties. Otherwise, we will display a small error message in the Inspector.

Great! Now I think we have everything that we needed to make it work!

Example πŸ†

So let’s get to the example of how to use our Attribute and Interface!

We basically have two options.

First, a simple one with the attribute and a reference.

using UnityEngine;

/// <summary>
/// This class contain example how to use RequireInterface attribute.
/// </summary>
public class ExampleScript : MonoBehaviour
{
    /// <summary>
    /// Working example of the RequireInterface attribute.
    /// </summary>
    [RequireInterface(typeof(IExampleInterface))]
    public Object simpleExample;
}

Second, with private reference and the property with the type of our Interface.

using UnityEngine;

/// <summary>
/// This class contain example how to use RequireInterface attribute.
/// </summary>
public class ExampleScript : MonoBehaviour
{
    /// <summary>
    /// Working example of the RequireInterface attribute.
    /// </summary>
    [SerializeField]
    [RequireInterface(typeof(IExampleInterface))]
    private Object _complexExample;
    public IExampleInterface ComplexExample => _complexExample as IExampleInterface;
}

The choice is yours! 😎

But with this approach, you can assign components and scriptable objects!

Proof in action!

If you find this useful, let me know in the comment section below or share it with your friend! I would really appreciate that! ❀️

And if you are interested in getting emails when I release new posts sign up for the newsletter!

You can also check the whole project at my public repository. πŸ”—

See you next time! πŸ€“

avatar
1 Comment threads
1 Thread replies
1 Followers
 
Most reacted comment
Hottest comment thread
2 Comment authors
Patryk GalachMiyavi Recent comment authors
  Subscribe  
newest oldest most voted
Notify of
Miyavi
Guest
Miyavi

Hi! I’m testing this with Unity 2019.3.0f6 (Personal) and it literally won’t let me assign anything to the public interfaces Objects in the inspector.

I’ve tried making my own test interface, but it’s the very same thing.

I see the reference in the Example GameObject to the ValidReference script correctly assigned, but as soon as I detach it, I’m unable to assign it again. The same happens if I try to reassign it even when it’s already assigned.

PS: This is from the downloaded repo from Bitbucket.