Define Unity Layers in Script

  July 7, 2014   Programming   Leslie Young

In this article I will show you how to access the Unity tag manager file to modify the layers and tags via editor scripting.

For plyGame I needed to predefine some tags and layers that the tool used regularly. Especially the layers where important as this is a maker tool and I did not want to leave it up to the user of the tool to define and add the needed layers to the objects.

The code I will present is from plyGame but it should be sufficient info to adapt it to your own needs.

The first thing I did was to define the tags and layers used in plyGame in a class as static properties. This is so that it is easier to access them without having to remember all the string values used for the various layers or tags. Remember that you can define only up to 31 layers and you should not use values lower than 8 as they are meant for internal layer definitions in Unity. Basically, stick to what you can do in the inspector via the editor.

public class GameGlobal : MonoBehaviour
{
    public static class LayerMapping
    {
        public const int Floor = 8;
        public const int Wall = 9;
        public const int Player = 18;
        public const int NPC = 19;
        public const int plyObject = 20;
        public const int plyItem = 21;
        public const int plyTrigger = 22;
    }

    public static class Tag
    {
        public const string Untagged = "Untagged";
        public const string MainCamera = "MainCamera";
    }
}

Then with some reflection I will extract the values and use it to update the values in the tag manager. This is where you might modify the code and remove the reflection stuff. I’ve modified the code such that you can easily spot where I get the values into lists.

The first two blocks are doing reflection to populate a list of tags and a dictionary of layers. I use dictionary for the 2nd as I need both the layer name and the index for later.

Then follow the interesting stuff. Open the file where the tags and layers are stored and run through and add the tags to it.

Then the layers are checked. First I remove any clashing names just in case the layers are defined in the wrong spots. I want my Floor on layer 8, not 10 or 15. I can do this because plyGame expects to be a major part of your project and some things can be ‘hard coded’. For your own code you might want to work with the layer names only and simply grab the first open spot and define your layer(s) there if not yet defined. So you might skip the block that removes existing layers!

// Make a list of all the tags to be defined
List<string> tagNames = new List<string>();
System.Reflection.FieldInfo[] fields = typeof(plyGame.GameGlobal.Tag).GetFields();
foreach (System.Reflection.FieldInfo f in fields)
{
    string s = f.GetRawConstantValue().ToString();

    // these are skipped as unity define them by default
    if (s.Equals("Untagged")) continue;
    if (s.Equals("MainCamera")) continue;

    // add the tag name
    tagNames.Add(s);
}

// Make a list of all the layers to be defined <laye_name, layer_index>
fields = typeof(plyGame.GameGlobal.LayerMapping).GetFields();
Dictionary<string, int> layers = new Dictionary<string, int>();
foreach (System.Reflection.FieldInfo f in fields) layers.Add(f.Name, (int)f.GetRawConstantValue());

// Open tag manager
SerializedObject tagManager = new SerializedObject(AssetDatabase.LoadAllAssetsAtPath("ProjectSettings/TagManager.asset")[0]);
SerializedProperty tagsProp = tagManager.FindProperty("tags");

// *** Check the Tags
foreach (string s in tagNames)
{
    // check if the tag is defined
    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;
    }
}

// *** Check the Layers
// first remove all names that are the same as ones I want to define
for (int i = 8; i <= 31; i++)
{
    string nm = "User Layer " + i;
    SerializedProperty sp = tagManager.FindProperty(nm);
    if (sp != null)
    {
        if (layers.ContainsKey(sp.stringValue)) sp.stringValue = "";
    }
}

// now add layer names used by plyGame
foreach (KeyValuePair<string, int> kv in layers)
{
    string nm = "User Layer " + kv.Value;
    SerializedProperty sp = tagManager.FindProperty(nm);
    if (sp != null) sp.stringValue = kv.Key;
}

// *** save changes
tagManager.ApplyModifiedProperties();

And that is it. Not too hard once you know the format used in the TagManager.asset file.


Discussion


     Follow