Multiple tags for one GameObject
Hi everyone! Basically I am trying to create an inventory system for my game. Several tags attached to one object would really help! However it seems that that's currently not possible in Unity. If so — please advise me on some easy to understand workarounds
Answer by Xarbrough · Feb 20, 2018 at 01:54 AM
I would simply create a custom component which has a list of strings for tags. Then instead of asking for the GameObject.tag you do GetComponent().tags (or similar) to check if the object is tagged this way. You can build upon this and make it very easy to work with via editor scripts (e.g. dropdown lists instead of having to type the tag like the Unity tag system does). You can also use the Unity tag manager to define your tags and get them in an editor script.
If you are using tags for an inventory system, you also don't have to think about being efficient. Getting a component and searching through a list of objects is not a problem at all. Doing complex operations on thousands of objects is, but these tags are trivial, so don't worry. ;)
Here's some example code:
MonoBehavior
using UnityEngine;
using System.Collections.Generic;
public class CustomTag : MonoBehaviour
{
[SerializeField]
private List<string> tags = new List<string>();
public bool HasTag(string tag)
{
return tags.Contains(tag);
}
public IEnumerable<string> GetTags()
{
return tags;
}
public void Rename(int index, string tagName)
{
tags[index] = tagName;
}
public string GetAtIndex(int index)
{
return tags[index];
}
public int Count
{
get { return tags.Count; }
}
}
Custom Editor
using UnityEditor;
using UnityEngine;
using UnityEditorInternal;
[CustomEditor(typeof(CustomTag))]
public class CustomTagEditor : Editor
{
private string[] unityTags;
SerializedProperty tagsProp;
private ReorderableList list;
private void OnEnable()
{
unityTags = InternalEditorUtility.tags;
tagsProp = serializedObject.FindProperty("tags");
list = new ReorderableList(serializedObject, tagsProp, true, true, true, true);
list.drawHeaderCallback += DrawHeader;
list.drawElementCallback += DrawElement;
list.onAddDropdownCallback += OnAddDropdown;
}
private void DrawHeader(Rect rect)
{
EditorGUI.LabelField(rect, new GUIContent("Tags"), EditorStyles.boldLabel);
}
private void DrawElement(Rect rect, int index, bool isActive, bool isFocused)
{
var element = list.serializedProperty.GetArrayElementAtIndex(index);
rect.y += 2;
EditorGUI.LabelField(rect, element.stringValue);
}
private void OnAddDropdown(Rect buttonRect, ReorderableList list)
{
GenericMenu menu = new GenericMenu();
for (int i = 0; i < unityTags.Length; i++)
{
var label = new GUIContent(unityTags[i]);
// Don't allow duplicate tags to be added.
if (PropertyContainsString(tagsProp, unityTags[i]))
menu.AddDisabledItem(label);
else
menu.AddItem(label, false, OnAddClickHandler, unityTags[i]);
}
menu.ShowAsContext();
}
private bool PropertyContainsString(SerializedProperty property, string value)
{
if (property.isArray)
{
for (int i = 0; i < property.arraySize; i++)
{
if (property.GetArrayElementAtIndex(i).stringValue == value)
return true;
}
}
else
return property.stringValue == value;
return false;
}
private void OnAddClickHandler(object tag)
{
int index = list.serializedProperty.arraySize;
list.serializedProperty.arraySize++;
list.index = index;
var element = list.serializedProperty.GetArrayElementAtIndex(index);
element.stringValue = (string)tag;
serializedObject.ApplyModifiedProperties();
}
public override void OnInspectorGUI()
{
GUILayout.Space(6);
serializedObject.Update();
list.DoLayoutList();
serializedObject.ApplyModifiedProperties();
GUILayout.Space(3);
}
}
So, to use the example, you would call GetComponent().HasTag("Player") to check if the custom tags list contains the player tag.
PS: This answer shows how to implement a custom script that provides multiple tags per GameObject. There may be reasons for using such a system, but in general, I wouldn't recommend this for use as the base of an inventory system. In my idea, multiple tags would make sense if certain environment objects are tagged by designer and runtime scripts with multiple properties within a complex AI or quest scenario. For example, if the player picks up a knife from the counter, it gets the tag "TouchedByPlayer" and then the detective will accuse the player of being a suspect. And now there are many more tags like "HasBloodStains" etc which are all used by the narrative and quest system of the game.
Hi,
i found your solution in this thread. https://answers.unity.com/questions/1470694/multiple-tags-for-one-gameobject.html
you write the following:
Then ins$$anonymous$$d of asking for the GameObject.tag you do GetComponent().tags to check if the object is tagged this way.
I just can't check with GetComponent().tags if the object is tagged or not.
Could you please help me?
You need to add the script "CustomTag" and the custom editor script to the project and then add a CustomTag component instance to one of your GameObject. Then, you can call GetComponent().HasTag("Player"), for example.
Hello, I tried this and it works really well, but is there a way to use this in an OnCollisionEnter2D method? To detect if a collider is colliding with an object that has the multi tag
Yes, you would have to use GetComponent like this:
public class $$anonymous$$yCharacter : $$anonymous$$onoBehaviour
{
public void OnCollisionEnter2D(Collision2D collision)
{
var multiTag = collision.gameObject.GetComponent<CustomTag>();
if (multiTag != null && multiTag.HasTag("SomeTag"))
{
Debug.Log("$$anonymous$$yCharacter collided with SomeTag.");
}
}
}
Hello, I'm putting tags on AI so they can tell each other apart based on what faction they are in and their race etc. I am trying to get them to fight each other based on these tags but im getting a "cannot implicitly convert type 'bool' to 'UnityEngine.GameObject'" when I use this code "GameObject target = GetComponent().HasTag("bandit");". Do you have a solution or work around for this? Thanks
The example code I posted is of course limited. HasTag only returns true or false and tells you if the component has this tag assigned. If you want to find any or all targets with this tag you would have to get a list of all GameObjcts and iterate over it to check each one for HasTag and if true, you know that it's a valid target. Here's an example that doesn't care about performance:
GameObject randomTarget = null;
foreach (var potentialTarget in FindObjectsOfType<CustomTag>())
{
if (potentialTarget.HasTag("bandit"))
{
randomTarget = potentialTarget.gameObject;
break;
}
}
Best to use an enum for that and compare each unit's enum.
public enum Race { HU$$anonymous$$AN, ELF, DE$$anonymous$$ON, ANGEL }
public enum Faction { RED, BLUE, YELLOW }
public class Unit
{
[Serializefield] private Race race;
[Serializefield] private Faction faction;
}
This will be much more performant.
Wow this is wery cool with W and that is wery rare.
If you need to add tags using the Unity API and the script above, then
Add this code to your Tags script files:
Mono Behavior
public void AddTags(params string[] tagsToAdd)// Adds tag(s) to this Gameobject Tags component if this tag(s) exist in Unity Tags Array and this Component doesn't have one already
{
string[] unityTags = TagsGUI.GetUnityTags();
for (int i = 0; i < tagsToAdd.Length; i++)
{
bool doesTagExist = false;
for (int j = 0; j < unityTags.Length; j++)
{
if (tagsToAdd[i] == unityTags[j])
{
doesTagExist = true;
break;
}
}
if (!doesTagExist) // If tagsToAdd[i] does not exist in Unity Tags Array send warning log
{
Debug.LogWarning("Unity tag array doesn't contain tag: \"" + tagsToAdd[i] + "\".");
continue;
}
if (HasTag(tagsToAdd[i])) // If there is the same tag in TagsComponent of this gameObject
{
Debug.Log("There is the same tag: \"" + tagsToAdd[i] + "\"");
continue;
}
tags.Add(tagsToAdd[i]);
}
}
Editor
public static string[] GetUnityTags()
{
return InternalEditorUtility.tags;
}
(Maybe the code is bad, I'm just a newbie in gamedev. But it works)
Answer by Owen-Reynolds · Feb 20, 2018 at 01:37 AM
For anything even slightly complicated, throw away tags and use a script. Let the variables do all the work of tags. For example:
class inventoryItem : MonoBehavior {
public string name;
public int itemNum;
...
}
Then you can use GetComponent<inventoryItem>()
and examine those variables. Then you'll naturally start to add some functions.
Answer by ransomink · Feb 20, 2018 at 12:19 AM
There are Unity Assets for multiple tags. I'd check them out if you're really interested. Besides that, you can use inheritance if that's what you're thinking about. Is it something like, a sword is an item, a weapon, and a sword. Is that what you're trying to do?