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:
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;