如何在 Unity Inspector 中创建多维数组?

How to create multidimensional array in Unity Inspector?

如何在 Unity Inspector 中创建枚举多维数组并使其可序列化以便我可以从不同的脚本调用它?

public enum colors {red, blue, green, yellow, cyan, white, purple};
public int rows = 7;
public int column = 4;
public colors[,] blockColors;

private void Awake() {
    blockColors = new colors[rows, column];
}

对我来说,在脚本中手动输入所有 28 种颜色非常耗时,尤其是当我必须为数百个级别执行此操作时。有没有办法在 Inspector 中创建 table 来加快工作流程?

我尝试将 blockColors 变成 [Serializefield],但它不起作用。 我以前从未尝试过为检查员编写图表。有人可以指导我查看可以帮助我理解如何像图片中那样对图表进行编码的教程吗?

您可以为您的 class 构建一个 CustomEditor 脚本,然后使用 GUI 显示您的多维数组,就像编写普通 OnGUI 事件代码一样。

这是一个简单的伪代码

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

这里有一些链接可以帮助您入门

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

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

您需要创建一个自定义编辑器(或者更具体地说是 CustomPropertDrawer,如果您想要 re-use 它用于其他组件

创建这样的 table 所需的唯一 non-obvious 部分是强制元素以您想要的方式布局。一种方法是手动处理 Unity 给你的位置 Rect,但还有一种更简单(尽管不太灵活)的方法,只需将你的元素包装在 horizontal/vertical 布局组合中。 直观的方法是将元素包装在

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

但它有一个缺点 - 自动布局将只采用当前行中的元素宽度,但如果元素大小发生变化,这将破坏垂直排列。一个解决方案是先将每一列包裹在一个布局中,然后使用水平布局来组合垂直条带,它是这样的

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

括号只是为了清楚起见,它们什么都不做。 希望对您有所帮助

感谢所有提供的答案,我想出了这个解决方案:

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 ();

                }

            }
        }
    }
}

我现在的主要问题是它不会序列化。我对关卡所做的任何更改都会在播放时自动重置。如何从 Inspector 序列化自定义数组设置?

感谢以上所有优秀的回答。

对于任何在持久化自定义数组时遇到问题的人来说,一个问题是多维数组不可序列化。可能的解决方法包括将 2D 数组实现为锯齿状数组(元素为数组的数组)或创建一个本身可序列化的包装器 class。

正如 endrik exe 所建议的,您还需要提示 Unity 使用 EditorUtility.SetDirty() 保存对对象的更改。将以下内容添加到 OnInspectorGUI() 的末尾应该可以解决问题:

if (GUI.changed) {
    Undo.RecordObject(levels, "Edit levels");
    EditorUtility.SetDirty(levels);
}