Is it possible to have multiple tags (or the like) on one object?

Here's my situation. I'm coding scripts for a turn-based game that has an initiative system. Sometimes I need all of the planes to go first, sometimes the ground units, sometimes all the enemies, etc. The FindObjectsWithTag has such good functionality, but tagging an object based on its vehicle type means I can't tag it "player" or "enemy" (which makes things harder in other areas). Is there any way to have more than one tag so that I can iterate through my objects easily? Or is there another strategy in Unity that will get me what I want (simply iterating through planes[].Move() after defining "planes" with FindObjectsWithTag is sooo convenient...).

Thanks!

It is not currently possible, but i use a work-around.

On gameObjects that i need more than one tag on, i add an empty game objects as a child for each tag i need, and give each of these separate tags.. Then you can still search for tags using FindGameObjectsWithTag then just reference the parent object.. as an example in C#

sensedObjects = GameObject.FindGameObjectsWithTag(theTag);

foreach (GameObject foundOne in sensedObjects)
{
    GameObject objectMain = foundOne.transform.parent.gameObject;
        //use it...
}

When i want to multi-tag something i create Acronyms for the tags
Ex:

  • Ally = All
  • Minion = Min
  • Champion = Chm

So when i want for some object to have multiple tags i tag it with a string containing several of them.
ex : “EnChm” (for Enemy Champion) contains “Ch” , and i get by with doing the regular checks :

 tag.Contains(string name)

As alternative to re-implementing tags (as suggested by @CarterG81) just use a (possibly empty) component instead of using tags at all - this is far more flexible, and you can search using FindObjectsOfType just the same - without any additional framework/code.

If you need to classify objects in the first place, such as by adding tags, it’s likely you’ll discover at a later time that those objects have more in common than just a name - and you’ll need a behavior for that anyhow; this way, you have a placeholder for that already.

In my space shooter tutorial project, I use a Shootable behavior, which I apply to anything that can be shot - for example:

public class Shootable : MonoBehaviour {
    public GameObject explosion;

    public void Explode()
    {
        Destroy(gameObject);

        Instantiate(explosion, transform.position, transform.rotation);
    }
}

The controller for my shot is responsible for checking collisions - it checks if objects it collides with are Shootable, and if so, destroys itself, and tells the Shootable to explode:

private void OnTriggerEnter(Collider other)
{
    var shootable = other.gameObject.GetComponent<Shootable>();

    if (shootable) {
        Destroy(gameObject);

        shootable.Explode();
    }
}

This way, shootables are responsible for generating their own explosions - you can elaborate on this concept and also make them responsible for counting damage, deciding whether to destroy themselves, and any other effects that may be specific to each kind of shootable object.

I created a simple asset called Tag Frenzy that allows you use multiple tags in Unity. It is (in my opinion) as easy to use as the built in tag system. You can see it in action here. I’d encourage you to check it out if you don’t want to manually add child gameobjects like in the solution above.

It’s pretty simple to just replace Unity’s tag system with your own.

Here’s an example of how you could write a tag replacement system (to show how easy it is).

Pretty easy, and also extendable.


public class Tags : Monobehaviour
{
    public string[] myTags;

    bool hasTag(string tagToCheck)
    {
        foreach(string tag in myTags) //Can replace foreach with 'for loop'
        {
            if(tag == tagToCheck)
            {
                return true;
            }
        }
        return false;
    }
}

Alternatively you could replace the string with enum TagType

public enum TagType
{
    Item,
    NPC,
    Player,
    Object
}
//Tags.cs
public TagType[] myTags;
bool hasTag(TagType tagToCheck)
//etc.

If you want to be like Unity, you could also still use this system by simply introducing more code to do other functions, like FindGameObjectWithTag, which is much faster than GameObject.Find() because Unity stores all gameobjects with tags.


public static class TagSystem
{
    public static Dictionary<string, List<Gameobject>> allTaggedGameObjects;

    List<GameObject> FindObjectsWithTag(string tagToFind)
    {
        if(allTaggedGameObjects.ContainsKey(tagToFind)
        {
            return allTaggedGameObjects[tagToFind];
        }
        else { Debug.LogError("No Objects with Tag " + tagToFind + " found."); return null; }
    }

    GameObject FindObjectWithTag(string tagToFind) //Return the first tag found
    {
        if(allTaggedGameObjects.ContainsKey(tagToFind)
        {
            return allTaggedGameObjects[tagToFind][0];
        }
    }

    static void AddGameobjectTags(Tag tagScript)
    {
        foreach(TagType tag in tagScript.myTags)
        {
            if(allTaggedGameObjects.ContainsKey(tag)
            {
                allTaggedGameObjects[tag].Add(tagScript.gameObject);
            }
           else
           {
                //Add new tag & new gameobject list to dictionary, etc.
           }
        }
    }
}

//Tags.cs
void Awake()
{
    TagSystem.AddGameobjectTags(this);
}

Finally, to actually get any of the tag information, you’d need a reference to the Tags.cs component.

myGameObject.GetComponent<Tags>().hasTag(theTag);

Something like that, anyway. Pretty easy stuff to simulate Unity’s tag API.

You could even extend this system to include Layers, or a third similar type. Since it’s your own custom system, it’s fully extendable for easier readability, better performance, and more overall power as a feature.

Having Enums rather than strings can save you some problems with user error (typos), among other benefits. It’s up to you though. It’ll all perform extremely fast, so readability is most important.


Note: I’m not an expert, so I’m always open to improvement. I’d love for anyone who believes this is inefficient, to comment as to why (I’m trying to build a collection of solid ‘best practices’ or ‘systems’ which users could find & use.) so I can explain any caveats.

Or you could use FindObjectsOfType instead

e.g.

var planes = FindObjectsOfType<Plane>();

There are good suggestions all round. If you want to do “something” with objects across two tags you can also find all objects of either tag then copy those arrays into another array like this:

GameObject[] a = GameObject.FindGameObjectsWithTag("a");
GameObject[] b = GameObject.FindGameObjectsWithTag("b");
GameObject[] c = new GameObject[a.Length + b.Length];
a.CopyTo(c, 0);
b.CopyTo(c, a.Length);
//now you can do what you need to with c.

Since we still have classes with C#, I’d better make it this way:

[ IndiTargetClass.cs ] // attached to every target at the scene

using System.Collections;
using UnityEngine;
        
public class IndiTargetClass : MonoBehaviour {
        public bool active = true; // true / false - can act
        public bool target = true; // true / false - can be targeted
        public bool exist = true; // true / false - aside from health=0 or not
        public string name = ""; // "BTR-70", "SA-6 Gainful", ...
        public string type = ""; // "radar", "SA missile", "building", "vehicle"; "fighter", "bomber", "cargo", "helo", "airship"; ...
        //public bool enemy = true; // true / false
        public string enemy = "e"; // e: enemy / a: ally / n: neutral / c: enemy in cease fire or cold war state, don't engage / v: alien invasion
        public int health = 100; // 0-100, or even 0-900 if you need some add'l range there
        public string air = "g"; // a: air / g: ground / s: sea / v: space
}

[ IndiTargets.cs ] // usage :: see [IndiTargets.cs]:ShowTextTarget near 228

    var TargetClass = TargetList[TargetCurrent-1].transform.gameObject.GetComponent<IndiTargetClass>();
    TextTargetName.text = TargetClass.name + " : " + TargetClass.health + " [" + TargetCurrent + " of " + TargetQty + "]";

is there a reason why you can’t have multiple tags?

A GameObject has onlyOne tag It cannot contain more than one tag. There r many different ways to find that tag. e.g compare tag. findgameobject with tag etc