Ящик настраиваемых свойств Unity задает для `SerializeReference` значение UnityEngine.Object

Я создаю пользовательский ящик для пользовательского атрибута, который помогает мне инициализировать значение поля, отмеченного атрибутом SerializeReference.

По сути, в пользовательском ящике будет отображаться раскрывающееся меню, которое позволяет мне выбрать Type для создания и назначения полю.

Пока у меня есть следующий код для тестирования различных сценариев:

public override void OnGUI(Rect position, SerializedProperty property, GUIContent label)
{
    var targetObject = property.serializedObject.targetObject;
    var field = targetObject.GetType().GetField(property.name, FieldBindingFlags)!;

    if (field.GetValue(targetObject) == null)
        field.SetValue(targetObject, new OperaSinger());
    else
    {
        EditorGUI.BeginProperty(position, label, property);
        EditorGUI.PropertyField(position, property, label, true);
        EditorGUI.EndProperty();
    }
}

И это тестовые классы:

interface ISinger
{
    void Sing();
}

[System.Serializable]
class OperaSinger : ISinger
{
    [SerializeField] private string name;
    void Sing(){}
}

class StreetPerformer : MonoBehaviour, ISinger
{
    [SerializeField] private string streetName;
    void Sing(){}
}

Приведенный выше код работает нормально, он инициализирует свойство новым экземпляром OperaSinger и показывает редактор.

Но когда я пытаюсь сделать то же самое с реализацией MonoBehaviour, я получаю сообщение об ошибке:

Попытка №1:

public override void OnGUI(Rect position, SerializedProperty property, GUIContent label)
{
    var targetObject = property.serializedObject.targetObject;
    var field = targetObject.GetType().GetField(property.name, FieldBindingFlags)!;

    if (field.GetValue(targetObject) == null)
    {
        var x = ((Component)targetObject).gameObject.AddComponent<StreetPerformer>();
        field.SetValue(targetObject, x);
    }
    else
    {
        EditorGUI.BeginProperty(position, label, property);
        EditorGUI.PropertyField(position, property, label, true);
        EditorGUI.EndProperty();
    }
}

Ошибка: [SerializeReference] cannot serialize objects that derive from Unity.Object.


Попытка №2:

public override void OnGUI(Rect position, SerializedProperty property, GUIContent label)
{
    var targetObject = property.serializedObject.targetObject;
    var field = targetObject.GetType().GetField(property.name, FieldBindingFlags)!;

    if (field.GetValue(targetObject) == null)
    {
        var x = ((Component)targetObject).gameObject.AddComponent<StreetPerformer>();
        property.objectReferenceValue = x;
    }
    else
    {
        EditorGUI.BeginProperty(position, label, property);
        EditorGUI.PropertyField(position, property, label, true);
        EditorGUI.EndProperty();
    }
}

Ошибка: type is not a supported pptr value


Попытка №3:

public override void OnGUI(Rect position, SerializedProperty property, GUIContent label)
{
    var targetObject = property.serializedObject.targetObject;
    var field = targetObject.GetType().GetField(property.name, FieldBindingFlags)!;

    if (field.GetValue(targetObject) == null)
    {
        var x = ((Component)targetObject).gameObject.AddComponent<StreetPerformer>();
        property.objectReferenceInstanceIDValue = x.GetInstanceID();
    }
    else
    {
        EditorGUI.BeginProperty(position, label, property);
        EditorGUI.PropertyField(position, property, label, true);
        EditorGUI.EndProperty();
    }
}

Ошибка: type is not a supported pptr value


Что я делаю неправильно, и как я могу это исправить?

Насколько я понимаю, ошибка с попытки 2 и 3 связана с тем, что поле определено как тип интерфейса ISinger.

Стоит ли изучать PHP в 2026-2027 годах?
Стоит ли изучать PHP в 2026-2027 годах?
Привет всем, сегодня я хочу высказать свои соображения по поводу вопроса, который я уже много раз получал в своем сообществе: "Стоит ли изучать PHP в...
Поведение ключевого слова "this" в стрелочной функции в сравнении с нормальной функцией
Поведение ключевого слова "this" в стрелочной функции в сравнении с нормальной функцией
В JavaScript одним из самых запутанных понятий является поведение ключевого слова "this" в стрелочной и обычной функциях.
Приемы CSS-макетирования - floats и Flexbox
Приемы CSS-макетирования - floats и Flexbox
Здравствуйте, друзья-студенты! Готовы совершенствовать свои навыки веб-дизайна? Сегодня в нашем путешествии мы рассмотрим приемы CSS-верстки - в...
Тестирование функциональных ngrx-эффектов в Angular 16 с помощью Jest
В системе управления состояниями ngrx, совместимой с Angular 16, появились функциональные эффекты. Это здорово и делает код определенно легче для...
Концепция локализации и ее применение в приложениях React ⚡️
Концепция локализации и ее применение в приложениях React ⚡️
Локализация - это процесс адаптации приложения к различным языкам и культурным требованиям. Это позволяет пользователям получить опыт, соответствующий...
Пользовательский скаляр GraphQL
Пользовательский скаляр GraphQL
Листовые узлы системы типов GraphQL называются скалярами. Достигнув скалярного типа, невозможно спуститься дальше по иерархии типов. Скалярный тип...
0
0
63
1
Перейти к ответу Данный вопрос помечен как решенный

Ответы 1

Ответ принят как подходящий

Ну, попробовав следующее:

[SerializeReference] private ISinger singer;

private void Reset()
{
    singer = gameObject.AddComponent<StreetPerformer>()
}

И получив ту же ошибку, что и попытка 1, я понял, что это невозможно сделать.

Итак, решение, которое я придумал, заключается в создании класса-оболочки для класса StreetPerformer, который будет делегировать ему все методы следующим образом:

internal sealed class StreetPerformer_ISing_Wrapper : ISing
{
    [SerializeField] private StreetPerformer _instance;

    public void Sing()
    {
        _instance.Sing();
    }

}

Очевидно, что делать это для каждого класса, который мне нужен, будет утомительно, поэтому я просто написал генератор кода, который создает (и обновляет) этот класс для меня.

Так что теперь у меня может быть поле интерфейса в моем скрипте, которое ссылается на обычный класс, ScriptableObject или MonoBehaviour с минимальными усилиями 😁

Другие вопросы по теме