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; }
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; }
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!
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! ?
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.
You should be able, but you can’t drag GameObject there. You have to lock one Inspector window with the ExampleScript and open a second Inspector window with the object which has ValidReferenceScript. To assign the reference you have to drag the component to the field. For this field to be able to process GameObjects and get the component automatically you will have to change RequireInterfaceDrawer to draw ObjectField for an Object type. Using objectReferenceValue you will have to check if the reference is type of GameObject and then use GetComponent to get the reference. Assign it to the objectReferenceValue and… Read more »
Hi Patryk, would you be able to give an example of this or possibly update the post (since it may be a common question)? I’ve been digging around with this for a bit and so far haven’t managed any progress (due to my semi-limited understanding of types).
I was able to get it to work by modifying the following code:
to this:
Thank you for that. I’ve notice that after this change I couldn’t select scripts on the same GameObject so I’ve used both options (not sure how to post code):
You are a god, this worked for me!
this worked for me:
In Unity 2022 reference is not null but game object. I am uncertain if is only me, or constant, but it was easy to bug fix. Working version:
Where did you add this code?
Is there a way to use this in a stand-alone build? The ReqiredInterface is dependent on the UnityEditor being loaded.
I think it could be possible, but you would need to adapt it for your custom inspector.
I figured it out shortly after commenting, but with the comment approval system I couldn’t see how to edit my pending comment.
I’d just made the mistake of putting all of the files in game project, when actually the RequiredInterfaceDrawer file needs to be in the editor project.
Great article thanks.
Any idea on how to handle list fields?
Hi, thanks a lot for this, super useful in what I’m doing. I would like to ask, how would you specify the “set” parameter in the property for the private declaration in the example? I’m currently using a separate method in order to set the value of _complexExample from an external script.
Thanks!
You don’t need to have a separate method for it. You can make properties work as getters and as setters. It would be something like that:
public IExampleInterface ComplexExample { get { return _complexExample as IExampleInterface; } set { _complexExample = value as IExampleInterface; }
Just get my love♥
That’s amazing, it works beautifully!
I’ve been looking a lot for a solution like this one, thanks!
Do you know how I could modify the attribute and drawer code to make them work with arrays/lists of interfaces?
Cheers!
Hello! I have a question: what should happen in order to display the message “Property is not a reference type”?
I was unable to reproduce this use case…
It is wonderful! I don’t know how but it works for List<> Thanks a lot!