Как обеспечить уникальность объектов ScriptableObject при копировании в редакторе Unity 3D? - PullRequest
1 голос
/ 25 марта 2019

В 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();
    }
}

Как я уже сказал, этот код работает без нареканий, за исключениемссылка / клон.Это означает, что когда я меняю строку 1 в GameObject A, строка 1 в скопированном GameObject B также изменяется.Конечно, я мог бы легко клонировать ScriptableObject ссылки с ScriptableObject.Instantiate() - моя проблема в том, что я не знаю, КОГДА клонировать.

Мои вопросы:

  • существует ли какой-либо обратный вызов / виртуальный метод или аналогичный, который сообщает мне, когда мой MonoBehaviour дублируется в редакторе (через C & P или дублированные объекты GameObjects?)
  • В качестве альтернативы, есть ли какой-либо подсчет ссылок в C # или Unity-мудрый, который может сказать мне, как часто объект ссылается?Так как это всего лишь код редактора, метод, использующий отражение, тоже подойдет.
  • Является ли ScriptableObject лучшим выбором для базы данных, которая должна быть уникальной вообще, или есть альтернативы?

Франкхермес уже предложил в комментариях использовать простые классы сериализованных данных.Это прекрасно работает для одного класса, но, к сожалению, не для иерархий классов с базовым классом, поскольку на самом деле они не поддерживаются в сериализации Unity.

1 Ответ

0 голосов
/ 25 марта 2019

Редактировать:

--- Внимание ---

Этот ответ работает только частично.Уникальный идентификатор экземпляра ИЗМЕНЯЕТСЯ между сеансами, что делает его бесполезным для обнаружения и сериализации клонов :( https://answers.unity.com/questions/863084/does-getinstanceid-ever-change-on-an-object.html

.

Нашел решение в на форуме Unity :Сохранение и проверка идентификатора экземпляра GameObject, к которому прикреплен MonoBehaviour, работает очень хорошо (см. 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);
            }
        }
    }

}
Добро пожаловать на сайт PullRequest, где вы можете задавать вопросы и получать ответы от других членов сообщества.
...