[英]Unity references break after scene reload
我在统一中面临一个奇怪的问题,一旦我重新加载场景,引用就会不断中断,我试图了解真正发生的事情,但没有运气。 我制作了一个脚本来复制您可以在下面找到的问题。
当我通过更改大小来编辑最后一个数据元素“列表”时,更改会反映在其他数据对象列表中,因为它们被视为引用。 .
如果我重新加载场景,更改将不再像以前那样反映,这次它的行为类似于副本而不是引用。
有人可以帮我弄清楚发生了什么吗? .
using System.Collections;
using System.Collections.Generic;
using UnityEditor;
using UnityEngine;
public class test : MonoBehaviour
{
public List<data> Data = new List<data>();
}
[System.Serializable]
public class data
{
public List<int> list = new List<int>();
}
[CustomEditor(typeof(test))]
public class testEditor:Editor
{
test test;
public void OnEnable()
{
test = (test)target;
}
public override void OnInspectorGUI()
{
DrawDefaultInspector();
if (GUILayout.Button("Add"))
{
data data = new data();
if (test.Data.Count >= 1) data.list = test.Data[test.Data.Count - 1].list;
test.Data.Add(data);
EditorUtility.SetDirty(test);
}
if (GUILayout.Button("Clear"))
{
test.Data.Clear();
EditorUtility.SetDirty(test);
}
}
}
一般而言:不要直接访问和更改MonoBehaviour
实例的值!
正如您所指出的,您将不得不处理各种肮脏的标记并拯救自己。 当您在编辑器中重新打开场景时,您会遇到一些未正确标记为脏的内容,因此未与场景一起保存。
总是通过SerializedProperty
s 来处理所有标记为脏和保存,尤其是自动撤消/重做等:
[CustomEditor(typeof(test))]
public class testEditor : Editor
{
private SerializedProperty Data;
public void OnEnable()
{
Data = serializedObject.FindProperty(nameof(test.Data));
}
public override void OnInspectorGUI()
{
DrawDefaultInspector();
// load all current values of the properties in test into the SerializedProperty "clones"
serializedObject.Update();
if (GUILayout.Button("Add"))
{
// this simply adds a new entry to the list
// since the data and list are both serializable this already initializes them with values
Data.arraySize++;
// Actually the entire following block is redundant
// by using Data.arraySize++; the new added entry automatically
// is a full copy of the entry before!
// I just decided to add it as example how you would access further nested SerializedProperties
//// if there was an element before now there are two
//if (Data.arraySize >= 2)
//{
// // get the last added element
// var lastElement = Data.GetArrayElementAtIndex(Data.arraySize - 1);
// var beforeElement = Data.GetArrayElementAtIndex(Data.arraySize - 2);
// // deep clone the list
// var lastElementList = lastElement.FindPropertyRelative(nameof(data.list));
// var beforeElementList = beforeElement.FindPropertyRelative(nameof(data.list));
// lastElementList.arraySize = beforeElementList.arraySize;
// for (var i = 0; i < lastElementList.arraySize; i++)
// {
// lastElementList.GetArrayElementAtIndex(i).intValue = beforeElementList.GetArrayElementAtIndex(i).intValue;
// }
//}
}
if (GUILayout.Button("Clear"))
{
Data.arraySize = 0;
}
// write back the values of the SerializedProperty "clones" into the real properties of test
serializedObject.ApplyModifiedProperties();
}
}
这现在处理所有标记为脏,正确保存场景,自动撤消/重做等,您不必再关心这些了。
然后是一点“专业”提示:使用ReorderableList
! 设置看起来有点棘手,但功能非常强大:除此之外,正如名称所说,它允许您通过在 Inspector 中拖放来简单地重新排序元素,还允许从中间删除一个项目,这是不可能的普通列表抽屉。 这完全取代了您的Add
和Clear
按钮:
using UnityEditor;
using UnityEditorInternal;
[CustomEditor(typeof(test))]
public class testEditor : Editor
{
private SerializedProperty Data;
private ReorderableList dataList;
public void OnEnable()
{
Data = serializedObject.FindProperty(nameof(test.Data));
// should the list
// | be reorderable by drag&drop of the entries?
// | | display a header for the list?
// | | | have an Add button?
// | | | | have a Remove button?
// v v v v
dataList = new ReorderableList(serializedObject, Data, true, true, true, true)
{
// what shall be displayed as header
drawHeaderCallback = rect => EditorGUI.LabelField(rect, Data.displayName),
elementHeightCallback = index =>
{
var element = Data.GetArrayElementAtIndex(index);
var elementList = element.FindPropertyRelative(nameof(data.list));
return EditorGUIUtility.singleLineHeight * (elementList.isExpanded ? elementList.arraySize + 4 : 3);
},
drawElementCallback = (rect, index, isFocused, isActive) =>
{
var element = Data.GetArrayElementAtIndex(index);
EditorGUI.LabelField(new Rect(rect.x,rect.y,rect.width,EditorGUIUtility.singleLineHeight), element.displayName);
// in order to print the list in the next line
rect.y += EditorGUIUtility.singleLineHeight;
var elementList = element.FindPropertyRelative(nameof(data.list));
EditorGUI.PropertyField(new Rect(rect.x, rect.y, rect.width, EditorGUIUtility.singleLineHeight * (elementList.isExpanded ? elementList.arraySize + 1 : 1)), elementList, true);
}
};
}
public override void OnInspectorGUI()
{
// load all current values of the properties in test into the SerializedProperty "clones"
serializedObject.Update();
dataList.DoLayoutList();
// write back the values of the SerializedProperty "clones" into the real properties of test
serializedObject.ApplyModifiedProperties();
}
}
注意如果不是这样的话: testEditor
部分应该
Editor
的文件夹中的不同脚本中或者您应该在预处理器中包装与UnityEditor
命名空间相关的任何内容,例如
#if UNITY_EDITOR using UnityEditor; using UnityEditorInternal; #endif ... #if UNITY_EDITOR [CustomEditor(typeof(test))] public class testEditor : Editor { ... } #endif
否则,您将在构建应用程序时遇到错误,因为UnityEditor
命名空间在构建中被剥离并且仅存在于 Unity 编辑器本身中。
声明:本站的技术帖子网页,遵循CC BY-SA 4.0协议,如果您需要转载,请注明本站网址或者原文地址。任何问题请咨询:yoyou2525@163.com.