Is there a class/function similar to InspectorElement for property drawers?

Hi everyone,

The solution is right after the problem.

PROBLEM:

I have 2 C# classes, one for a slot and another for an item:

[System.Serializable]
public class ItemSlot
{
    [SerializeField] int amount = 1;
    [SerializeField] Item item;

    public ItemSlot(Item item, int amount)
    {
        this.item = item;
        this.amount = amount;
    }
}

[System.Serializable]
public class Item
{
    [SerializeField] ItemData data;
}

I have other classes that derive from the Item class. What I would like to do is having a dropdown with all the different items and when one is picked, show its property drawer.

[CustomPropertyDrawer(typeof(ItemSlot))]
public class ItemSlotPreview : PropertyDrawer
{
    public override VisualElement CreatePropertyGUI(SerializedProperty property)
    {
        VisualElement root = base.CreatePropertyGUI(property);
        root.Add(new DropdownField("Item", new List<string>() { "Weapon", "Sword" }, 0));
        root.Add(new PropertyField(new Item()));
        return root;
    }
}

Also in this example, VisualElement root = base.CreatePropertyGUI(property); gives root as null and when I try the following:

[CustomPropertyDrawer(typeof(ItemSlot))]
public class ItemSlotPreview : PropertyDrawer
{
    public override VisualElement CreatePropertyGUI(SerializedProperty property)
    {
        return base.CreatePropertyGUI(property);
    }
}

Here is the result:

9926535--1436049--upload_2024-7-6_11-24-36.png

SOLUTION:

To avoid reading the entire conversation, here is the answer to that question.

The solution is a bit more complex than with UnityEngine.Object since we cannot use InspectorElement for a simple C# class.

I found the following git repository, which works great, but it does not use VisualElement, which I wanted to use to easily make changes.

@spiney199 provided the following which is 2 simple classes, the attribute + custom inspector that uses VisualElement.

1 Like

ItemSlot and Item are not marked as [System.Serializable] so they won’t get serialized nor serialize their fields.

They are, I just did not copy past it.

I mean if you… simply looked at the source code, you’d see:

public virtual VisualElement CreatePropertyGUI(SerializedProperty property)
{
    return null;
}

You’re expected to instance a root and then return that, like so:

public override VisualElement CreatePropertyGUI(SerializedProperty property)
{
    VisualElement root = new VisualElement();
    root.Add(new DropdownField("Item", new List<string>() { "Weapon", "Sword" }, 0));
    root.Add(new PropertyField(new Item()));
    return root;
}
2 Likes

The PropertyDrawer docs cover that.

You can make a property drawer apply to derived types by passing true as the second argument to CustomPropertyDrawerAttribute.
Simple field serialization doesn’t handle polymorphism, by the way. One would probably need to lean on SerializeReferenceAttribute.

1 Like

Thanks but, CreateInspectorGUI for an UnityEngine.Object class returns null too but with the following code, it returns the default inspector.

public override VisualElement CreateInspectorGUI()
{
    VisualElement root = new VisualElement();
    root.Add(new IMGUIContainer(OnInspectorGUI));
    return root;
}

I was looking for something similar but for property drawers.

Also, the following line does not work:

root.Add(new PropertyField(new Item()));

It was just an example of the result I have been looking for.

So there are no ways in a property drawer to make use of a similar function as new InspectorElement(). The only method would be to use a custom attribute?

I have found this git repository but I couldn’t understand how it works and I like to understand what I use. So I tried to redo that, but so far without success.

A PropertyField is the correct way to generically draw a serialisable class, but you don’t throw a new instance to it, you need to bind it to a respective SerializedProperty so that the changes to it reflect on the serialised data correctly.

Again, just read the documentation. It’s literally all there in the link Spy-Master shared.

1 Like

I don’t have to “elaborate” on that since you understood the problematic.

How do I create a SerializedProperty from a C# class?

Read the documentation Spy-Master linked.

2 Likes

I just read it twice to be sure and maybe I stupid but I really don’t see where I can find my solution inside this article.

Maybe you pointed me to the following line property.FindPropertyRelative("item"); but if the item is the type of sword (class which derives from item), the property returned is the type of Item and not sword.

Also, I don’t really see the difference between the attribute and the custom property drawer, in the sense of functionality. Both seem to be able to do the same but one can be use everywhere and the other is constrained to a specific class.

I don’t see where to display the property drawer of any class inside another one.

What they’re saying is literally just use SerializeReference instead of SerializeField on the Item field, otherwise it will only ever be serialised as Item as Unity’s default serialisation does not support polymorphism.

I wrote a simply property drawer to enable subtype selection with SerialiseReference here: https://discussions.unity.com/t/948612/15

2 Likes

This is exactly what I was looking for. And it is even simplier than the git repository I found.

It seems that there is an issur when I select a subclass, it appears as expected, but when a class is selected after selecting null, it does not appear. I am trying to fix this but I am not sure where it is coming from. Otherwise it’s very nice to use.

Not sure why that might happen but I can look into that later.

1 Like