[英]How to ensure ScriptableObjects are unique when copied in the Unity 3D editor?
在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.Instantiate()
克隆ScriptableObject
引用-我的問題是,我不知道何時克隆。
我的問題是:
MonoBehaviour
(通過C&P或復制的GameObjects)時,是否有任何回調/虛擬方法或類似方法告訴我? ScriptableObject
是最佳選擇嗎?還是有替代方法? 注釋中已經建議frankhermes使用簡單的序列化數據類。 這對於單個類來說效果很好,但不幸的是,對於具有基類的類層次結構而言,效果並不理想,因為afaik在Unity序列化中不支持它們。
編輯:
-注意-
該答案僅部分起作用。 會話之間唯一的實例ID會發生變化,這使得它無法用於克隆檢測和序列化:( https://answers.unity.com/questions/863084/does-getinstanceid-ever-change-on-an-object.html
。
在Unity論壇上找到了解決方案:存儲並檢查MonoBehaviour
附加到的GameObject
的實例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);
}
}
}
}
聲明:本站的技術帖子網頁,遵循CC BY-SA 4.0協議,如果您需要轉載,請注明本站網址或者原文地址。任何問題請咨詢:yoyou2525@163.com.