I ran into another issue with [SerializeReference]. If I have such a field on a component that’s used in a prefab, and I then change the type inside the that field to something else in a prefab instance (either as part of another prefab, or in the scene itself), the new type doesn’t actually get saved. I think this is because Unity’s serialisation format doesn’t actually have a way to specify type overrides yet.
Is this a known issue? Are there any known workarounds?
With all these teething issues, I’m probably going to go back to [SerializeField], ISerializationCallbackReceiver and type-indicating enums for now. The new feature to serialise references is really powerful, but doesn’t quite feel like a first-class citizen in Unity just yet.
@LeonhardP can you say if there are any plans to include a default property renderer for [SerializedReference] fields? (I’m imagining something that just gives you a dropdown for all types derived from field’s type and then just uses the normal property rendering for the actual value.)
Re: default property renderer, there are no plans at the moment.
Re: overriding type in a prefab instance,
unfortunately this isn’t possible due to how prefab instances store modifications (path/value). It’s a known limitation but is currently undocumented. We’ll include a note in the docs.
Thanks for the quick response. That’s unfortunate. The lack of a default renderer isn’t a huge issue, since it’s not too hard to write one yourself, but not being able to actually change the type of a field is a pretty big deal breaker for a feature that’s supposed to enable polymorphic serialisation.
This is my setup:
The interface and the implementation:
using System.Collections;
using System.Collections.Generic;
using UnityEngine;
public interface IBullet
{
void Fire();
}
public class MachineGunBullet : IBullet
{
[SerializeField] string aName = "MachineGun bullet";
void IBullet.Fire()
{
}
}
public class AK47Bullet : IBullet
{
[SerializeField] string aName = "AK47 bullet";
[SerializeField] GameObject particle;
void IBullet.Fire()
{
}
}
public class ShotGunBullet : IBullet
{
[SerializeField] string aName = "ShotGun bullet";
[SerializeField] AudioClip shotSound;
void IBullet.Fire()
{
}
}
And the component:
using System.Collections;
using System.Collections.Generic;
using UnityEngine;
public class Shooter : MonoBehaviour
{
[SerializeReference] [SerializeReferenceButton] IBullet mainBullet;
[SerializeReference] [SerializeReferenceButton] List<IBullet> bullets;
// Start is called before the first frame update
void Start()
{
}
// Update is called once per frame
void Update()
{
}
}
The ‘[SerializeReferenceButton]’ comes from a plugin developed by @TextusGames . Basically it creates a button beside the field which lets you to create and add an implementation of the interface from a dropdown list of all available implementation.
The shooter is attached to a gameobject which is a prefab. The default implementation of ‘mainBullet’ in shooter is ‘ShotGunBullet’. If I drag the prefab into scene and modify ‘mainBullet’ to ‘AK47Bullet’ from ‘ShotGunBullet’, I am able to do so. Then if I press play and enter playmode, the field ‘mainBullet’'s data does not get lost or revert into something. Even I can drag the prefab into another prefab and yet no data loss. Is the issue fixed? I am confused.