Draw custom array properties on inspector

Hi, I am working on displaying an abstract class array on inspector depends on ActionType of each element.

My current result as following, I can only display all variables by DrawPropertiesExcluding or display selected variables outside array block…

public class EventHandler : MonoBehaviour
{
    public ActionWrapper[] actionList;
    private void OnEnable()
    {
        foreach (var action in actionList)
        {
            action.actionValue = action.eventAction.CreateAction();
        }
   
    }
}

[Serializable]
public class ActionWrapper
{
    public EventAction eventAction;
    public ActionValueBase actionValue = null;

}
public enum ActionValueType
{
    Unknown = -1,
    OpenURL = 8,
    Scale = 15,
    ...

}

[Serializable]
public class EventAction
{
    public ActionValueType actionType = ActionValueType.Unknown;
    public OpenURLAction OpenURLAction = new OpenURLAction();
    public ScaleAction ScaleAction = new ScaleAction();
    ...

    public ActionValueBase CreateAction()
    {
        return GetActionById((int)actionType);
    }

    public Type GetClassId(ActionValueType type)
    {
        return GetActionById((int)type).GetType();
    }

    private ActionValueBase GetActionById(int id)
    {
        switch (id)
        {
            ...
            case 8:
                return OpenURLAction;
            case 15:
                return ScaleAction;
            ...
        }
    }
}
[CustomEditor(typeof(EventHandler), true)]
public class EventHandlerEditor : Editor
{
    protected EventHandler _eventHandler;
    protected SerializedObject _serializedEventHandler;
    protected SerializedProperty _serializedActionsProperties;

    private void OnEnable()
    {
        _eventHandler = (EventHandler)target;
        _serializedEventHandler = new SerializedObject(_eventHandler);
        _serializedActionsProperties = _serializedEventHandler.FindProperty("actionList");

    }

    public override void OnInspectorGUI()
    {
        _serializedEventHandler.Update();
        DraweventAction();
        _serializedEventHandler.ApplyModifiedProperties();
    }

    protected void DraweventAction()
    {
        if(_serializedActionsProperties.arraySize > 0)
        {
            for (int i = 0; i < _serializedActionsProperties.arraySize; i++)
            {
                SerializedProperty element = _serializedActionsProperties.GetArrayElementAtIndex(i);
            
                EditorGUILayout.LabelField("eventAction", EditorStyles.boldLabel);
                EditorGUILayout.PropertyField(element.FindPropertyRelative("eventAction").FindPropertyRelative("actionType"));

                EventAction _eventAction = _eventHandler.actionList[i].eventAction;
                Type typeOfAction = _eventAction.GetClassId(_eventAction.actionType);
                SerializedProperty additionalValues = (element.FindPropertyRelative("eventAction").FindPropertyRelative(typeOfAction.ToString())).Copy();
                string parentPath = additionalValues.propertyPath;
                while (additionalValues.NextVisible(true) && additionalValues.propertyPath.StartsWith(parentPath))
                {
                    EditorGUILayout.PropertyField(additionalValues);
                }

           
            }
        }
    }
}

I can update display properties by enum but how can I display the selected type of properties only on array element block? And dynamicly update array elements on inspector?

My expectation:

Stay away from IMGUI, it’s legacy. Editor tooling is a lot easier with UI Toolkit.

I would disagree ^^ Especially when it comes to dynamic UI, working with element / object based approaches things gets a lot more complicated.

I’m not sure if I get your exact setup here. You said you have an “abstract class array” and this is already an issue. I guess you don’t really have an abstract class because Unity could not serialize it unless you use the SerializeReference attribute. So you seem to have a huge class that can represent various things and based on the type selected you only want to show certain data.

First of all, usually you don’t want to create a CustomEditor / Editor for things like that but just a PropertyDrawer for your “EventAction” class. Your logic should actually work, but I don’t quite get the point of your while loop. Why do you iterate through the child elements manually? Just calling

EditorGUILayout.PropertyField(additionalValues);

once should be enough to draw the whole expandable sub property.

Sorry for my poor english, my abstract base class ActionValueBase contains base variables for actions, while the subclasses include additional values for specific action types. Different groups of actions are executed on an object, and they are all stored in array actionList.

[Serializable]
public abstract class ActionValueBase
{
    [JsonIgnore] public abstract ActionValueType id { get; }

    public int group = -1;
    public int obj_id = -1;
    public float delay_time = 0;
    public int play_count = -1;
  
}

[Serializable]
public class OpenURLAction : ActionValueBase
{
    //Action id 8 = Open URL
    public override ActionValueType id => ActionValueType.OpenURL;

    public string url = "";

}

[Serializable]
public class ScaleAction : ActionValueBase
{
    //Action id 15 = Scale
    public override ActionValueType id => ActionValueType.Scale;

    public float scale = 1;

}

My code is referenced from the blog below, but I want to display it within an array block:
https://www.linkedin.com/pulse/unity-hack-dynamic-property-field-inspector-zhen-stephen-gou
Each element of ActionWrapper should display only the selected type variables in inspector, my expectation below (actually DrawPropertiesExcluding or EditorGUILayout.PropertyField(_serializedActionsProperties) draw all subclasses within the element block, but my custom DraweventAction() cannot draw the array block…):
9792705--1405413--upload_2024-4-25_11-26-52.png
I am new to editor scripting, may I know how DrawPropertiesExcluding function draw an array? Can I control which subproperties are displayed or not?

while (additionalValues.NextVisible(true) && additionalValues.propertyPath.StartsWith(parentPath))
{
    EditorGUILayout.PropertyField(additionalValues);
}

This while loop draws all action variables of the selected type. However, a single call may only draw the first variable.

Okay, thank you for @Bunny83 's advice.
I wrote a PropertyDrawer for EventAction, now it works well!!

1 Like