Я создаю пользовательский ящик для пользовательского атрибута, который помогает мне инициализировать значение поля, отмеченного атрибутом 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, я получаю сообщение об ошибке:
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.
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
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.





Ну, попробовав следующее:
[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 с минимальными усилиями 😁