- Home /
Is it possible to create a tag programmatically?
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?
Answer by Leslie-Young · Mar 07, 2015 at 10:16 AM
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();
Thanks, that works great, but I got one hickup in Unity 5.2.2:
When I assign a layer created with the above procedure to a GameObject (via GameObject.layer = Layer$$anonymous$$ask.NameToLayer(layerName) ), it shows as "Unnamed 3" in the inspector (I got a public Layer$$anonymous$$ask var in order to debug it).
Any ideas?
Duh, forget it, I was using layer mask wrong. The following format works just fine...
Layer$$anonymous$$ask mask = 1 << Layer$$anonymous$$ask.NameToLayer(layerName);
Used code from your answer in my project. It worked well! But I want to mark some moments in work with tags.
$$anonymous$$y case of usage: I had a bundle (heh... near 150) of objects. For each of them I wanted to assign a unique tag, based on name of the object (why not use names themselves? Oh... I read somewhere that it is slower than tags)
tag$$anonymous$$anager.asset was created succesfully with this code. But... After restart of the Unity every assigned tag was reset to "Untagged"!
At first I used construction given in the reply by Yoerick:
gameObject.tag = tag;
I thought the trouble was here, but even when I used SerializedObject
and
SerializedProperty
ins$$anonymous$$d of gameObject
, saving of the scene wouldn't work.
The solution was a kind of mystery. After applying of the script I just assigned random tag to one of the object, then returned the value back. Unity suggested to save scene! I saved it, rerun Unity and -- oh yeah -- I got scene with saved freshly generated tags.
$$anonymous$$aybe it will be helpful for someone.
P.S.: and, by the way, if you close Unity, open tag$$anonymous$$anager.asset in e.g. Notepad++, you can easily sort tags. After reopening of the scene tags remain assigned to the same objects as earlier while tags in "Tags and Layers" Window are perfectly ordered.
Answer by Yoerick · Nov 11, 2010 at 07:47 AM
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
Ok, that's it then. A pity because I'm much more a code guy than a GUI guy. I wish I could do everything in code. Thank you.
Oh, I know about assigning existing tags. The question was about creating custom tags programmatically, though. I wanted to do this in a "setup project" kind of script.
Have you tried looking up the classes in the UnityEditor namespace? It's possible that you find something there.
As of Unity 3.1.0f4 there are no features in the scripting reference that I can find that would do this (create a layer/tag programatically). It would definitely be a handy tool to have.
I second this. I want to use about 560 different tags and I'm dreading the task of creating them manually.
Answer by yoyo · Mar 22, 2015 at 05:59 AM
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();
}
}
}
unable to access the static class TagHelper in a mono script :( . ive put the TagHelper script in Assets>Editor folder.
Ins$$anonymous$$d of adding the tag at 0 you should add it at the end of the array to avoid that all existing tags are moved around and thus everything becomes a mess each time you add a new tag...
Replace line 22. - 23. with:
tags.InsertArrayElementAtIndex(tags.arraySize);
tags.GetArrayElementAtIndex(tags.arraySize - 1).stringValue = tag;
Thank you for this sharing. It's very useful for me. But this line caused to error.
tags.GetArrayElementAtIndex(0).stringValue = tagname;
"tagname" should have changed to "tag";
and it's done !
Answer by ricardo_arango · Jul 19, 2013 at 10:42 PM
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();
}
}
Hmm, this only shows the list of tags in a new window, if you change or add a new tag in it, the list in the main tag editor won't change, also, it doesn't give anyway to add the new tags in the script?
To actually modify the tags so they show up in the main editor, you could call tag$$anonymous$$anager.Apply$$anonymous$$odifiedProperties() and then UnityEditorInternal.InternalEditorUtility.RepaintAllViews().
Answer by ctwheels · Feb 16, 2016 at 01:01 PM
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!
You, my friend, are an absolute saint. I've been trying to figure out how to add layers and tags from an editor script for three days now, and all the code snippets and documentation I've found have been incomplete and missing required things to make them function. You've just made my day, thank you so much!
Yo man, I am a newbie in unity dev and I thought that with this script I could manage tags, but this is not buildable because of using UnityEditor. So it's almost useless. Am I wrong?
Tags management is for the Unity editor, not for a built game. Why would you want to create tags during the game's standalone run time anyway? This script's purpose is to more easily manage tags when working inside the editor, and nothing else, that doesn't make it useless. Tags are an easy way to categorize objects in your scene that Unity offers, but it comes with its limitations, not being able to change them once the game is built, is one of them. For example if you want the user of your game to be able to arbitrarily create categories himself, then you will need to implement your own "tag" system.
Yes, I understood this yesterday. Just to explain my situation: I have to develop games only via code; the Editor will be used just to attach the main script to an empty new gameObject and then build. That's for a specific purpose that I won't explain here because it's not the right place for.
So, I was looking for a way to manage tags at runtime, before I knew that is impossible right now. So I mean, it's useless at runtime. The code itself is very good.
@$$anonymous$$acDx could you explain what do you mean with custom tag system? Thank you.