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 方法):
效果如下: