How to draw in editor an object without UnityEngine.Object?

I would like to draw a property right after instantiation, without reference to any Object. I saw that in Odin it is possible to do this using a PropertyTree, but I would like to get the same result without depending on this Asset.

I’ve tried every way, but I can only do this using SerializedObject or SerializedProperty, both depend on Object for this, and I would like the object itself to be fully independent of inheritances or context.

Here is the example of the object I’m trying to draw.

    [Serializable]
    public class Test
    {
        [SerializeField] public GameObject go;
        [SerializeField] public int integer;
    }

    public class TestDrawer : EditorWindow
    {
        private Test test;

        private void OnEnable()
        {
            test = new Test();
        }

        private void CreateGUI()
        {
            // TODO: Draw test by VisualElement
        }

        private void OnGUI()
        {
            // TODO: Draw test by GUI
        }
    }

I think you just need a custom property drawer… check those out.

Yes you just use property drawers: Unity - Scripting API: PropertyDrawer

I believe that PropertyDrawer does not solve my problem; I’ll show my code to clarify

public abstract class MenuEditorWindow : EditorWindow
    {
        /// <summary> Last selected asset </summary>
        protected ContentUI SelectedContent { get; private set; }

        /// <summary> Contains all ContentUI </summary>
        protected MenuTree Tree { get; private set; }

        private UnityEditor.Editor selectionEditor;

        #region GUI + Editor Window Areas
        private void OnGUI()
        {
            // Left panel button tree
            DrawMenuTree();

            // Right panel displaying current object
            selectionEditor.DrawDefaultInspector();
        }

        private void DrawHierarchyBtn(ContentUI content)
        {
            // Check if is the current select object
            GUI.enabled = SelectedContent != content;
            if (GUILayout.Button(content.Title, content.ButtonIcon))
            {
                SelectActiveContent(content);
            }
        }

        private void SelectActiveContent(ContentUI content)
        {
            SelectedContent = content;
            selectionEditor = UnityEditor.Editor.CreateEditor(content.Target);
        }

        // DrawMenuTree logic...

    }

This code results in this editor window

This is drawable, because content.Target inherits from ScriptableObjects, so it also inherits from UnityEngine.Object, and that lets me use Editor.CreateEditor(); However this is not possible with objects that do not inherit anything.

Using Odin’s PropertyTree this is possible, you can pass any type of object, regardless of its inheritance and the Editor is able to draw it perfectly.

I would like to be able to create the right panel from any type of object, but without Odin, using only Unity’s base classes. How can I do this?

If it is possible to do this using PropertyDrawers please show me

You’re going to have to work with serialised properties and other Unity IMGUI tools like PropertyField: Unity - Scripting API: EditorGUILayout.PropertyField

So if the object is not a Unity Object you’ll need to handle it differently. There’s no one size fits all solution here, as you’ll probably have other edge cases to deal with.

Needless to say Odin spoils us a bit with it’s far better systems.

I tried using EditorGUILayout.PropertyField but the arguments force me to use SerializedProperty which depends on UnityEngine.Object :frowning:

Indeed, Odin has an excellent system to deal with this!! But I would still like to do this.

Well Unity can only know what to draw if it has access to the data structure of the object. You will still need access to the root UnityEngine.Object (as all serialisation starts at the root Unity object), after which you can find the appropriate SerializedProperty like so: https://docs.unity3d.com/ScriptReference/SerializedObject.FindProperty.html

Again, no one size fits all solution here. It’s going to take a bit of work.

I tried it in two different ways

1 - Instantiating a ScriptableObject with my object inside, in this case “instance” was not able to find the property’s path. Declaring it finds the path, but that way doesn’t solve my problem, because I need to work with object.

public class DevelopmentToolsWindow : EditorWindow
{
   
       // Show Window code here...

        public void CreateGUI()
        {
            DrawProperty<LevelDesignTools>(rootVisualElement);
        }

        private void DrawProperty<T>(VisualElement root)
        {
            MyTestObject myObj = ScriptableObject.CreateInstance<MyTestObject>();
            instance.Set<T>();
            SerializedObject serializedObject = new SerializedObject(myObj);

            PropertyField property = new PropertyField();
            property.bindingPath = nameof(myObj.instance);
            property.Bind(serializedObject);

            root.Add(property);
        }
    }

    public class MyTestObject : ScriptableObject
    {
        [SerializeField] public object instance;
        [SerializeField] public LevelDesignTools test;

        public void Set<T>()
        {
            instance1 = Activator.CreateInstance(typeof(T));
        }
    }

2 - It also doesn’t work with a generic type, because it is not possible to instantiate MyTestGeneric and you end up falling in the first case if you inherit from the “object” type

 public class MyTestGeneric<T> : ScriptableObject
    {
        [SerializeField] public T instance1;
        [SerializeField] public LevelDesignTools instance2;
    }

    public class MyObject : MyTestGeneric<object> { }

Well it’s not going to work because Unity’s default SerializeField cannot serialise System.Object. You might want to try SeralizeReference in that case.

Remember the whole system works around Unity’s ability to read the object’s serialised data structure. If it’s not being serialised it won’t work.

THAT WORK!! Thank you Spiney! :slight_smile:

This is the final result. Using SerializedProperty it will be very similar.

public static class PropertyBuilder
{
    public static void DrawProperty<T>(VisualElement root)
    {
       GenericProperty instance = ScriptableObject.CreateInstance<GenericProperty>();
       instance.property = Activator.CreateInstance<T>();
       SerializedObject serializedObject = new SerializedObject(instance);

       PropertyField property = new PropertyField();
       property.bindingPath = nameof(instance.property);
       property.Bind(serializedObject);

       root.Add(property);

       BuildAttributes(instance.property, root);
   }
}


public class GenericProperty : ScriptableObject
{
  [SerializeReference] public object property;
}

Of course, this code doesn’t look like clean code, it looks like a big hack, but in the end it works. Hahaha

1 Like

Glad it’s working!

I will say, you could probably do this without the need to make wrapper objects.

So long as you have access to the unity object this data is stored in, you can walk along it’s entire serialised property structure and just draw them. How you select these properties is another matter, but definitely not impossible.