Home > database >  Unity Simple Property Drawer or Popup to display custom object-array scriptable object
Unity Simple Property Drawer or Popup to display custom object-array scriptable object

Time:12-01

I'm still quite new to unity and c# and trying to replicate a seemingly simple custom editor and property drawer for data i'm preparing via scriptable object. In this case a class to use multiple tags on a gameObject, to identify what is what quickly when lots of them are detected by a sensor. I'm on that since way too long and questioning my sanity because it can't be that hard. I'm just lacking some rather basic knowledge/understanding, i think. The whole concept around SerializedProperty and the handling of it is very unintuitive for me.

Found this handy code-snippet here to create LayerMasks containing multiple layers:

using UnityEditor;
using UnityEngine;

[CustomPropertyDrawer(typeof(SingleUnityLayer))]
public class SingleUnityLayerPropertyDrawer : PropertyDrawer
{
    public override void OnGUI(Rect _position, SerializedProperty _property, GUIContent _label)
    {
        EditorGUI.BeginProperty(_position, GUIContent.none, _property);
        SerializedProperty layerIndex = _property.FindPropertyRelative("m_LayerIndex");
        _position = EditorGUI.PrefixLabel(_position, GUIUtility.GetControlID(FocusType.Passive), _label);
        if (layerIndex != null)
        {
            layerIndex.intValue = EditorGUI.LayerField(_position, layerIndex.intValue);
        }
        EditorGUI.EndProperty();
    }
}

which works off this class

using UnityEngine;

[System.Serializable]
public class SingleUnityLayer {
    [SerializeField]
    private int m_LayerIndex = 0;
    private string m_LayerName = "";

    public int LayerIndex {
        get { return m_LayerIndex; }
    }

    public string LayerName {
        get { return m_LayerName; }
    }

    public void Set(int _layerIndex) {
        if(_layerIndex > 0 && _layerIndex < 32) {
            m_LayerIndex = _layerIndex;
            m_LayerName = LayerMask.LayerToName(m_LayerIndex);
        }
    }

    public int Mask {
        get { return 1 << m_LayerIndex; }
    }
}

and creates this result, which is great:

enter image description here

Now: I want to have the same thing, showing an array of a custom tags scriptable object class or even a simple string[] if necessary but can't get it to work. The property field for the drawer would be something like public Tag[] tags; where the Tag class simply contains a public name property for the moment.

I don't even have the code of my many attempts because it got messy and i kinda gave up and i found some solutions online which i didnt' even try because they seemed way to complex to be necessary for that simple task.

Can someone please push me in the right direction. A little more than "read up on custom editors" would be amazing ;)

Thanks

(not really the topic here but if someone can tell me a better(cheap) way to identify colliders detected with an overlapcircle than with tags, please do tell ;)

(hope the code-blocks and stuff work out..first post)

Edit: After helpful input from @derHugo who understood better what i want than myself, i came up with this simple solution:

[CustomPropertyDrawer(typeof(SingleUnityTag))]
public class SingleUnityTagPropertyDrawer : PropertyDrawer {
    //string selectedTag = "";
    public override void OnGUI(Rect _position, SerializedProperty _property, GUIContent _label) {
        EditorGUI.BeginProperty(_position, GUIContent.none, _property);
        SerializedProperty tagIndex = _property.FindPropertyRelative("m_TagIndex");
        SerializedProperty tagName = _property.FindPropertyRelative("m_TagName");
        _position = EditorGUI.PrefixLabel(_position, GUIUtility.GetControlID(FocusType.Passive), _label);
        if(tagIndex != null) {
            tagName.stringValue = EditorGUI.TagField(_position, tagName.stringValue);
        }
        EditorGUI.EndProperty();
    }
}

CodePudding user response:

So if I understand correctly now what you actually want is not a ScriptableObject at all but rather have a custom tag selection dropdown.

Unfortunately there isn't something built-in for this but you can create one using EditorGUI.TagField like e.g.

    using System;
    using UnityEngine;
#if UNITY_EDITOR
    using System.Linq;
    using UnityEditor;
#endif

    public class TagAttribute : PropertyAttribute
    {
#if UNITY_EDITOR
        [CustomPropertyDrawer(typeof(TagAttribute))]
        private class TagAttributeDrawer : PropertyDrawer
        {
            public override float GetPropertyHeight(SerializedProperty property, GUIContent label)
            {
                return EditorGUIUtility.singleLineHeight;
            }

            public override void OnGUI(Rect position, SerializedProperty property, GUIContent label)
            {
                EditorGUI.BeginProperty(position, label, property);

                if (property.propertyType != SerializedPropertyType.String)
                {
                    EditorGUI.HelpBox(position, $"{nameof(TagAttribute)} can only be used for strings!", MessageType.Error);
                    return;
                }

                if (!UnityEditorInternal.InternalEditorUtility.tags.Contains(property.stringValue))
                {
                    property.stringValue = "";
                }

                var color = GUI.color;
                if (string.IsNullOrWhiteSpace(property.stringValue))
                {
                    GUI.color = Color.red;
                }

                property.stringValue = EditorGUI.TagField(position, label, property.stringValue);
                GUI.color = color;

                EditorGUI.EndProperty();
            }
        }
#endif
    }

so in your components you can now simply have a

[Tag] public string tag;

or

[Tag] public string[] tags;
  • Related