unity 通过 二维数组序列化 实现二维数组在编辑器面板查看和配置数据

实现思路如下:

1、定义一个二维数组数据类

2、奖数据类标记为 Serializable  并实现 unity内置的  ISerializationCallbackReceiver接口,接口提供了序列化和反序列化方法,参见官方文档(文档中给出了字典的序列化方法):Unity - Scripting API: ISerializationCallbackReceiver

3、使用unity序列化系统可识别的数据存储二维数组中的数据,这里使用一维数组

4、将数据类作为变量,重写主体类的inspector编辑器面板,并提供数据操作方法

代码如下:

数据类

using System.Collections;
using System.Collections.Generic;
using UnityEngine;
[System.Serializable]
public class DataGrid2 : ISerializationCallbackReceiver
{
    #region 数据类内容
    /// <summary>
    /// 行名
    /// </summary>
    [HideInInspector] public List<string> rowNames = new List<string>();
    /// <summary>
    /// 列名
    /// </summary>
    [HideInInspector] public List<string> columnNames = new List<string>();

    [SerializeField] public string[,] data = new string[0, 0];

    #endregion

    #region 序列化与反序列化操作
    [HideInInspector]
    public string[] serializedArray;
    public void OnBeforeSerialize()
    {
        int rows = rowNames.Count;
        int cols = columnNames.Count;
        serializedArray = new string[rows * cols];
        for (int i = 0; i < rows; i++)
        {
            for (int j = 0; j < cols; j++)
            {
                serializedArray[i * cols + j] = data[i, j];
            }
        }

    }
    public void OnAfterDeserialize()
    {
        int rows = rowNames.Count;
        int cols = columnNames.Count;
        data = new string[rows, cols];
        for (int i = 0; i < rows; i++)
        {
            for (int j = 0; j < cols; j++)
            {
                data[i, j] = serializedArray[i * cols + j];
            }
        }
    }
    #endregion

    #region 对数据内容的操作
    public void AddRow()
    {
        rowNames.Add("RowName");
        string[,] newData = new string[rowNames.Count, columnNames.Count];
        for (int i = 0; i < rowNames.Count; i++)
        {
            if (i == rowNames.Count - 1)
            {
                for (int j = 0; j < columnNames.Count; j++)
                {
                    newData[i, j] = "0";
                }
            }
            else
            {
                for (int j = 0; j < columnNames.Count; j++)
                {
                    newData[i, j] = data[i, j];
                }
            }

        }
        data = newData;
    }
    public void DeletedRow()
    {
        rowNames.RemoveAt(rowNames.Count - 1);
        string[,] newData = new string[rowNames.Count, columnNames.Count];
        for (int i = 0; i < rowNames.Count; i++)
        {
            for (int j = 0; j < columnNames.Count; j++)
            {
                newData[i, j] = data[i, j];
            }

        }
        data = newData;
    }
    public void AddColumn()
    {
        columnNames.Add("ColumnName");
        string[,] newData = new string[rowNames.Count, columnNames.Count];
        for (int i = 0; i < rowNames.Count; i++)
        {
            for (int j = 0; j < columnNames.Count; j++)
            {
                if (j == columnNames.Count - 1)
                    newData[i, j] = "0";
                else
                    newData[i, j] = data[i, j];
            }
        }
        data = newData;
    }
    public void DeletedColumn()
    {
        columnNames.RemoveAt(columnNames.Count - 1);
        string[,] newData = new string[rowNames.Count, columnNames.Count];
        for (int i = 0; i < rowNames.Count; i++)
        {
            for (int j = 0; j < columnNames.Count; j++)
            {
                newData[i, j] = data[i, j];
            }

        }
        data = newData;
    }
    public void CleraData()
    {
        rowNames.Clear();
        columnNames.Clear();
        data = new string[0, 0];
    }
    #endregion

}

编辑器重写

public class TestDataGrid2 : MonoBehaviour
{
    public string name = "常规变量";
    public DataGrid2 data = new DataGrid2();
}

 

using DG.Tweening;
using System.Collections;
using System.Collections.Generic;
using UnityEditor;
using UnityEngine;
using UnityEngine.UI;

