簡體   English   中英

在Unity 3D編輯器中復制時,如何確保ScriptableObject是唯一的?

[英]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)時,是否有任何回調/虛擬方法或類似方法告訴我?
  • 另外,在C#或Unity-wise中是否有任何種類的引用計數,可以告訴我對象被引用的頻率? 由於它只是編輯器代碼,因此使用反射的方法也是可以的。
  • 對於完全必須唯一的數據類, 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.

 
粵ICP備18138465號  © 2020-2024 STACKOOM.COM