编辑器关闭后,Unity Scriptable Object 不保留其值
Unity Scriptable Object does not retain his values after the editor is closed
我有以下可编写脚本的对象:
[CreateAssetMenu(fileName = "Assets/Gladio Games/Resources/Levels/LEVLE_TMP", menuName = "Utils/Crossword/Generate Empty Level", order = 100)]
public class Level : ScriptableObject
{
public char[,] Table { get; protected set; }
public List<Crossword> Crosswords { get; protected set; }
protected static char EMPTY_CHAR = '[=10=]';
public Crossword GetCrosswordAtIndex(int x, int y, Direction direction)
{
//Ottieni la prima e l'unica parola che si trova sotto quell'indice
return Crosswords.First(crossword => crossword.Direction == direction && crossword.GetCrosswordIndexes().Contains(new Vector2(x, y)));
}
}
这是我用来保存脚本对象的代码
private static void Save(Level level, string path)
{
EditorUtility.SetDirty(level);
AssetDatabase.CreateAsset(level, path + ".asset");
AssetDatabase.SaveAssets();
AssetDatabase.Refresh();
EditorSceneManager.MarkSceneDirty(EditorSceneManager.GetActiveScene());
}
该对象已通过编辑器脚本成功创建,并且保存了所有数据,但如果我关闭并重新打开编辑器,并尝试加载字段,则字段为空。用于保存和加载脚本对象的脚本在场景中使用
我做错了什么?
特别是你的情况
-
Serializers in Unity work directly on the fields of your C# classes rather than their properties, so there are rules that your fields must conform to to be serialized.
-
Note: Unity doesn’t support serialization of multilevel types (multidimensional arrays, jagged arrays, dictionaries, and nested container types)
所以你的属性根本没有序列化(=保存)。
作为解决方案
对于Crosswords
确保类型 Crossword
本身是 [Serializable]
改用字段
public List<Crossword> Crosswords;
对于Table
有多种方法。
我可能会简单地使用包装器 class,例如
// Could of course make this generic but for the drawer it easier this way for now
[Serializable]
public class Table
{
[SerializeField]
[Min(1)]
private int xDimension = 1;
[SerializeField]
[Min(1)]
private int yDimension = 1;
[SerializeField]
private char[] flatArray = new char[1];
public Table() { }
public Table(char[,] array)
{
xDimension = array.GetLength(0);
yDimension = array.GetLength(1);
flatArray = new char[xDimension * yDimension];
for (var x = 0; x < xDimension; x++)
{
for (var y = 0; y < yDimension; y++)
{
flatArray[x * yDimension + y] = array[x, y];
}
}
}
public Table(int x, int y)
{
xDimension = x;
yDimension = y;
flatArray = new char[x * y];
}
public Table(char[] array, int x, int y)
{
xDimension = x;
yDimension = y;
flatArray = array;
}
public char this[int x, int y]
{
get => flatArray[x * yDimension + y];
set => flatArray[x * yDimension + y] = value;
}
#if UNITY_EDITOR
[CustomPropertyDrawer(typeof(Table))]
private class TableDrawer : PropertyDrawer
{
private const int k_elementSpacing = 5;
public override float GetPropertyHeight(SerializedProperty property, GUIContent label)
{
var height = 1f;
if (property.isExpanded)
{
height += property.FindPropertyRelative(nameof(Table.xDimension)).intValue * 1.5f;
}
return height * EditorGUIUtility.singleLineHeight;
}
public override void OnGUI(Rect position, SerializedProperty property, GUIContent label)
{
var foldoutRect = position;
foldoutRect.height = EditorGUIUtility.singleLineHeight;
foldoutRect.width = EditorGUIUtility.labelWidth;
property.isExpanded = EditorGUI.Foldout(foldoutRect, property.isExpanded, label);
if (property.isExpanded)
{
var xRect = position;
xRect.height = EditorGUIUtility.singleLineHeight;
xRect.x += foldoutRect.width;
xRect.width -= foldoutRect.width;
xRect.width *= 0.5f;
var yRect = xRect;
yRect.x += xRect.width;
var xDimension = property.FindPropertyRelative(nameof(Table.xDimension));
var yDimension = property.FindPropertyRelative(nameof(Table.yDimension));
var array = property.FindPropertyRelative(nameof(Table.flatArray));
using (var changeCheck = new EditorGUI.ChangeCheckScope())
{
EditorGUI.PropertyField(xRect, xDimension, GUIContent.none);
EditorGUI.PropertyField(yRect, yDimension, GUIContent.none);
if (changeCheck.changed)
{
array.arraySize = xDimension.intValue * yDimension.intValue;
}
}
position.y += EditorGUIUtility.singleLineHeight * 1.5f;
EditorGUI.indentLevel++;
var elementRect = EditorGUI.IndentedRect(position);
EditorGUI.indentLevel--;
var elementWidth = elementRect.width / yDimension.intValue - k_elementSpacing;
var elementHeight = EditorGUIUtility.singleLineHeight;
var currentPosition = elementRect;
currentPosition.width = elementWidth;
currentPosition.height = elementHeight;
for (var x = 0; x < xDimension.intValue; x++)
{
for (var y = 0; y < yDimension.intValue; y++)
{
var element = array.GetArrayElementAtIndex(x * yDimension.intValue + y);
EditorGUI.PropertyField(currentPosition, element, GUIContent.none);
currentPosition.x += elementWidth + k_elementSpacing;
}
currentPosition.x = elementRect.x;
currentPosition.y += elementHeight * 1.5f;
}
}
}
}
#endif
}
然后宁愿有例如
public Table Table;
我有以下可编写脚本的对象:
[CreateAssetMenu(fileName = "Assets/Gladio Games/Resources/Levels/LEVLE_TMP", menuName = "Utils/Crossword/Generate Empty Level", order = 100)]
public class Level : ScriptableObject
{
public char[,] Table { get; protected set; }
public List<Crossword> Crosswords { get; protected set; }
protected static char EMPTY_CHAR = '[=10=]';
public Crossword GetCrosswordAtIndex(int x, int y, Direction direction)
{
//Ottieni la prima e l'unica parola che si trova sotto quell'indice
return Crosswords.First(crossword => crossword.Direction == direction && crossword.GetCrosswordIndexes().Contains(new Vector2(x, y)));
}
}
这是我用来保存脚本对象的代码
private static void Save(Level level, string path)
{
EditorUtility.SetDirty(level);
AssetDatabase.CreateAsset(level, path + ".asset");
AssetDatabase.SaveAssets();
AssetDatabase.Refresh();
EditorSceneManager.MarkSceneDirty(EditorSceneManager.GetActiveScene());
}
该对象已通过编辑器脚本成功创建,并且保存了所有数据,但如果我关闭并重新打开编辑器,并尝试加载字段,则字段为空。用于保存和加载脚本对象的脚本在场景中使用
我做错了什么?
特别是你的情况
-
Serializers in Unity work directly on the fields of your C# classes rather than their properties, so there are rules that your fields must conform to to be serialized.
-
Note: Unity doesn’t support serialization of multilevel types (multidimensional arrays, jagged arrays, dictionaries, and nested container types)
所以你的属性根本没有序列化(=保存)。
作为解决方案
对于
Crosswords
确保类型
Crossword
本身是[Serializable]
改用字段
public List<Crossword> Crosswords;
对于
Table
有多种方法。我可能会简单地使用包装器 class,例如
// Could of course make this generic but for the drawer it easier this way for now [Serializable] public class Table { [SerializeField] [Min(1)] private int xDimension = 1; [SerializeField] [Min(1)] private int yDimension = 1; [SerializeField] private char[] flatArray = new char[1]; public Table() { } public Table(char[,] array) { xDimension = array.GetLength(0); yDimension = array.GetLength(1); flatArray = new char[xDimension * yDimension]; for (var x = 0; x < xDimension; x++) { for (var y = 0; y < yDimension; y++) { flatArray[x * yDimension + y] = array[x, y]; } } } public Table(int x, int y) { xDimension = x; yDimension = y; flatArray = new char[x * y]; } public Table(char[] array, int x, int y) { xDimension = x; yDimension = y; flatArray = array; } public char this[int x, int y] { get => flatArray[x * yDimension + y]; set => flatArray[x * yDimension + y] = value; } #if UNITY_EDITOR [CustomPropertyDrawer(typeof(Table))] private class TableDrawer : PropertyDrawer { private const int k_elementSpacing = 5; public override float GetPropertyHeight(SerializedProperty property, GUIContent label) { var height = 1f; if (property.isExpanded) { height += property.FindPropertyRelative(nameof(Table.xDimension)).intValue * 1.5f; } return height * EditorGUIUtility.singleLineHeight; } public override void OnGUI(Rect position, SerializedProperty property, GUIContent label) { var foldoutRect = position; foldoutRect.height = EditorGUIUtility.singleLineHeight; foldoutRect.width = EditorGUIUtility.labelWidth; property.isExpanded = EditorGUI.Foldout(foldoutRect, property.isExpanded, label); if (property.isExpanded) { var xRect = position; xRect.height = EditorGUIUtility.singleLineHeight; xRect.x += foldoutRect.width; xRect.width -= foldoutRect.width; xRect.width *= 0.5f; var yRect = xRect; yRect.x += xRect.width; var xDimension = property.FindPropertyRelative(nameof(Table.xDimension)); var yDimension = property.FindPropertyRelative(nameof(Table.yDimension)); var array = property.FindPropertyRelative(nameof(Table.flatArray)); using (var changeCheck = new EditorGUI.ChangeCheckScope()) { EditorGUI.PropertyField(xRect, xDimension, GUIContent.none); EditorGUI.PropertyField(yRect, yDimension, GUIContent.none); if (changeCheck.changed) { array.arraySize = xDimension.intValue * yDimension.intValue; } } position.y += EditorGUIUtility.singleLineHeight * 1.5f; EditorGUI.indentLevel++; var elementRect = EditorGUI.IndentedRect(position); EditorGUI.indentLevel--; var elementWidth = elementRect.width / yDimension.intValue - k_elementSpacing; var elementHeight = EditorGUIUtility.singleLineHeight; var currentPosition = elementRect; currentPosition.width = elementWidth; currentPosition.height = elementHeight; for (var x = 0; x < xDimension.intValue; x++) { for (var y = 0; y < yDimension.intValue; y++) { var element = array.GetArrayElementAtIndex(x * yDimension.intValue + y); EditorGUI.PropertyField(currentPosition, element, GUIContent.none); currentPosition.x += elementWidth + k_elementSpacing; } currentPosition.x = elementRect.x; currentPosition.y += elementHeight * 1.5f; } } } } #endif }
然后宁愿有例如
public Table Table;