[CustomEditor(typeof(TestDataGrid2))]
public class TestDataGrid2Editor : Editor
{
    GUIStyle green = new GUIStyle(EditorStyles.textArea);
    GUIStyle red = new GUIStyle(EditorStyles.textArea);
    TestDataGrid2 DG2;
    private void Awake()
    {
        DG2 = (TestDataGrid2)target;
    }
    float width = 0;
    public override void OnInspectorGUI()
    {
        base.OnInspectorGUI();
        EditorGUILayout.LabelField("这是一个二维数组的配置文件,默认使用 string 类型");
        EditorGUILayout.LabelField("data : ");
        EditorGUILayout.BeginVertical();
        if (GUILayout.Button("清空数据"))
        {
            DG2.data.CleraData();
        }
        EditorGUILayout.BeginHorizontal();
        EditorGUILayout.TextField($"{DG2.data.rowNames.Count}行/{DG2.data.columnNames.Count}列", red);
        for (int j = 0; j < DG2.data.columnNames.Count; j++)
        {
            DG2.data.columnNames[j] = EditorGUILayout.TextField(DG2.data.columnNames[j].ToString(), green);
        }
        EditorGUILayout.EndHorizontal();
        for (int i = 0; i < DG2.data.rowNames.Count; i++)
        {
            EditorGUILayout.BeginHorizontal();
            DG2.data.rowNames[i] = EditorGUILayout.TextField(DG2.data.rowNames[i], green);
            for (int j = 0; j < DG2.data.columnNames.Count; j++)
            {
                DG2.data.data[i, j] = EditorGUILayout.TextField(DG2.data.data[i, j].ToString());
            }
            EditorGUILayout.EndHorizontal();
        }


        EditorGUILayout.BeginHorizontal();
        if (GUILayout.Button("++添加尾行++"))
        {
            DG2.data.AddRow();
        }
        GUILayout.Space(15);
        if (GUILayout.Button("--删除尾行--"))
        {
            DG2.data.DeletedRow();
        }
        EditorGUILayout.EndHorizontal();
        EditorGUILayout.BeginHorizontal();

        if (GUILayout.Button("++添加尾列++"))
        {
            DG2.data.AddColumn();
        }
        GUILayout.Space(15);
        if (GUILayout.Button("--删除尾列--"))
        {
            DG2.data.DeletedColumn();
        }
        EditorGUILayout.EndHorizontal();
        EditorGUILayout.EndVertical();
        serializedObject.ApplyModifiedProperties();
    }
}

面板效果如图

 附:将数据类作为持久化数据保存,提取编辑器面板类,在作为变量使用时,也作为子面板绘制

数据类

using System;
using System.Collections;
using System.Collections.Generic;
using UnityEngine;
[CreateAssetMenu(fileName = "DataGrid", menuName = "DataGrid", order = 10)]
public class DataGrid : ScriptableObject, ISerializationCallbackReceiver
{
    #region 数据内容
    [HideInInspector] public List<string> rowNames = new List<string>();
    [HideInInspector] public List<string> columnNames = new List<string>();
    [SerializeField]
    public string[,] data = new string[0, 0];
    #endregion
    #region  序列化和反序列化
    [HideInInspector]
    public string[] serializedArray;
    public void OnBeforeSerialize()
    {
        int rows = rowNames.Count;
        int cols = columnNames.Count;
        serializedArray = new string[rows * cols];
        for (int i = 0; i < rows; i++)
        {
            for (int j = 0; j < cols; j++)
            {
                serializedArray[i * cols + j] = data[i, j];
            }
        }

    }

    public void OnAfterDeserialize()
    {
        int rows = rowNames.Count;
        int cols = columnNames.Count;
        data = new string[rows, cols];
        for (int i = 0; i < rows; i++)
        {
            for (int j = 0; j < cols; j++)
            {
                data[i, j] = serializedArray[i * cols + j];
            }
        }
    }
    #endregion

    #region 数据操作
    public void AddRow()
    {
        rowNames.Add("RowName");
        string[,] newData = new string[rowNames.Count, columnNames.Count];
        for (int i = 0; i < rowNames.Count; i++)
        {
            if (i == rowNames.Count - 1)
            {
                for (int j = 0; j < columnNames.Count; j++)
                {
                    newData[i, j] = "0";
                }
            }
            else
            {
                for (int j = 0; j < columnNames.Count; j++)
                {
                    newData[i, j] = data[i, j];
                }
            }

        }
        data = newData;
    }
    public void DeletedRow()
    {
        rowNames.RemoveAt(rowNames.Count - 1);
        string[,] newData = new string[rowNames.Count, columnNames.Count];
        for (int i = 0; i < rowNames.Count; i++)
        {
            for (int j = 0; j < columnNames.Count; j++)
            {
                newData[i, j] = data[i, j];
            }

        }
        data = newData;
    }
    public void AddColumn()
    {
        columnNames.Add("ColumnName");
        string[,] newData = new string[rowNames.Count, columnNames.Count];
        for (int i = 0; i < rowNames.Count; i++)
        {
            for (int j = 0; j < columnNames.Count; j++)
            {
                if (j == columnNames.Count - 1)
                    newData[i, j] = "0";
                else
                    newData[i, j] = data[i, j];
            }
        }
        data = newData;
    }
    public void DeletedColumn()
    {
        columnNames.RemoveAt(columnNames.Count - 1);
        string[,] newData = new string[rowNames.Count, columnNames.Count];
        for (int i = 0; i < rowNames.Count; i++)
        {
            for (int j = 0; j < columnNames.Count; j++)
            {
                newData[i, j] = data[i, j];
            }

        }
        data = newData;
    }
    public void CleraData()
    {
        rowNames.Clear();
        columnNames.Clear();
        data = new string[0, 0];
    }
    #endregion
}

 编辑器面板

using DG.Tweening;
using System;
using System.Collections;
using System.Collections.Generic;
using System.Linq;
using UnityEditor;
using UnityEngine;
[CustomEditor(typeof(DataGrid))]
public class DataGridEditor : Editor
{
    DataGrid grid;
    GUIStyle green = new GUIStyle(EditorStyles.textArea);
    GUIStyle red = new GUIStyle(EditorStyles.textArea);
    private void Awake()
    {
        grid = (DataGrid)target;
        green.normal.textColor= Color.green;
        red.normal.textColor= Color.red;

    }
    public override void OnInspectorGUI()
    {
        base.OnInspectorGUI();
        EditorGUILayout.LabelField("这是一个二维数组的配置文件,默认使用 string 类型");
        EditorGUILayout.LabelField("data : ");
        EditorGUILayout.BeginVertical();
        if (GUILayout.Button("清空数据"))
        {
            grid.CleraData();
            EditorUtility.SetDirty(grid);
            AssetDatabase.SaveAssets();
        }
        EditorGUILayout.BeginHorizontal();
        EditorGUILayout.TextField($"{grid.rowNames.Count}行/{grid.columnNames.Count}列", red);
        for (int j = 0; j < grid.columnNames.Count; j++)
        {
            grid.columnNames[j] = EditorGUILayout.TextField(grid.columnNames[j].ToString(), green);
        }
        EditorGUILayout.EndHorizontal();
        for (int i = 0; i < grid.rowNames.Count; i++)
        {
            EditorGUILayout.BeginHorizontal();
            grid.rowNames[i] = EditorGUILayout.TextField(grid.rowNames[i], green);
            for (int j = 0; j < grid.columnNames.Count; j++)
            {
                grid.data[i, j] = EditorGUILayout.TextField(grid.data[i, j].ToString());
            }
            EditorGUILayout.EndHorizontal();
        }


        EditorGUILayout.BeginHorizontal();
        if (GUILayout.Button("++添加尾行++"))
        {
            grid.AddRow();
            EditorUtility.SetDirty(grid);
            AssetDatabase.SaveAssets();
        }
        GUILayout.Space(15);
        if (GUILayout.Button("--删除尾行--"))
        {
            grid.DeletedRow();
            EditorUtility.SetDirty(grid);
            AssetDatabase.SaveAssets();
        }
        EditorGUILayout.EndHorizontal();
        EditorGUILayout.BeginHorizontal();

        if (GUILayout.Button("++添加尾列++"))
        {
            grid.AddColumn();
            EditorUtility.SetDirty(grid);
            AssetDatabase.SaveAssets();
        }
        GUILayout.Space(15);
        if (GUILayout.Button("--删除尾列--"))
        {
            grid.DeletedColumn();
            EditorUtility.SetDirty(grid);
            AssetDatabase.SaveAssets();
        }
        EditorGUILayout.EndHorizontal();
        EditorGUILayout.EndVertical();
        if (GUILayout.Button("保存/更新"))
        {
            EditorUtility.SetDirty(grid);
            AssetDatabase.SaveAssets();
        }
        serializedObject.ApplyModifiedProperties();
    }
}

 效果:

 作为变量使用(借助 Editor.CreateEditor 方法):

效果如下: