I spend over 8 hours on this already and have no idea how to do this. I will use animals as examples.
I have a scriptable object Person with a field Animal pet and I’m adding a Dog or a Cat to the field. I want to be able to edit the animal in the inspector.
using UnityEditor;
using UnityEngine;
[CustomPropertyDrawer(typeof(Animal))]
public class AnimalDrawer : PropertyDrawer
{
public override void OnGUI(Rect position, SerializedProperty property, GUIContent label)
{
Animal myProperty = fieldInfo.GetValue(property.serializedObject.targetObject) as Animal;
if (myProperty is Dog)
{
EditorGUILayout.ObjectField(myProperty as Dog); // This doesn't work
}
if (myProperty is Cat)
{
EditorGUILayout.PropertyField(myProperty as Cat); // This doesn't work
}
}
}
[Serializable] public class Animal{
public string fullName;
public void Eat(){}
public void Sleep(){}
}
[Serializable] public class Dog : Animal {
public bool bites;
public Color favouritaBallColor;
}
[Serializable] public class Cat : Animal {
public bool scratches;
public int birdsCatched;
}
public class Person : ScriptableObject{
Animal favoritePet;
Animal otherPet;
// ...
}
I add the dog and cat to the person, but I can only see the animal properties in the inspector. Also how to make it work for any child classes?
Then you wasted 8 hours for nothing ^^. Unity does NOT support polymorphism / inheritance for custom serializable classes. Such class fields are always serialized based on the field type. This is well explained in the docs. So when you store a Dog and a Cat in your Animal variable, it would be just an Animal instance after deserialization.
Note that a lot of people get the purpose of a PropertyDrawer wrong. It can not change how things are serialized. You can only change how they are displayed.
It’s possible to use the ISerializationCallbackReceiver interface to perform custom serialization logic. However all you can do with this interface is transferring non serializable data into fields that Unity can serialize. For an example, see ISerializationCallbackReceiver.
If you need polymorphism for serialized objects you would need to derive your base class either from MonoBehaviour or ScriptableObject as they do support inheritance because the actual type is stored with the object and those are also serialized on their own.
First, you’ll need to derive Animal from ScriptableObject, as such: Animal : ScriptableObject
Then, as soon as you’ve done that, you should see that the Person class in the inspector now expects 2 scriptable objects to be referenced. You’ll need to write a custom inspector that simply gives you the ability to insert a certain type of scriptable object into the field.
(Outlined in red is what you’ll need to program in the background.)
Now, you have the ability to assign the person any type of animal, BUT, you still can’t access the fields of that scriptable object since you’re creating it on-demand with the ‘+’ button and not storing it anywhere, thus, Unity couldn’t care less about it.
In order to fix this, you’ll need to implement a property drawer that enables you to view and edit all data of a scriptable object in an inspector. A solution has been kindly provided for us already, refer to: RedDude’s gists · GitHub
P.S. - It’s a ton of work. I’ve asked a similar question a few months back, if it means anything:
Also, I might add one very crucial note: Unity WILL NOT save your reference in the inspector. If you need it saved, you’ll almost certainly either opt-in to create actual scriptable objects in your project and drag and drop them OR convert that data into a .json file and store it somewhere.
FYI, there is also now SerializeReference attribute - I didn’t see it being mentioned:
But the minus side of using this is the fact that you can only add stuff properly from code runtime or using some custom editor - AFAIK. I’ve only used it once or twice so I don’t have any proper opinion about it.