在Unity 3D中,我有一个MonoBehaviour
,其中包含所有基于共同的ScriptableObject
的类派生列表。
该列表将在自定义编辑器中填充和处理。这可以正常工作,但有一个例外:每当我复制/粘贴MonoBehaviour
或在Unity编辑器中复制持有该对象的游戏对象时,我的列表仅包含实例,而不包含唯一的克隆。
这是一些示例代码(请注意,这只是简化的测试代码,我的实际类复杂得多,需要单独的数据类):
using System.Collections.Generic;
using UnityEditor;
using UnityEngine;
public abstract class MyAbstractBaseClass : ScriptableObject
{
public abstract void foo();
}
public class MyTestScriptableObject : MyAbstractBaseClass
{
public string stringMember;
public override void foo()
{
}
}
public class MyTestMonoBehaviour : MonoBehaviour
{
public List<MyAbstractBaseClass> testList;
}
[CustomEditor(typeof(MyTestMonoBehaviour))]
public class MyTestMonoBehaviourEditor : Editor
{
const int NUM_LISTENTRIES = 5;
public override void OnInspectorGUI()
{
SerializedProperty testListProp = serializedObject.FindProperty("testList");
for (int i = 0; i < testListProp.arraySize; i++)
{
SerializedObject myTestScriptableObjectSO = new SerializedObject(testListProp.GetArrayElementAtIndex(i).objectReferenceValue);
SerializedProperty stringMemberProp = myTestScriptableObjectSO.FindProperty("stringMember");
EditorGUILayout.PropertyField(stringMemberProp);
myTestScriptableObjectSO.ApplyModifiedProperties();
}
if( GUILayout.Button("Generate List"))
{
testListProp.arraySize = NUM_LISTENTRIES;
for( int i=0; i<NUM_LISTENTRIES; i++)
testListProp.GetArrayElementAtIndex(i).objectReferenceValue = ScriptableObject.CreateInstance<MyTestScriptableObject>();
}
serializedObject.ApplyModifiedProperties();
}
}
就像我说的那样,除了引用/克隆问题之外,这段代码可以完美地工作。这意味着,当我更改游戏对象A上的字符串1时,复制的游戏对象B上的字符串1也将更改。
我当然可以轻松地用ScriptableObject
克隆ScriptableObject.Instantiate()
引用-我的问题是,我不知道何时克隆。
我的问题是:
MonoBehaviour
时(通过C&P或复制的GameObjects),是否有任何回调/虚拟方法或类似方法告诉我?ScriptableObject
是必须完全唯一的数据类基础的最佳选择,还是有替代方法?frankhermes使用简单的序列化数据类。对于单个类来说,这很好用,但是不幸的是,对于具有基类的类层次结构而言,这不是可行的,因为afaik在Unity序列化中不支持它们。
答案 0 :(得分:0)
编辑:
---注意---
此答案仅部分起作用。会话之间唯一的实例ID更改,这使得它无法用于克隆检测和序列化:( https://answers.unity.com/questions/863084/does-getinstanceid-ever-change-on-an-object.html
。
在in the Unity forum找到了解决方案:
存储并检查GameObject
附加到的MonoBehaviour
的实例ID很好(请参阅MakeListElementsUnique()
)。
唯一失败的情况是复制MonoBehaviour
并将其粘贴到相同的GameObject
。但这对于我的用例和我猜想的许多其他情况来说并不是一个真正的问题。
using System.Collections.Generic;
using UnityEditor;
using UnityEngine;
public abstract class MyAbstractBaseClass : ScriptableObject
{
public abstract void foo();
}
public class MyTestScriptableObject : MyAbstractBaseClass
{
public string stringMember;
public override void foo()
{
}
}
public class MyTestMonoBehaviour : MonoBehaviour
{
public int instanceID = 0;
public List<MyAbstractBaseClass> testList;
}
[CustomEditor(typeof(MyTestMonoBehaviour))]
public class MyTestMonoBehaviourEditor : Editor
{
const int NUM_LISTENTRIES = 5;
public override void OnInspectorGUI()
{
MyTestMonoBehaviour myScriptableObject = (MyTestMonoBehaviour)target;
SerializedProperty testListProp = serializedObject.FindProperty("testList");
MakeListElementsUnique(myScriptableObject, testListProp);
for (int i = 0; i < testListProp.arraySize; i++)
{
SerializedObject myTestScriptableObjectSO = new SerializedObject(testListProp.GetArrayElementAtIndex(i).objectReferenceValue);
SerializedProperty stringMemberProp = myTestScriptableObjectSO.FindProperty("stringMember");
EditorGUILayout.PropertyField(stringMemberProp);
myTestScriptableObjectSO.ApplyModifiedProperties();
}
if (GUILayout.Button("Generate List"))
{
testListProp.arraySize = NUM_LISTENTRIES;
for (int i = 0; i < NUM_LISTENTRIES; i++)
testListProp.GetArrayElementAtIndex(i).objectReferenceValue = ScriptableObject.CreateInstance<MyTestScriptableObject>();
}
serializedObject.ApplyModifiedProperties();
}
private void MakeListElementsUnique( MyTestMonoBehaviour scriptableObject, SerializedProperty testListProp )
{
SerializedProperty instanceIdProp = serializedObject.FindProperty("instanceID");
// stored instance id == 0: freshly created, just set the instance id of the game object
if (instanceIdProp.intValue == 0)
{
instanceIdProp.intValue = scriptableObject.gameObject.GetInstanceID();
}
// stored instance id != current instance id: copied!
else if (instanceIdProp.intValue != scriptableObject.gameObject.GetInstanceID())
{
// don't forget to change the instance id to the new game object
instanceIdProp.intValue = scriptableObject.gameObject.GetInstanceID();
// make clones of all list elements
for (int i = 0; i < testListProp.arraySize; i++)
{
SerializedProperty sp = testListProp.GetArrayElementAtIndex(i);
sp.objectReferenceValue = Object.Instantiate(sp.objectReferenceValue);
}
}
}
}