Почему не сериализуются ссылки на объекты в SerializedProperty.boxedValue?

Продолжаю работу над окнами диалогов CustomEditorWindow. Диалоги содержат все необходимые публичные поля, далее в OnGui само окно сериализуется в SerializedObject, который выводится при помощи PropertyDrawer и EditorGuiLayout в окошке. Вот как-то так:

public void OnGUI()
{
  so = new SerializedObject(this);
  GUILayout.BeginVertical();
  {
    so.Update();
    EditorGUILayout.LabelField(EditorWindow.focusedWindow.ToString());
    EditorGUILayout.LabelField("Данные для сравнения");
    EditorGUILayout.PropertyField(so.FindProperty("arg1"));
    if (!string.IsNullOrEmpty(arg1?.ToString()) && compareType == SearchCondition.CompareType.None)
      compareType = arg1.notVal ? SearchCondition.CompareType.NotExist : SearchCondition.CompareType.Exist;
    EditorGUILayout.PropertyField(so.FindProperty("compareType"), GUIContent.none);
    var argProp = so.FindProperty("arg2");
    if (argProp != null)
      EditorGUILayout.PropertyField(argProp);

    so?.ApplyModifiedProperties();
    EditorGUILayout.BeginHorizontal();
    {
      if (GUILayout.Button("Установить"))
        SetValue();
      if (GUILayout.Button("Отмена"))
        this.Close();
    }
    EditorGUILayout.EndHorizontal();
  }
  GUILayout.EndVertical();
}

Возникла потребность при формировании диалога инициировать его ссылкой на компонент. Для этого добавил атрибут в котором передается название поля или метода откуда следует извлекать ссылку.

Объекты arg1 и arg2, это простой класс в основном состоящий из value значений. Но для того чтобы их настроить написан свой PropertyDrawer, который открывает диалог, а диалог должен быть инициирован ссылкой на компонент в качестве входящего параметра. Эта ссылка устанавливается в arg вручную до отрисовки, а arg по задумке должен ее извлекать перед созданием диалога и в редакторе создав диалог инициировать его.

С какой проблемой столкнулся. Если ссылка хранится в serializedObject, то мы спокойно ее получаем через parentProperty.serializedObject.targetObject. Но беда в том, что когда PropertyDrawer для arg1 пытается получить внутри себя ссылку из своего свойства Result, то на выходе получается null.

По факту получается в что в Window лежит ArgData, а внутри ArgData есть публичное свойство Result. В отладчике я вижу в SerializedObject.targetObject.arg1.Result, что это свойство заполнено. Но если я получаю serializedProperty для arg1 и пытаюсь через Reflection извлечь ArgData из boxedValue, то вижу в дебагере что все поля со ссылками null, а значимые заданы. При этом у сериализованного свойства arg1 ссылка SerializedObject.targetObject ссылается на окно и если дальше от него спускаться и проверить то Result содержит требуемую ссылку.

Пока вижу только один вариант. Парсить путь для serializedProperty.propertyPath и от SerializedObject.targetObject через Reflection получать значение на всю глубину serializedProperty.depth. Будут заморочки со списками поскольку propertyPath в формате Unity (типа такого args.Array.data[1].pathVal), но вроде все решаемо.

Может я как всегда все усложняю и есть более простой и правильный способ?


Ответы (1 шт):

Автор решения: Yaroslav

Как я уже писал ранее, если ты создаёшь SerializedObject сам, это значит, что ты делаешь какую то херню и приводит это к какой-то херне. В документации даже не описан его конструктора, как бы намекая что для тебя, его НЕ СУЩЕСТВУЕТ! Ты лезешь в работу редактора, творя дичь и удивляешься, что от твоего SerializedObject, получаются пустые SerializedProperty.

Диалоговое окно это окошко с текстом и кнопками да/нет, никаких ссылок он не принимает. Что ты называешь диалогом, одному тебе известно.


Что мы должны увидеть в этом OnGUI? Груду лейаутов и монструозный if? А this это ваще чё? Это некий ArgData... а он чьи будет, от кого наследуется?

Почему в вертикали ты используешь GUILayout а в горизонтали EditorGUILayout? Грязь! IMGUI без пяти минут legacy и нужно переходить на Toolkit!


Существует один единственный способ отрисовки UnityEngine.Object и это Editor, который наследуется от ScriptableObject. Об этом чёрным по английски написано в документации, в первом же предложении:

Derive from this base class to CREATE a custom INSPECTOR OR EDITOR FOR your custom OBJECT.

Всё что требуется, это передать этот объект как target. То есть не нужно создавать никаких ScriptableObject и SerializedObject и заниматься самодеятельностью, все инструменты уже есть и процедуры написаны, какими они должны быть, без ошибок.

А PropertyDrawer используется для остальных сериализуемых типов, например struct. Такого объекта как ScriptableObject там существовать не должно ВООБЩЕ!, от слова "СОВСЕМ", ни в каком виде, на расстоянии пушечного выстрела, что можно заметить по документации. Он работает только SerializedProperty, полученным через аргумент в OnGUI или CreatePropertyGUI и всё.


Пытаешься из arg1 получить ArgData, который является обладателем поля arg1? Проворачиваем фарш обратно? Не понимая что творишь полную херню? Что бы что? Почему нельзя просто передать ArgData целиком? как target в новый Editor?


Может я как всегда все усложняю и есть более простой и правильный способ?

ДА! Максимально ужасным образом!

Лень всему голова, но в итоге потратишь больше времени и нервов пожиная последствия. Прошло почти 2 месяца, а ты как мышь из анекдота: плачет, колется, но продолжает упорно есть кактус.

→ Ссылка