How It Works
Unity does not natively serialize interface fields. Unity Object Reference works around this limitation using two main methods:
- Serializable UnityObjectReference<T> type, which stores a
UnityEngine.Objectreference that Unity can serialize- A custom drawerer is then used so you can set this in the inspector with validation the object implements the interface.
- ILPostProcessing to transform supported fields after compilation, so you dont need to manually change all your fields to use
UnityObjectReference<T>
This lets you write normal C# interfaces in your source code while the generated assembly uses a serializable UnityObjectReference<T> behind the scenes.
Source code
Section titled “Source code”You can declare an interface field normally:
[SerializeField]private IPlayerCueInputProvider _inputProvider;From your source code’s perspective, this is a regular interface field. You can access it directly:
_inputProvider.DoSomething();ILPostProcessing
Section titled “ILPostProcessing”During the ILPostProcessing step, the field is transformed into a UnityObjectReference<T>.
Conceptually, this:
[SerializeField]private IPlayerCueInputProvider _inputProvider;becomes:
[SerializeField]private UnityObjectReference<IPlayerCueInputProvider> _inputProvider;The transformation happens in the compiled assembly, so your source code does not need to explicitly use the wrapper.
Accessing the field
Section titled “Accessing the field”Field accesses are transformed as well.
An access such as:
_inputProvider.DoSomething();is rewritten to access the wrapped value:
_inputProvider.Value.DoSomething();This means the generated code interacts with the actual interface implementation while Unity serializes the UnityObjectReference<T>.
UnityObjectReference<T>
Section titled “UnityObjectReference<T>”UnityObjectReference<T> stores a reference to a UnityEngine.Object rather than attempting to serialize the interface itself.
When .Value is accessed, the stored Unity object is cast to the requested type:
public T Value => _object as T;This provides the bridge between Unity’s serialization system and normal C# interfaces.
The wrapper can therefore serialize objects such as:
MonoBehaviourcomponents.ScriptableObjectassets.- Other
UnityEngine.Objectinstances compatible withT.
Inspector integration
Section titled “Inspector integration”A custom property drawer provides the Inspector experience for UnityObjectReference<T>.
It supports dragging compatible Unity objects directly onto the field.
For example, if the field is:
[SerializeField]private IPlayerCueInputProvider _inputProvider;you can drag a component implementing IPlayerCueInputProvider onto it.
GameObject references
Section titled “GameObject references”You can also drag a GameObject onto the field.
When this happens, the drawer searches the GameObject for a component matching the required type and uses the first matching component it finds.
This allows the Inspector to work naturally with both components and their containing GameObjects.
Validation
Section titled “Validation”The drawer also validates assigned objects against the requested type.
If an object cannot provide the required interface, the Inspector displays a warning rather than silently accepting an invalid reference.