How to expose Unity tags in the inspector?

Thank you for that.

OK why are tags strings?!
And layers are ints (or worse strings)!

Not sure why Unity did not have some type that could be auto Inspected for these, while being optimal to compare.
I have grabbed Single Layer Inspector code before, now I need single tag inspector code (when it should be provided!)

/rant

the Cinemachine solution still exists once I worked out what to extract (not using Cinemachine), so thanks!!

Tags are for new beginners, when you get more advanced you realize markup components and similar are better tools in the toolbox

I use both - tags are super simple for quick checks. But a component can indeed give a lot more info, though not without some added weight

Tags never make sense. They are magic strings. Never use magic strings

Isn’t that what they have done again with UI Toolkit?! Please stop it with the magic strings - Unity Engine - Unity Discussions

I mean there’s my post after OP which shows a work around. We have always been able to Q<T> for strongly typed visual elements.

This is also completely off topic to the thread.

Tags are an old feature of Unity. They are no longer needed and could honestly be removed without any loss.

Today I came across this same problem, while trying to make a beginner-friendly tool. My solution was this:

  1. Property:
public sealed class TagMaskFieldAttribute : PropertyAttribute { }
[CustomPropertyDrawer(typeof(TagMaskFieldAttribute))]
    public class TagMaskFieldAttributeEditor : PropertyDrawer
    {
        public override void OnGUI(Rect position, SerializedProperty property, GUIContent label)
        {
            property.stringValue = EditorGUI.TagField(position, label, property.stringValue);
        }
  1. Implementation:
public class ScriptExample : MonoBehaviour
    {
        [SerializeField, TagMaskField] private string _tag;
    }
  1. Inspector Result:

3 Likes

This works like a charm.

1 Like

Hi, everyone

I managed to get the list of tags in TagManager.asset file under ProjectSettings.

Here is the script MyScript as an example.

using UnityEngine;

public class MyScript : MonoBehaviour
{
	[SerializeField]
	private string m_Tag = "Untagged";

	[SerializeField]
	private int m_SelectedIndex = 0;

	[SerializeField]
	private int m_SelectedValue = 0;

	[SerializeField]
	private int m_BitMask = 0;

	// Start is called before the first frame update
	void Start()
	{

	}

	// Update is called once per frame
	void Update()
	{

	}
}

Now the editor script MyEditor showing how to get all tags and display them on Inspector.

using System.Collections.Generic;
using UnityEngine;
using UnityEditor;

/// <summary>
/// 
/// </summary>
[CustomEditor(typeof(MyScript))]
public class MyEditor : Editor
{
	// Tag for TagField
	private SerializedProperty m_TagProp;

	// Selected index for Popup
	private SerializedProperty m_SelectedIndexProp;

	// Selected index for IntPopup
	private SerializedProperty m_SelectedValueProp;

	// BitMask for MaskField
	//  0 = Nothing
	// -1 = Eveything
	private SerializedProperty m_BitMaskProp;

	// Labels
	private GUIContent m_TagLabel = EditorGUIUtility.TrTextContent("Tag:", "Tooltip for Tag.");
	private GUIContent m_PopupLabel = EditorGUIUtility.TrTextContent("Popup Tags:", "Tooltip for Popup.");
	private GUIContent m_IntPopupLabel = EditorGUIUtility.TrTextContent("IntPopup Tags:", "Tooltip for IntPopup.");
	private GUIContent m_MaskFieldLabel = EditorGUIUtility.TrTextContent("MaskField Tags:", "Tooltip for MaskField.");

	// Default Unity Tags from all versions of Unity
	// See https://docs.unity3d.com/Manual/Tags.html
	private List<string> m_Tags = new List<string>(new string[]
	{
		"Untagged",
		"Respawn",
		"Finish",
		"EditorOnly",
		"MainCamera",
		"Player",
		"GameController"
	});

	// Labels for Popup and IntPopup
	private GUIContent[] m_TagsDisplayedOptions = null;

	private int[] m_TagsOptionValues = null;

	void OnEnable()
	{
		m_TagProp = serializedObject.FindProperty("m_Tag");
		m_SelectedIndexProp = serializedObject.FindProperty("m_SelectedIndex");
		m_SelectedValueProp = serializedObject.FindProperty("m_SelectedValue");
		m_BitMaskProp = serializedObject.FindProperty("m_BitMask");

		// Look for custom tags in TagManager.asset under ProjectSettings
		SerializedObject serializedTagManagerAssetObject = new SerializedObject(AssetDatabase.LoadAllAssetsAtPath("ProjectSettings/TagManager.asset")[0]);
		
		// The serialized property containing only custom tags
		SerializedProperty tagsProp = serializedTagManagerAssetObject.FindProperty("tags");

		// Adding custom tags in tags list
		for (int i = 0; i < tagsProp.arraySize; i++)
			m_Tags.Add(tagsProp.GetArrayElementAtIndex(i).stringValue);
		
		m_TagsDisplayedOptions = m_Tags.ConvertAll(tag => EditorGUIUtility.TrTextContent(tag)).ToArray();

		m_TagsOptionValues = new int[m_TagsDisplayedOptions.Length];

		for (int i = 0; i < m_TagsOptionValues.Length; i++)
		{
			m_TagsOptionValues[i] = i;
		}
	}

	public override void OnInspectorGUI()
	{
		// Update the serializedObject - always do this in the beginning of OnInspectorGUI.
		serializedObject.Update();

		// You can use Tag field
		EditorGUILayout.BeginHorizontal();
		m_TagProp.stringValue = EditorGUILayout.TagField(m_TagLabel, m_TagProp.stringValue, GUILayout.ExpandWidth(true));
		EditorGUILayout.EndHorizontal();

		// You can use Popup field
		EditorGUILayout.BeginHorizontal();
		m_SelectedIndexProp.intValue = EditorGUILayout.Popup(m_PopupLabel, m_SelectedIndexProp.intValue, m_TagsDisplayedOptions, GUILayout.ExpandWidth(true));
		EditorGUILayout.EndHorizontal();

		// You can use IntPopup field
		EditorGUILayout.BeginHorizontal();
		EditorGUILayout.IntPopup(m_SelectedValueProp, m_TagsDisplayedOptions, m_TagsOptionValues, m_IntPopupLabel, GUILayout.ExpandWidth(true));
		EditorGUILayout.EndHorizontal();

		// You can use MaskField field
		EditorGUILayout.BeginHorizontal();
		m_BitMaskProp.intValue = EditorGUILayout.MaskField(m_MaskFieldLabel, m_BitMaskProp.intValue, m_Tags.ToArray(), GUILayout.ExpandWidth(true));
		EditorGUILayout.EndHorizontal();

		// Apply changes to the serializedObject - always do this in the end of OnInspectorGUI.
		serializedObject.ApplyModifiedProperties();
	}
}

On Inspector, it’ll look like this

Let me know what you guys think

Thanks

That looks… incredibly roundabout compared to just getting all tags via the undocumented API methods to do so: UnityCsReference/Editor/Mono/InternalEditorUtility.bindings.cs at master · Unity-Technologies/UnityCsReference · GitHub

Though the real advice for this thread would be to not use tags. There are substantially better ways to do things these days.

2 Likes

I am trying to enqueue a Scriptable Render Pass for the MainCamera and comparing tags like this: renderingData.cameraData.camera.CompareTag
How else can I uniquely identify a camera from renderingData?

@spiney199
Which header to copy for this function you’ve mentioned here? UnityCsReference/Editor/Mono/InternalEditorUtility.bindings.cs at master · Unity-Technologies/UnityCsReference · GitHub

The things you can attach any number of in any combination to game objects… aka, components.

Wouldn’t call that a better way than tags in this case.

It is, because you can define data however you want on a component. You could also put data that drives the scriptable render pass on said component. You can’t do that with a tag: it’s just a string. And strings are really only good for displaying text.