I’m trying to set up a custom inspector that lets me choose and expose subtypes of a custom class like how the default inspector does for Serializable classes.
I set up a ZooEnclosure
class with an Animal animalInside
variable. Animal is an abstract base; what I want to do is pick the concrete implementations Giraffe
or Dog
from a drop-down list in ZooEnclosure
’s inspector, and then be able to edit the subclass’ specific variables.
I screwed around with making the Animal classes ScriptableObjects, and with the Serializable and SerializeField attributes, but didn’t manage to get it working; in most cases, values get reset to their defaults on hitting play, and never get saved when trying to make a ZooEnclosure
prefab.
I’ve spent ages looking through similar problems, with no luck. The best solution seems to be to make one big factory class that contains fields for both Giraffe
and Dog
and let it instantiate the selected subtype, and make a custom inspector that hides fields from the subtype that isn’t selected.
That probably isn’t a bad solution, but I definitely want to know whether this can work without having to make a monolothic class to handle it; if anyone can shed some light on this, I’d really appreciate it.
My current code is:
ZooEnclosure (this version only exposes the age
field of Animal
, but I haven’t managed to get that to save):
using UnityEngine;
public enum AnimalType {Giraffe, Dog}
public class ZooEnclosure : MonoBehaviour {
public float height = 100f;
public float width = 100f;
public AnimalType animalType;
public Animal animalInside = new Dog();
}
ZooEnclosure’s custom editor:
using UnityEngine;
using UnityEditor;
using System;
using System.Reflection;
[CustomEditor(typeof(ZooEnclosure))]
public class ZooEnclosureEditor : Editor {
public override void OnInspectorGUI() {
ZooEnclosure script = (ZooEnclosure) target;
DrawDefaultInspector();
if (script.animalType == AnimalType.Dog && script.animalInside.GetType() != typeof(Dog)) {
script.animalInside = new Dog();
} else if (script.animalType == AnimalType.Giraffe && script.animalInside.GetType() != typeof(Giraffe)) {
script.animalInside = new Giraffe();
}
EditorGUILayout.Space();
DrawCustomFields(script);
}
private void DrawCustomFields(ZooEnclosure script) {
script.animalInside.age = EditorGUILayout.FloatField("Age: ", script.animalInside.age);
EditorUtility.SetDirty(script);
}
}
And the Animal classes:
public abstract class Animal {
public string animalName = "Default Animal Name";
public float age = 0f;
}
public class Giraffe : Animal {
public float neckHeight = 10f;
public Giraffe() { }
}
public class Dog : Animal {
public float barkLoudness = 200f;
public Dog() { }
}