I'm curious to what you guys use tags for, if you even use them at all. I don't feel like its efficient to use at all. However, I don't know what else I can use. What would you use when, for example, a game is full of boxes, and there is a button to highlight/create/remove them all? Would you findgameobjectwithtag and manipulate these gameobjects or use something else? Also, would you use tags to easily locate and differentiate walls or positions of gameobjects in a room? (North/east/south/west) If you have any best practices for tagging, I would love to hear about it!
Personally I don't use the tags themselves... I have used tag like things (usually as enums or something). But I find Unity's tags a bit limiting (especially since you have to predefine them... why strings if they have to be predefined??? That's a rhetorical question... I know why, it's just stupid). This isn't to say they're not used at all by my team. A tag can be used to hack together something in a pinch. But at the end of the day... there's almost always a better approach to whatever you're using a tag for.
I rarely use them, and most of the time they are only there in the prototype and are replaced with q custon solution. I personally hate the fact it's called a tag and an object can only have one. It might be because I'm not native to english but it feels wrong.
I do not use Unity tags, mainly because of performance issue (last time I checked they were slow and created some garbage collection). Even without talking about the performance issue, they are mostly a 'hack', so it's tempting to use them. They are so generic that they can be used for things completely different, which in the end will generate some readability problems in the project. It you want something similar to tags for the GameObjects, you can add components (with only data) to them, and if you want some performance when getting all the GOs with a given tag, you can add the objects to a list of objects with the same tag. In the end, you can have a dictionary of lists of gameObjects, so that you can get very fast all the objects with a given tag, just be sure to update the dictionary with the Awake() and Destroy() methods properly so that your dictionary is always up to date.
This is only if you do "obj.tag == MYTAG", use the 'CompareTag' method to avoid GC: https://docs.unity3d.com/ScriptReference/Component.CompareTag.html The garbage comes from 'tag' having to return a new string when pulling the data from the Unity/C++ side of things. The 'name' property has this same issue.
That's good to know. I didn't knew that the 'name' property had the problem, I just realized that I avoided the problem by having my own 'Name' for my ScriptableObjects (I suppose that the SO do have the same problem with their 'name' property). I did that because at first they weren't SO but plain classes. Now I realize that I have been lucky, thanks for the info.
I use tags for a few things. I play different footstep sounds when the player is walking depending on what he's walking on. Each tag maps back to a "surface type" that determines what kinds of sounds to play. I went with tags for this, instead of layers, because there is a lot more variety of surface types than layers would support. Similarly, for bullet impact sounds and decals, the surface type a bullet hits determines what kind of sound and decal to use. (cracked glass, concrete, metal, etc). Occasionally I'll check the tags when doing some raycast in cases where the limited number of layers prevents me from being able to have a dedicated layer for all combinations of traits. But I try to avoid that if possible. As for your other question of "finding" stuff in your scene, I have various use cases where I need to act on all the stuff in the scene every frame, or every fixed update. Using approaches like FindGameObjectsWithTag or FindObjectsOfType is much too slow for that. Instead, for each object I need to track in this way, I add a component that causes it to register itself with a singleton tracking service on Awake, and to unregister itself on destroy. Then my code can iterate over the arrays in my tracking service rather than finding the objects through Unity's methods. It turns out that this is fairly similar (I believe) to the approach that Hybrid ECS uses to track things performantly in the scene. So, if you're having performance issues due to needing to find and act on lots of stuff in the scene, you might consider Hybrid ECS as an approach.
Two words, endless possibilities - Physics Materials. Actually a lookup between Physics Material and Sound. So that's more than two words. Tags are near useless and more of a bad practice. Don't use them.
I use them for lots of things. - In OnCollisionEnter using CompareTag I check what kind of object a projectile hits. If it hit a Ship tagged object I do something to the ship, otherwise I don't. - My various UI windows sometimes need references to scripts on other UI windows, so I have a base UI object with references that branch out to every other UI window which I just tag with UI for an easy FindWithTag if needed. Though I often go back through my code later and set inspector references instead. - I have some use of tags on the client side of my networked game to establish references between objects. This is because objects are instantiated and spawned server side, but on the client side the instantiation of objects is in response to received network messages instead of in a place where object references can be directly cached from the appropriate script. The biggest problem with tags is they tend to promote a liberal use of FindGameObjectsWithTag, and that should only be used in one off situations, not every update.
I actually prefer OnEnable() to OnDisable() because my feeling is that a disabled Component should not be considered part of a master list of things. Getting back to the original question, in lieu of tags I like to use empty MonoBehaviors named appropriately. The ability to hang a bunch of them on any given GameObject gives them far better flexibility than tags.
For gameobjects, I find it's better to check if a gameobject has a component rather than a tag. It's very easy to just make a new component to identify an object at runtime. Hell, you could even make a "Tags" component that can contain multiple string tags, which is already better than Unity's single-tag system. In Unreal Engine all actors have a list of tags, which is very useful if one actor needs to be tagged to interact with multiple systems.
I use tags when I need to perform attacks. I use the Physics#OverlapSphere function, then I use the tag of certain objects to detect if they are enemies or not etc.
I use them for quick and dirty prototypes. For anything else, they really don't do the job effectively. The one object, one tag limitation is the real killer for me. It effectively means that only one in game system can rely on tags. Every other system must implement a custom solution. And since you are implementing a custom solution anyway, you might as will put everything onto the custom system. If I'm doing an information only system (what type of terrain am I walking on? what type of damage do I deal? etc), then I will normally add a component with an enum. If I am doing an action system (different objects can all be hit by damage), I tend to use interfaces. For finding objects I tend to use a registration system.
I love this and now am wondering (not for the first time): Are Monobehaviours implemented so efficiently that there is not much performance drawback from using them over tags? If so, I think I will adapt that method immediately to distinguish types of objects in my scene.
If a MonoBehaviour doesn't implement any event functions it will basically just happily sit in a list of components and do nothing.
Its ~72 bytes per MonoBehaviour (might be less in build). Other than that - its pretty much free. You can always profile to see the difference of the specific MB by adding multiple of it to the same gameObject each next step in editor. And run a separate script that does something like this: Code (CSharp): Profiler.BeginSample("Test"); Instantiate(*thatPrefab*); Profiler.EndSample(); Then check the GC alloc or memory profiler.