I am creating a custom drawer for a custom attribute that helps me initialize the value of a field marked with the attribute SerializeReference
.
Basically, the custom drawer will show a dropdown menu that allows me to select the Type
to create and assign to the field.
I have the following code so far, to test the different scenarios:
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();
}
}
And those are the test classes:
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(){}
}
The above code seems to work fine, it initializes the property to a new instance of the OperaSinger
and shows the editor.
But, when I try to do the same with the MonoBehaviour
implementation, I get an error:
Attempt #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();
}
}
Error: [SerializeReference] cannot serialize objects that derive from Unity.Object
.
Attempt #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();
}
}
Error: type is not a supported pptr value
Attempt #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();
}
}
Error: type is not a supported pptr value
What am I doing wrong, and how can I fix it?
As far as I understand, the error from attempts 2 and 3 is because the field is defined as the interface type ISinger
.
CodePudding user response:
Well, after trying the following:
[SerializeReference] private ISinger singer;
private void Reset()
{
singer = gameObject.AddComponent<StreetPerformer>()
}
And getting the same error as attempt 1, I realized that it is not possible to do it.
So, the solution I came up with is creating a wrapper-class to the StreetPerformer
class that will delegate all methods to it like so:
internal sealed class StreetPerformer_ISing_Wrapper : ISing
{
[SerializeField] private StreetPerformer _instance;
public void Sing()
{
_instance.Sing();
}
}
Obviously, doing so to every class that I need will be tedious so I simply wrote a code generator that creates (and updates) this class for me.
So now I can have an interface field in my script that references a regular class, a ScriptableObject or a MonoBehaviour with minimum effort