A wrapper that allows you to serialize interfaces. Both UnityEngine.Object and regular object implementers work!
- The package is available on the openupm registry. You can install it via openupm-cli.
openupm add net.tnrd.serializableinterface
- Installing through a Unity Package created by the Package Installer Creator from Needle
Usage is pretty easy and straightforward. Assuming you have the following interface
public interface IMyInterface
{
void Greet();
}You can add it to a behaviour like so
using TNRD;
using UnityEngine;
public class MyBehaviour : MonoBehaviour
{
[SerializeField] private SerializableInterface<IMyInterface> mySerializableInterface;
private void Awake()
{
mySerializableInterface.Value.Greet();
}
}Back in the Unity inspector it will look like this
And when you click on the object selector button you will be shown a dropdown window with all possible options like this
As you can see you can select items from multiple locations:
- Assets (Scriptable Objects and Prefabs that implement said interface)
- Classes (custom classes that implement said interface)
- Scene (objects in the scene that implement said interface)
For the sake of example for the image above I have created some simple implementations
using UnityEngine;
public class MyComponent : MonoBehaviour, IMyInterface
{
/// <inheritdoc />
public void Greet()
{
Debug.Log("Hello, World! I'm MyComponent");
}
}using UnityEngine;
public class MyPoco : IMyInterface
{
/// <inheritdoc />
public void Greet()
{
Debug.Log("Hello, World! I'm MyPoco");
}
}using UnityEngine;
[CreateAssetMenu(menuName = "MyScriptable")]
public class MyScriptable : ScriptableObject, IMyInterface
{
/// <inheritdoc />
public void Greet()
{
Debug.Log("Hello, World! I'm MyScriptable");
}
}The OnlyShow attribute lets you choose which type of objects should be displayed in the dropdown:
[OnlyShow(ObjectsThatAre.Classes)]
[SerializeField] private SerializableInterface<IMyInterface> pureCsharpClassesOnly;
[OnlyShow(ObjectsThatAre.SceneComponents)]
[SerializeField] private SerializableInterface<IMyInterface> sceneComponentsOnly;
[OnlyShow(ObjectsThatAre.SceneComponents | ObjectsThatAre.Assets)]
[SerializeField] private SerializableInterface<IMyInterface> mixedObjects;By default, pure C# classes are organised based on the namespace they're declared in.
The Label attribute on pure C# class declaration lets you customize the path to the dropdown item.
namespace A.Very.Long.Namespace
{
// Will appear at the root of the dropdown picker
[Label(nameof(MyPoco))]
public class MyPoco : IMyInterface { }
}
// Knight item will appear under the "Classes/Physical" folder
[Label("Physical/" + nameof(Knight))]
public class Knight : IMyInterface { }
// Rogue item will appear under the "Classes/Physical" folder
[Label("Physical/" + nameof(Rogue))]
public class Rogue : IMyInterface { }
// Wizard item will appear under the "Classes/Magical" folder
[Label("Magical/" + nameof(Wizard))]
public class Wizard : IMyInterface { }
// Necromancer item will appear under the "Classes/Magical" folder
[Label("Magical/" + nameof(Necromancer))]
public class Necromancer : IMyInterface { }The ReferencesSource attribute lets you specify a function from which references must be fetched.
This allows you to:
- reference existing C# instances from somewhere else,
- customise how a pure C# instance should be constructed,
- have more control over which objects can be referenced
Note that the reference will be COPIED if it comes from another "host object" (MonoBehaviour, ScriptableObject, or other UnityEngine class). See documentation of SerializeReference.
public class MyMonoBehaviour : MonoBehaviour
{
[ReferencesSource(nameof(GetReferences))]
public SerializableInterface<IMyInterface> implementations;
private IEnumerable<Reference> GetReferences(UnityEngine.Object serializedObject)
{
Interface[] components = (serializedObject as MyMonoBehaviour).GetComponents<Interface>();
for (int i = 0 ; i < components.Length ; i++)
{
yield return new Reference(components[i], $"Components/{i}) {components[i].GetType().Name}");
}
yield return new Reference(new ComplexImplementationRequiringDependencies("foo", 5, this), "Classes/Complex");
...
}
}Serializable Interface is a small and open-source utility that I hope helps other people. It is by no means necessary but if you feel generous you can support the original author Thundernerd by donating.
Pull requests are welcomed. Please feel free to fix any issues you find, or add new features.

