Search code examples
c#unity-game-engineunity-editor

Can I use an abstract base class as a Unity Editor element?


I'm trying to create a component for a Unity GameObject, let's call it MediaController. I want it to be able to manage timing (play/pause/etc) for different media (audio/video). I created an abstract class PlayableMedia with basic properties/fields/methods and created 2 classes, PlayableVideo and PlayableAudio, that inherit and implement according to our needs.

The intent was to have a singular list of PlayableMedia that could be audio/video agnostic, allowing an easy (i.e.) media.Play() call regardless of type at specific app times... but my field public List<PlayableMedia> MediaList; is not appearing in the editor and there is no error.

So ultimately my question is as the title states: is it possible to use the PlayableMedia class as the type of a field?

I'm suspecting "no" based on my experiences with this, but I've found links that say "yes" or "yes, sort of" that seem to point to custom editors/inspectors/drawers, but I have 0 experience with those and haven't been able to get it implemented (see below).

[System.Serializable]
public class RegisteredMedia
{
    public float StartTime;
    public PlayableMedia Media;
}

[CustomPropertyDrawer(typeof(RegisteredMedia))]
class RegisteredMediaDrawer : PropertyDrawer
{
    public override void OnGUI(Rect position, SerializedProperty property, GUIContent label)
    {
        EditorGUI.BeginProperty(position, label, property);

        position = EditorGUI.PrefixLabel(position, GUIUtility.GetControlID(FocusType.Passive), new GUIContent("Playable Media"));

        var indent = EditorGUI.indentLevel;
        EditorGUI.indentLevel = 0;

        Rect rectStartTime = new Rect(position.x, position.y, 30, position.height);
        Rect rectMedia = new Rect(position.x + 35, position.y, 50, position.height);

        EditorGUI.PropertyField(rectStartTime, property.FindPropertyRelative("StartTime"), GUIContent.none);
        EditorGUI.PropertyField(rectMedia, property.FindPropertyRelative("Media"), GUIContent.none);

        EditorGUI.indentLevel = indent;

        EditorGUI.EndProperty();
    }
}

public class MediaController : MonoBehaviour
{
    public List<RegisteredMedia> MediaList = new List<RegisteredMedia>();

    \\[...] rest of implementation
}

Can anyone help me out? Either confirm that it isn't possible, or help me with an implementation if it is?

Also, if it can be done with custom editors/inspectors/drawers, can someone help me get a single item in the List<RegisteredMedia> to display as Start Time ____ Playable Media [=====] (where PlayableMedia will be a GameObject with the proper component attached)?


Solution

  • Be careful of your use of the word "property". In C# it means something very specific.

    is it possible to use the PlayableMedia class as the type of a property?

    I think you are asking the wrong question here. Rather than coming up with a new implementation, consider why your current implementation might not be working?

    Firstly, I'll give you the following example:

    public abstract class Car : MonoBehaviour { }
    
    public class Jeep : Car { }
    
    public class Ferrari : Car { }
    
    public class CarHolder : MonoBehaviour
    {
        public List<Car> Cars;
    }
    

    In this example, I could create a GameObject with the CarHolder component, and was able to attach both Jeep and Ferrari Objects. It is important to note that each monoBehavior class I defined sits in its own file and the file name matches the class name. This is just how Unity works.

    So to answer the question I think you are asking (assuming we replace "property" with "field"), it is indeed possible to use abstract class types and have them show up in the inspector. I suspect that you need to separate your classes into separate files.