Sexy inspector for an Array of Array?

Hello there,

So, I’m working on my game Swords and Souls 2 (#ShamelessPromo), and I have this CombatData class which contains an array of CombatWave, and each of this CombatWave contains an array of enum EnemyType. Here is the code, just to be clear:

public class CombatData : MonoBehaviour {
    [SerializeField]
    private string title;

    [SerializeField]
    private CombatWave[] waves;
}

[System.Serializable]
public class CombatWave {
    [SerializeField]
    public EnemyType[] enemyList;
}

public enum EnemyType {
    Orc,
    Goblin,
    Gnobul
}

So, here is what I have in the Inspector:

… And here is what I would like to have, more or less:

The “+” button would add a row, (I’d need a “-” button as well). Each row represents a CombatWave with the 3 EnemyType it contains. (I won’t ever have more than 3 enemies per wave, so no worry). That would be the most convenient way for me to handle my CombatData, I think.

I’ve look around for custom Class serialization or Array serialization, etc. but it’s always unclear or too complicated, or doesn’t fit my needs. I know I’d have to deal with Editor stuff and all, but I’m terrible with Editor related stuff, I’m just not a coding God, I need to work from a good model, or with solid leads.

If someone can help by providing code, ideas, leads, or examples of similar customized Component, I would greatly appreciate!

Thanks for your help and time

using UnityEngine;
using System;

#if UNITY_EDITOR
using UnityEditor;
[CustomEditor(typeof(WaveStarter))]
public class Wave2DArrayPropDrawer : Editor
{
    public override void OnInspectorGUI()
    {
        DrawDefaultInspector();

        WaveStarter ws = target as WaveStarter;
        EditorGUILayout.BeginHorizontal();
        {
            GUILayout.Label("Waves");
            GUILayout.FlexibleSpace();
            if (GUILayout.Button("+"))
            {
                Undo.RecordObject(ws, "Add Wave");
                WaveStarter.Wave wave = new WaveStarter.Wave();
                wave.wave = new WaveStarter.Type[0];
                ArrayUtility.Add(ref ws.waves, wave);
            }
        }
        EditorGUILayout.EndHorizontal();

        EditorGUI.indentLevel++;
        {
            const float labelWidth = 20;
            const float minusWidth = 20;
            GUILayoutOption enumWidth = GUILayout.Width((EditorGUIUtility.currentViewWidth - labelWidth - minusWidth) / 3f - 20); // -20 because EnumPopup seems to add some padding that I don't know where it comes from...
            GUILayoutOption minusWidthOp = GUILayout.Width(minusWidth);
            Undo.RecordObject(ws, "Edit Wave");
            for (int wave = 0; wave < ws.waves.Length; wave++)
            {
                EditorGUILayout.BeginHorizontal();
                {
                    GUILayout.Label(wave.ToString(), GUILayout.Width(labelWidth));
                    for (int enemy = 0; enemy < ws.waves[wave].wave.Length; enemy++)
                    {
                        ws.waves[wave].wave[enemy] = (WaveStarter.Type)EditorGUILayout.EnumPopup(ws.waves[wave].wave[enemy], enumWidth);
                        if (ws.waves[wave].wave[enemy] == WaveStarter.Type.None)
                        {
                            ArrayUtility.RemoveAt(ref ws.waves[wave].wave, enemy);
                            enemy--; // to compensate for next j++
                        }
                    }
                    if (ws.waves[wave].wave.Length < 3)
                    {
                        WaveStarter.Type add = WaveStarter.Type.None;
                        add = (WaveStarter.Type)EditorGUILayout.EnumPopup(add, enumWidth);
                        if (add != WaveStarter.Type.None)
                            ArrayUtility.Add(ref ws.waves[wave].wave, add);
                    }
                    GUILayout.FlexibleSpace();
                    if (GUILayout.Button("-", minusWidthOp))
                    {
                        ArrayUtility.RemoveAt(ref ws.waves, wave);
                        wave--; // to compensate for next i++;
                    }
                }
                EditorGUILayout.EndHorizontal();
            }
        }
        EditorGUI.indentLevel--;
    }
}
#endif

public class WaveStarter : MonoBehaviour
{
    public enum Type
    {
        Orc, Gnobul, JeanLouis, None
    };


    [Serializable]
    public struct Wave
    {
        public Type[] wave; // max 3 by tacit contract!
    }

    [HideInInspector] // we are going to draw it ourselves so useless
    public Wave[] waves = new Wave[0];
}

Quick hack, gives me this :

2920303--215664--upload_2017-1-14_14-30-55.png

1 Like

It’s perfect…