0

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();
    }
}

5
  • Found this handy code-snippet here to create LayerMasks containing multiple layers actually it is kind of the other way round .. by default LayerMask is an enum flag which allows multiple values and this drawer allows only a single one to be selected.... What speaks against Unity's default drawer for a list/array of ScriptableObject ? Commented Nov 29, 2022 at 21:55
  • a better(cheap) way to identify colliders detected with an overlapcircle than with tags would be to use a dedicated layer instead and use the LayerMask parameter of the OverlapCircle so you only hit the according layers in the first place ... Commented Nov 29, 2022 at 22:04
  • thanks @derHugo : So, for a simple example if i would have different enemies, they shouldn't all be on the same layer but on different ones depending on their type? I assumed it would be better to keep that organized and split up categorization to another level, e.g. tags or something similar. Commented Nov 30, 2022 at 7:36
  • would something speak against simply having a TagField then? I don't really understand what you need your ScriptableObject for Commented Nov 30, 2022 at 7:59
  • omg, thank you. My brain must really be empty by now. I didn't know it existed but of course it does, if the LayerField exists. So i should be able to really copy the solution for the layers almost exactly -_- Commented Nov 30, 2022 at 8:15

1 Answer 1

0

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;
Sign up to request clarification or add additional context in comments.

3 Comments

Man, thanks a Ton. And even Code for it. I just came back here to mark your first answer as sufficient. Just wrote the code myself. just adapted the Layer Version. Yes, you're right. I just wanted a custom tag-selection and thought i have to go through the lengths of creating my own tag class and so on.. But your hint was fine. I'll go through your code aswell and see if i can improve on my solution but it seems i already got it working
No idea how i can mark this topic as solved or mark your answer as solution, honestly -_-
I tested your code and it's perfect. Should be the go-to solution! Thanks man. Someone tell me how to flag solutions, please. This is closed :)

Your Answer

By clicking “Post Your Answer”, you agree to our terms of service and acknowledge you have read our privacy policy.

Start asking to get answers

Find the answer to your question by asking.

Ask question

Explore related questions

See similar questions with these tags.