Abstract Class in List, only shows base variables

I purposely upgraded to 2020.2 early, because I need the reorderable lists and it is so far the easiest way since.

I have a class called ActionSeries, which holds a property of List. How can I make the reorderable List be able to accept any class that inherits from Action (Click on the little ‘+’ icon, and it gives a drop down or menu of classes that inherit from Action to choose from to add).

Action is not inheriting from Monobehaviour, so it will be editable from the inspector, and later ActionSeries will be a ScriptableObject.

I tried a basic button to just add a [new AddItemAction()] (that inherits from Action), to the [List Series]. While it does add a new Action, as far as I can tell, it only adds an Action and not an actual AddItemAction as it only shows what the Action class has and nothing that the AddItemAction has for settings.

ActionSeries Editor

[CustomEditor(typeof(ActionSeries))]
public class ActionSeriesEditor : Editor
{
    SerializedProperty actions;

    private void OnEnable() {
        actions = serializedObject.FindProperty("series");
    }

    public override void OnInspectorGUI() {
        ActionSeries series = (ActionSeries)target;

        EditorGUILayout.LabelField("Add Action Event to List");

        if (GUILayout.Button("Add Item")) {
            series.series.Add(new AddItemAction());
        }

        CEHelpers.Spacer();

        EditorGUILayout.LabelField("Action Series Settings");
        DrawDefaultInspector();
    }
}

ActionSeries

public class ActionSeries : MonoBehaviour
{
    public List<Action> series;
}

Action
(I know the Run() > Run(null) is bad- just ignore it please)

[System.Serializable]
public class Action
{
    [Header("Action Settings")]
    [Tooltip("Whether or not this can be ran multiple times")]
    public bool runOnce = false;

    [Header("Action Info (do not touch)")]
    [Tooltip("Whether or not this has ran before")]
    [SerializeField]private bool hasRan = false;

    /// <summary>
    /// What the Action will do, if allowed to run on [Perform]
    /// </summary>
    public virtual void Run(GameObject obj) {
        //Does nothing
    }

    /// <summary>
    /// Run an action without a GameObject
    /// </summary>
    public virtual void Run() {
        Run(null);
    }

    /// <summary>
    /// Base set for how to run an action, core method to launch Action's to do stuff
    /// </summary>
    public virtual void Perform(GameObject obj) {
        if (!runOnce) {
            //If [runOnce] is false, always run
            Run(obj);
            hasRan = true;
        } else if (runOnce && !hasRan) {
            //If [runOnce] is true but has not ran before
            Run(obj);
            hasRan = true;
        } else {
            //if [runOnce] is true, and has ran before
            DisabledRun();
        }
    }

    /// <summary>
    /// Perform an action without a gameobject
    /// </summary>
    public virtual void Perform() {
        Perform(null);
    }

    /// <summary>
    /// Optional 'to do' for when runOnce is true, and it has already ran before
    /// </summary>
    public virtual void DisabledRun() {
        //Do nothing
    }
}

[SerializeReference] public List<Action> series; should do what you want. Just be aware that SerializeReference has several pitfalls (like not being compatible if the type to be serialized is a UnityEngine.Object or has nested reference to one)

3 Likes

Thank you. This is beautiful.

(And no, Action and what inherits it- do not inherit from Object or Monobehaviour thankfully)

6488564--729365--Capture.PNG

1 Like