I'm setting a script to initialize common stuff I usually add to a new project, and one of those are some custom tags. Is it possible at all to create a tag programmatically?
You can only do it in an Editor script. I’ve written an article explaining it for Tags and Layers.
http://www.plyoung.com/blog/define-unity-layers-in-script.html
Works for both Unity 4 and Unity 5. The article goes into more details but in case it disappear, here is some code…
// Open tag manager
SerializedObject tagManager = new SerializedObject(AssetDatabase.LoadAllAssetsAtPath("ProjectSettings/TagManager.asset")[0]);
SerializedProperty tagsProp = tagManager.FindProperty("tags");
// For Unity 5 we need this too
SerializedProperty layersProp = tagManager.FindProperty("layers");
// Adding a Tag
string s = "the_tag_i_want_to_add";
// First check if it is not already present
bool found = false;
for (int i = 0; i < tagsProp.arraySize; i++)
{
SerializedProperty t = tagsProp.GetArrayElementAtIndex(i);
if (t.stringValue.Equals(s)) { found = true; break; }
}
// if not found, add it
if (!found)
{
tagsProp.InsertArrayElementAtIndex(0);
SerializedProperty n = tagsProp.GetArrayElementAtIndex(0);
n.stringValue = s;
}
// Setting a Layer (Let's set Layer 10)
string layerName = "the_name_want_to_give_it";
// --- Unity 4 ---
SerializedProperty sp = tagManager.FindProperty("User Layer 10");
if (sp != null) sp.stringValue = layerName;
// --- Unity 5 ---
SerializedProperty sp = layersProp.GetArrayElementAtIndex(10);
if (sp != null) sp.stringValue = layerName;
// and to save the changes
tagManager.ApplyModifiedProperties();
I don't think it's possible.. If you look it up in the Unity Script Reference you will find: "Tags must be declared in the tag manager before using them".
EDIT: You can actually assign tags to objects if you have already declared them in the tag manager. Example:
gameObject.tag = "Player";
This will work if you've made a tag called "Player" in the tag manager. The tag manager can be found by going to Edit -> Project Settings -> Tags
Similar to Leslie Young’s answer, here is a script you can put in an Editor folder and use to add as many tags as you want, simply by calling TagHelper.AddTag(“mytag”).
using UnityEngine;
using UnityEditor;
public static class TagHelper
{
public static void AddTag(string tag)
{
UnityEngine.Object[] asset = AssetDatabase.LoadAllAssetsAtPath("ProjectSettings/TagManager.asset");
if ((asset != null) && (asset.Length > 0))
{
SerializedObject so = new SerializedObject(asset[0]);
SerializedProperty tags = so.FindProperty("tags");
for (int i = 0; i < tags.arraySize; ++i)
{
if (tags.GetArrayElementAtIndex(i).stringValue == tag)
{
return; // Tag already present, nothing to do.
}
}
tags.InsertArrayElementAtIndex(0);
tags.GetArrayElementAtIndex(0).stringValue = tagname;
so.ApplyModifiedProperties();
so.Update();
}
}
}
You can edit the tags via script in the Editor, but not a runtime:
using UnityEngine;
using UnityEditor;
public class LayerTagsManager : EditorWindow {
Vector2 scrollPos;
SerializedObject tagManager;
[MenuItem ("Window/Open My Tags and Layers Window")]
static void OpenTagsEditorWindow (){
LayerTagsManager window = EditorWindow.GetWindow <LayerTagsManager>("Tags & Layers");
window.tagManager = new SerializedObject (AssetDatabase.LoadAllAssetsAtPath ("ProjectSettings/TagManager.asset")[0]);
}
void OnGUI () {
EditorGUIUtility.LookLikeInspector ();
scrollPos = EditorGUILayout.BeginScrollView(scrollPos);
SerializedProperty it = tagManager.GetIterator ();
bool showChildren = true;
while (it.NextVisible(showChildren)) {
// if (it.name == "data") <-- It's a tag property
// if (it.name.Contains ("Layer")) <-- It's a layer property
showChildren = EditorGUILayout.PropertyField (it);
}
EditorGUILayout.EndScrollView();
}
}
If you just want to copy paste the code without reading most of this, it’s the last code section.
I have taken @LeslieYoung 's answer and modified it to make a method call to add either a tag or a layer. Note that this has only been tested in Unity 5 and I am using the portion of @LeslieYoung 's answer that tailors to Unity 5 only.
**How to use it?**
Call a function like this: bool result = AddTag("MyTagNameHere");
for tag and bool result = AddLayer("MyLayerNameHere");
for layer. It returns the result true if the property was successfully added and false otherwise.
**Added functionality**
For my own project, I call this in a custom editor in my OnInspectorGUI()
function to ensure the tag/layer exists before I do something with it. I’ve also used it to assign the target GameObject to a specific tag or layer (if it exists)
One use case for this code is to allow tag editing in the editor (in my case I wanted to be able to edit tags in my custom inspector.
Using the following code, you can programmatically add a tag or layer, keep track of it and allow it to be renamed directly in the inspector.
Note: If the tag or layer is edited directly in Tags or Layers, a new tag or layer will be created with the previously existing name the next time the script is enabled ( OnEnable()
function)
This script holds our variables for use in game and across scripts
MyCustomScript.cs
using UnityEngine;
using System.Collections;
namespace MyScripts {
public class SomeClass {
public static string Layer = "MyInitialLayerName";
}
public class MyCustomScript : MonoBehaviour {
public string layer = SomeClass.Layer;
}
}
This script allows us to edit the layer
variable from MyCustomScript with a custom inspector.
When the value is changed, the layer name in Layer is also changed. Any GameObjects with this layer will also be changed accordingly. The code below also allows for "Undo"s to be performed
MyCustomInspector.cs
using UnityEditor;
using UnityEngine;
using System.Collections;
using MyScripts;
using MyCustomEditor;
[InitializeOnLoad]
[ExecuteInEditMode]
[CustomEditor(typeof(MyCustomScript))]
public class MyCustomInspector : Editor {
private GUIContent guiContent;
private MyCustomScript myTarget;
private static string layer = SomeClass.Layer;
void OnEnable() {
myTarget = (MyCustomScript)target;
layer = myTarget.layer = SomeClass.Layer;
TagsAndLayers.AddLayer (layer);
}
public override void OnInspectorGUI() {
string tmpString;
myTarget = (MyCustomScript)target;
EditorGUI.BeginChangeCheck ();
guiContent = new GUIContent ("Layer Name", "Set the name of the layer");
tmpString = EditorGUILayout.TextField (guiContent, myTarget.layer);
if (EditorGUI.EndChangeCheck ()) {
Undo.RecordObject (myTarget, "Layer Name");
myTarget.layer = tmpString;
if (layer != null && myTarget.layer != "") {
if(myTarget.layer != layer) {
TagsAndLayers.RemoveLayer (layer);
}
TagsAndLayers.AddLayer (myTarget.layer);
layer = SomeClass.Layer = myTarget.layer;
}
}
EditorUtility.SetDirty (myTarget);
}
void OnInspectorUpdate() {
this.Repaint ();
}
}
**The actual thing**
The code below does the following when creating a tag or layer:- Checks to see if there is still “room” in the property to write another element
- If there is no more room (maximum number of tags is 10001 and maximum number of layers is 32)
- Checks to see if the tag of layer property value already exists
- If (3) returns false, we add the tag or layer name to the property, then we save the property.
The removal of a tag or layer works similarly to the steps described above, except we unset the property value: For layers, we set the value to an empty string ""
and for tags, we remove the element at the index of our match.
MyCustomEditor.cs
using UnityEditor;
using UnityEngine;
using System.Collections;
namespace MyCustomEditor {
public class TagsAndLayers {
private static int maxTags = 10000;
private static int maxLayers = 31;
/// <summary>
/// Adds the tag.
/// </summary>
/// <returns><c>true</c>, if tag was added, <c>false</c> otherwise.</returns>
/// <param name="tagName">Tag name.</param>
public static bool AddTag (string tagName) {
// Open tag manager
SerializedObject tagManager = new SerializedObject (AssetDatabase.LoadAllAssetsAtPath ("ProjectSettings/TagManager.asset") [0]);
// Tags Property
SerializedProperty tagsProp = tagManager.FindProperty ("tags");
if (tagsProp.arraySize >= maxTags) {
Debug.Log("No more tags can be added to the Tags property. You have " + tagsProp.arraySize + " tags");
return false;
}
// if not found, add it
if (!PropertyExists (tagsProp, 0, tagsProp.arraySize, tagName)) {
int index = tagsProp.arraySize;
// Insert new array element
tagsProp.InsertArrayElementAtIndex(index);
SerializedProperty sp = tagsProp.GetArrayElementAtIndex(index);
// Set array element to tagName
sp.stringValue = tagName;
Debug.Log ("Tag: " + tagName + " has been added");
// Save settings
tagManager.ApplyModifiedProperties ();
return true;
} else {
//Debug.Log ("Tag: " + tagName + " already exists");
}
return false;
}
/// <summary>
/// Removes the tag.
/// </summary>
/// <returns><c>true</c>, if tag was removed, <c>false</c> otherwise.</returns>
/// <param name="tagName">Tag name.</param>
public static bool RemoveTag(string tagName) {
// Open tag manager
SerializedObject tagManager = new SerializedObject (AssetDatabase.LoadAllAssetsAtPath ("ProjectSettings/TagManager.asset") [0]);
// Tags Property
SerializedProperty tagsProp = tagManager.FindProperty ("tags");
if (PropertyExists (tagsProp, 0, tagsProp.arraySize, tagName)) {
SerializedProperty sp;
for(int i = 0, j = tagsProp.arraySize; i < j; i++) {
sp = tagsProp.GetArrayElementAtIndex (i);
if(sp.stringValue == tagName) {
tagsProp.DeleteArrayElementAtIndex (i);
Debug.Log("Tag: " + tagName + " has been removed");
// Save settings
tagManager.ApplyModifiedProperties ();
return true;
}
}
}
return false;
}
/// <summary>
/// Checks to see if tag exists.
/// </summary>
/// <returns><c>true</c>, if tag exists, <c>false</c> otherwise.</returns>
/// <param name="tagName">Tag name.</param>
public static bool TagExists(string tagName) {
// Open tag manager
SerializedObject tagManager = new SerializedObject (AssetDatabase.LoadAllAssetsAtPath ("ProjectSettings/TagManager.asset") [0]);
// Layers Property
SerializedProperty tagsProp = tagManager.FindProperty ("tags");
return PropertyExists (tagsProp, 0, maxTags, tagName);
}
/// <summary>
/// Adds the layer.
/// </summary>
/// <returns><c>true</c>, if layer was added, <c>false</c> otherwise.</returns>
/// <param name="layerName">Layer name.</param>
public static bool AddLayer (string layerName) {
// Open tag manager
SerializedObject tagManager = new SerializedObject (AssetDatabase.LoadAllAssetsAtPath ("ProjectSettings/TagManager.asset") [0]);
// Layers Property
SerializedProperty layersProp = tagManager.FindProperty ("layers");
if (!PropertyExists (layersProp, 0, maxLayers, layerName)) {
SerializedProperty sp;
// Start at layer 9th index -> 8 (zero based) => first 8 reserved for unity / greyed out
for (int i = 8, j = maxLayers; i < j; i++) {
sp = layersProp.GetArrayElementAtIndex (i);
if (sp.stringValue == "") {
// Assign string value to layer
sp.stringValue = layerName;
Debug.Log ("Layer: " + layerName + " has been added");
// Save settings
tagManager.ApplyModifiedProperties ();
return true;
}
if (i == j)
Debug.Log ("All allowed layers have been filled");
}
} else {
//Debug.Log ("Layer: " + layerName + " already exists");
}
return false;
}
/// <summary>
/// Removes the layer.
/// </summary>
/// <returns><c>true</c>, if layer was removed, <c>false</c> otherwise.</returns>
/// <param name="layerName">Layer name.</param>
public static bool RemoveLayer(string layerName) {
// Open tag manager
SerializedObject tagManager = new SerializedObject (AssetDatabase.LoadAllAssetsAtPath ("ProjectSettings/TagManager.asset") [0]);
// Tags Property
SerializedProperty layersProp = tagManager.FindProperty ("layers");
if (PropertyExists (layersProp, 0, layersProp.arraySize, layerName)) {
SerializedProperty sp;
for(int i = 0, j = layersProp.arraySize; i < j; i++) {
sp = layersProp.GetArrayElementAtIndex (i);
if(sp.stringValue == layerName) {
sp.stringValue = "";
Debug.Log ("Layer: " + layerName + " has been removed");
// Save settings
tagManager.ApplyModifiedProperties ();
return true;
}
}
}
return false;
}
/// <summary>
/// Checks to see if layer exists.
/// </summary>
/// <returns><c>true</c>, if layer exists, <c>false</c> otherwise.</returns>
/// <param name="layerName">Layer name.</param>
public static bool LayerExists(string layerName) {
// Open tag manager
SerializedObject tagManager = new SerializedObject (AssetDatabase.LoadAllAssetsAtPath ("ProjectSettings/TagManager.asset") [0]);
// Layers Property
SerializedProperty layersProp = tagManager.FindProperty ("layers");
return PropertyExists (layersProp, 0, maxLayers, layerName);
}
/// <summary>
/// Checks if the value exists in the property.
/// </summary>
/// <returns><c>true</c>, if exists was propertyed, <c>false</c> otherwise.</returns>
/// <param name="property">Property.</param>
/// <param name="start">Start.</param>
/// <param name="end">End.</param>
/// <param name="value">Value.</param>
private static bool PropertyExists(SerializedProperty property, int start, int end, string value) {
for (int i = start; i < end; i++) {
SerializedProperty t = property.GetArrayElementAtIndex (i);
if (t.stringValue.Equals (value)) {
return true;
}
}
return false;
}
}
}
Note: I changed variable names from my own code, if something doesn’t work properly, feel free to let me know so I can edit it, or edit it yourself if you are able to do so!
In Unity 2020.3, there is an undocumented api to create a tag in Editor:
UnityEditorInternal.InternalEditorUtility.AddTag("xxx");