How to create multidimensional array in Unity Inspector?

狂风中的少年 提交于 2019-12-04 16:04:05
zambari

You need to create a custom editor (or more specifically CustomPropertDrawer if you want to re-use it for other components

The only non-obvious part required to create a table like that is forcing the elements to lay out the way you want. One way is manually handling position Rect's given you by Unity, but there is a much simple (albeit a bit less flexible) way, just wrap your elements in horizontal/vertical layout combos. The intuitive approach would be to wrap your elements in

GUILayout.BeginHorizontal();
{
  // your elements line 1
}
GUILayout.EndHorizontal(); 
GUILayout.BeginHorizontal();
{
  // your elements line 2 and so on
}
GUILayout.EndHorizontal(); 

but it has a downside - autolayout will only take widts of elements in a current line, but if element size varies this will break vertical alingment. A solution is to wrap each column in a layout first, and than use the horizontal layout to combine vertical strips, it goes like this

GUILayout.BeginHorizontal();
{
  GUILayout.BeginVertical();
  {
   // your elements column 1
  }
 GUILayout.EndVertical();
 GUILayout.BeginVertical();
 { 
   // your elements column 2
 }  
 GUILayout.EndVertical();
}
GUILayout.EndHorizontal(); 

Brackets are just for clarity, they do nothing. I hope this helps

You can build a CustomEditor script for your class, and then display your multidimensional array using GUI, just like you code normal OnGUI event.

Here's a simple pseudo code

Loop for every Rows
   Divide inspector width with columns length;
   Loop for every Columns
       Render Custom Field with dividen width;
   End Loop
   Incrase posY for new rows ++;
End Loop

Here are some links that will help you get started

https://docs.unity3d.com/Manual/editor-CustomEditors.html

https://unity3d.com/learn/tutorials/topics/interface-essentials/building-custom-inspector

user1105430

Thanks to all the answers provided I have come up with this solution:

Levels.cs

using UnityEngine;

public enum BlockColors {blank, red, blue, green, yellow, cyan, white, purple};

[System.Serializable] public class level {
    #if UNITY_EDITOR
    [HideInInspector] public bool showBoard;
    #endif
    public int rows = 9;
    public int column = 9;
    public BlockColors [,] board = new BlockColors [columns, rows];
}


public class Levels : MonoBehaviour {

    public Level[] allLevels;

}

Editor/LevelEditor.cs

using UnityEngine;
using UnityEditor;


[CustomEditor(typeof(Levels))]
public class LevelEditor : Editor {

    public bool showLevels = true;

    public override void OnInspectorGUI() {
        Levels levels = (Levels)target;
        EditorGUILayout.Space ();

        showLevels = EditorGUILayout.Foldout (showLevels, "Levels ("+levels.allLevels.Length+")");
        if (showLevels) {
            EditorGUI.indentLevel++;
            for (ushort i = 0; i < levels.allLevels.Length; i++) {

                levels.allLevels[i].showBoard = EditorGUILayout.Foldout(levels.allLevels[i].showBoard, "Board");
                if (levels.allLevels [i].showBoard) {

                    EditorGUI.indentLevel = 0;

                    GUIStyle tableStyle = new GUIStyle ("box");
                    tableStyle.padding = new RectOffset (10, 10, 10, 10);
                    tableStyle.margin.left = 32;

                    GUIStyle headerColumnStyle = new GUIStyle ();
                    headerColumnStyle.fixedWidth = 35;

                    GUIStyle columnStyle = new GUIStyle ();
                    columnStyle.fixedWidth = 65;

                    GUIStyle rowStyle = new GUIStyle ();
                    rowStyle.fixedHeight = 25;

                    GUIStyle rowHeaderStyle = new GUIStyle ();
                    rowHeaderStyle.fixedWidth = columnStyle.fixedWidth - 1;

                    GUIStyle columnHeaderStyle = new GUIStyle ();
                    columnHeaderStyle.fixedWidth = 30;
                    columnHeaderStyle.fixedHeight = 25.5f;

                    GUIStyle columnLabelStyle = new GUIStyle ();
                    columnLabelStyle.fixedWidth = rowHeaderStyle.fixedWidth - 6;
                    columnLabelStyle.alignment = TextAnchor.MiddleCenter;
                    columnLabelStyle.fontStyle = FontStyle.Bold;

                    GUIStyle cornerLabelStyle = new GUIStyle ();
                    cornerLabelStyle.fixedWidth = 42;
                    cornerLabelStyle.alignment = TextAnchor.MiddleRight;
                    cornerLabelStyle.fontStyle = FontStyle.BoldAndItalic;
                    cornerLabelStyle.fontSize = 14;
                    cornerLabelStyle.padding.top = -5;

                    GUIStyle rowLabelStyle = new GUIStyle ();
                    rowLabelStyle.fixedWidth = 25;
                    rowLabelStyle.alignment = TextAnchor.MiddleRight;
                    rowLabelStyle.fontStyle = FontStyle.Bold;

                    GUIStyle enumStyle = new GUIStyle ("popup");
                    rowStyle.fixedWidth = 65;

                    EditorGUILayout.BeginHorizontal (tableStyle);
                    for (int x = -1; x < levels.allLevels [i].columns; x++) {
                        EditorGUILayout.BeginVertical ((x == -1) ? headerColumnStyle : columnStyle);
                        for (int y = -1; y < levels.allLevels [i].rows; y++) {
                            if (x == -1 && y == -1) {
                                EditorGUILayout.BeginVertical (rowHeaderStyle);
                                EditorGUILayout.LabelField ("[X,Y]", cornerLabelStyle);
                                EditorGUILayout.EndHorizontal ();
                            } else if (x == -1) {
                                EditorGUILayout.BeginVertical (columnHeaderStyle);
                                EditorGUILayout.LabelField (y.ToString (), rowLabelStyle);
                                EditorGUILayout.EndHorizontal ();
                            } else if (y == -1) {
                                EditorGUILayout.BeginVertical (rowHeaderStyle);
                                EditorGUILayout.LabelField (x.ToString (), columnLabelStyle);
                                EditorGUILayout.EndHorizontal ();
                            }

                            if (x >= 0 && y >= 0) {
                                EditorGUILayout.BeginHorizontal (rowStyle);
                                levels.allLevels [i].board [x, y] = (BlockColors)EditorGUILayout.EnumPopup (levels.allLevels [i].board [x, y], enumStyle);
                                EditorGUILayout.EndHorizontal ();
                            }
                        }
                        EditorGUILayout.EndVertical ();
                    }
                    EditorGUILayout.EndHorizontal ();

                }

            }
        }
    }
}

My main problem now is it won't serialize. Any change I made to the level will automatically reset on Play. How can I serialize the custom array setup from the Inspector?

Thanks to all the excellent answers above.

For anyone having trouble persisting their custom arrays, one issue is that multidimensional arrays are not serializable. Possible workarounds include implementing your 2D array as a jagged array (an array whose elements are arrays) or creating a wrapper class that is serializable itself.

As endrik exe suggested, you'll also want to prompt Unity to save changes to your object using EditorUtility.SetDirty(). Adding the following to the end of OnInspectorGUI() should do the trick:

if (GUI.changed) {
    Undo.RecordObject(levels, "Edit levels");
    EditorUtility.SetDirty(levels);
}
易学教程内所有资源均来自网络或用户发布的内容,如有违反法律规定的内容欢迎反馈
该文章没有解决你所遇到的问题?点击提问,说说你的问题,让更多的人一起探讨吧!