Hello, there is any way do create a PropertyDrawer that show a enum pop up with the child classes and after select one show in the inspector that class. For example in the next codes:
'code'
public class Animal
{
public virtual void Interact() { }
}
public class Dog: Animal
{
[SerializeField] private int height;
[SerializeField] private string name;
}
public class Cat : Animal
{
[SerializeField] private int size;
[SerializeField] private Vector3 target;
}
public class Farm: MonoBehaviour
{
[SerializeField] private Animal[] animals;
}
I know that is not the best example case Animal class must have some generic variables but it is just for ilustration.
In the Farm class in the Inspector i wanna have the option to select in a popup the derived class (cat ordog) that i wannna use,draw that class and saved in the inspector, something like drag and drop from monobehaviour derived classes but for base class.
I want to do dynamically, beacasue i dont wannt to change the property drawer for each time that i create a new derived class.
Firstly, you need to serialize the field by-reference to enable polymorphism. This is done with the [SerializeReference] attribute: Unity - Scripting API: SerializeReference
Secondly, a PropertyField can draw a SerializedProperty automatically.
Thank you! That helps me a lot. However, my current issue is that I cannot display the class in the Inspector without creating a CustomPropertyDrawer for each class. Additionally, the classes with a CustomPropertyDrawer are not updating as expected.
Here is like i’m trying to draw the class
public override void OnGUI(Rect rect, SerializedProperty property, GUIContent label)
{
EditorGUI.BeginProperty(rect, GUIContent.none, property);
float acumulativeHeigth = EditorGUIUtility.singleLineHeight;
rect.height = EditorGUIUtility.singleLineHeight;
property.isExpanded = EditorGUI.Foldout(rect, property.isExpanded, label, toggleOnLabelClick: true);
SerializedProperty useMonobehaviourProperty = property.FindPropertyRelative("_useMonobehaviour");
SerializedProperty commandMonobehaviourProperty = property.FindPropertyRelative("_commandMonoBehaviour");
SerializedProperty commandClassProperty = property.FindPropertyRelative("_commandClass");
string labelDescription = "Set a Step Command";
if (useMonobehaviourProperty.boolValue)
{
if (commandMonobehaviourProperty.objectReferenceValue != null)
labelDescription = commandMonobehaviourProperty.objectReferenceValue.GetType().ToString();
}
else
{
if (commandMonobehaviourProperty.objectReferenceValue != null)
labelDescription = commandClassProperty.managedReferenceValue.ToString();
}
using (new EditorGUI.DisabledScope(true))
EditorGUI.LabelField(rect, ".", labelDescription);
if (property.isExpanded)
{
rect.y += acumulativeHeigth;
EditorGUI.PropertyField(rect, useMonobehaviourProperty);
rect.y += acumulativeHeigth;
EditorGUI.indentLevel++;
if (useMonobehaviourProperty.boolValue)
EditorGUI.PropertyField(rect, commandMonobehaviourProperty);
else
ShowClassPropertyfield(rect, commandClassProperty);
property.serializedObject.ApplyModifiedProperties();
EditorGUI.indentLevel--;
}
EditorGUI.EndProperty();
}
private void ShowClassPropertyfield(Rect rect, SerializedProperty property)
{
StepCommandClass[] stepCommandClasses = GetStepCommandClasses();
string[] options = new string[stepCommandClasses.Length];
for (int i = 0; i < stepCommandClasses.Length; i++)
options[i] = stepCommandClasses[i].ToString();
string name = String.Empty;
if (property.managedReferenceValue != null)
name = property.managedReferenceValue.ToString();
int currentIndex = GetIndex(options, name);
if (ParametersPopup(rect, "Step class command", currentIndex, options, out int newIndex))
{
property.managedReferenceValue = stepCommandClasses[newIndex];
property.serializedObject.ApplyModifiedProperties();
rect.y += EditorGUIUtility.singleLineHeight;
ConstructorInfo constructor = stepCommandClasses[newIndex].GetType().GetConstructor(Type.EmptyTypes);
if (constructor != null)
{
object value = constructor.Invoke(null);
property.managedReferenceValue = value;
property.serializedObject.ApplyModifiedProperties();
}
EditorGUI.PropertyField(rect, property, new GUIContent(name));
}
}
private StepCommandClass[] GetStepCommandClasses()
{
List<Type> StepCommandList = new List<Type>();
Assembly[] assemblies = AppDomain.CurrentDomain.GetAssemblies();
foreach (Assembly assembly in assemblies)
{
Type[] types = assembly.GetTypes();
IEnumerable<Type> filteredTypes = types
.Where(type => typeof(StepCommandClass).IsAssignableFrom(type) && type.IsClass && !type.IsAbstract && type != typeof(StepCommandClass));
StepCommandList.AddRange(filteredTypes);
}
StepCommandClass[] stepCommandClasses = new StepCommandClass[StepCommandList.Count];
for (int i = 0; i < StepCommandList.Count; i++)
stepCommandClasses[i] = (StepCommandClass)Activator.CreateInstance(StepCommandList[i]);
return stepCommandClasses;
}
private int GetIndex(string[] parameters, string currentSelection)
{
for (int i = 0; i < parameters.Length; i++)
{
if (parameters[i] == currentSelection)
return i;
}
return -1;
}
I tried using an attribute as you did, but I couldn’t display the property (including the custom property drawer). Additionally, using UI Toolkit didn’t work since I’m only working with UGUI.
In my current code, I don’t understand why the value isn’t being updated in the Inspector. I resolved the issue with the property drawer by creating a default drawer for the BaseClass.