In any given game, you are probably going to find dozens, if not hundreds of different objects colliding or intersecting with one another. Hence, one of the first things you learn in Unity is how to identify the type of object you have touched. These are the most common ways to do so among beginners:
void OnTriggerEnter(Collider other) {
// If a GameObject has an "Enemy" tag, remove him.
if(other.tag == "Enemy") {
Destroy(other.gameObject);
}
}
void OnCollisionEnter(Collision collisionData) {
// If a GameObject has an "Enemy" tag, remove him.
if(collisionData.collider.tag == "Enemy") {
Destroy(other.gameObject);
}
}
Essentially, the idea is checking whether the object we are colliding with or touching has been labelled with an Enemy tag, before we perform any action on the object.
While it is simple and easy-to-understand, there are better ways of identifying objects we are colliding with.
Comparing tags more efficiently
Firstly, the tag comparison method shown above isn’t the best way. It is actually more efficient to use CompareTag()
, which is available to both GameObjects and Components (explained below). The method can be used by both GameObjects and Components, which means that Collider objects have access to the method too.
void OnTriggerEnter(Collider other) {
// If a GameObject has an "Enemy" tag, remove him.
if( other.CompareTag("Enemy") ) {
Destroy(other.gameObject);
}
}
According to this post on Unity answers, this is why CompareTag()
is faster than a direct comparison:
The key difference between tag and compareTag is that compareTag does not result in a heap allocation. In Unity, retrieving strings from game Objects will create a duplicate of the string, which will need to be garbage collected.
The source I found reports a ~27% increase in performance of
compareTag(aString)
vs. usinggameObject.tag == "aString";
Source: Unity 5 Game Optimization by Chris Dickinson
By leoszeto — Unity Answers
In simple language, the reason why CompareTag()
is faster is because the use of GameObject.tag
creates an extra variable that will need to be garbage collected.
Name-checking
If you have specific types of GameObjects that are unique (i.e. only 1 of it exists at any given time), you can give it a unique name and simply check its name instead of its tag.
void OnTriggerEnter(Collider other) {
// If the touching GameObject is named Player, delete it.
if( other.name == "Player" ) {
Destroy(other.gameObject);
}
}
In case you don’t know, there are a couple of ways you can set the name of a GameObject.
Names are not the best way of identifying GameObjects, but it saves you the trouble of assigning tags to certain objects. You just have to make sure that the names match exactly.
Component checking
The best and most scalable way — by far — is checking whether the GameObject has a particular Component. This is how it’s done:
void OnTriggerEnter(Collider other) {
// If the touching GameObject has a Component called Enemy, delete it.
if( other.GetComponent<Enemy>() ) {
Destroy(other.gameObject);
}
}
GetComponent()
searches a GameObject and retrieves a Component named Enemy
. If such a Component is not found, it returns null
. When put into a conditional, it is automatically converted into a boolean value — a null
value becomes false
, and any other value becomes true
.
For readability, some people may choose to make the conditional more explicit:
if( other.GetComponent<Enemy>() == null )
One benefit of checking your object in such a way is that you don’t have to make sure all your GameObjects are tagged and / or named correctly, which makes your game much easier to scale. Otherwise, you would have to create a tag for every type of GameObject in your game, and make sure that they are all tagged correctly to replicate this functionality.
Another benefit is that checking your GameObjects this way gives you access to the component without you having to do a separate function call to retrieve it.
void OnTriggerEnter(Collider other) { // Try to get the Rigidbody component of the colliding GameObject. Rigidbody rb = other.GetComponent<Rigidbody>(); // If the object has a Rigidbody, reflect it back at the same speed. if( rb ) { rigidbody.velocity *= -1; } }
If you used tag checking, you would have to do a GetComponent()
call anyway after checking the tag, which makes the tag checking redundant. See the example below:
void OnTriggerEnter(Collider other) { // If the object has a PhysicsObject tag, reflect it back at the same speed. if( other.CompareTag("PhysicsObject") ) { // Get the Rigidbody component of the colliding GameObject. Rigidbody rb = other.GetComponent<Rigidbody>(); rigidbody.velocity *= -1; } }
Additionally, because you are unable to assign multiple tags to a single GameObject, you are unable to make your conditions more specific.
// Check if an object has 2 tags assigned // (will never succeed because a GameObject can only have 1 tag). if ( other.CompareTag("PhysicsObject") && other.CompareTag("Projectile") )
But you can do that with Components:
// Does our GameObject have both the Rigidbody and Projectile components?
if ( other.GetComponent<Rigidbody>() && other.GetComponent<Projectile>() )
GameObjects and Components
In Unity, everything that you create on your Scene are GameObjects. At the same time, there are different Components in the Scene interacting with each other all the time. This is because each GameObject contains multiple Components, and each Component adds a set of behaviours to a GameObject.
So why is it that, in our examples above, other.name
, other.tag
and other.GetComponent()
successfully retrieve information from the GameObjects they belong to? Note that, in these examples, other
is not a GameObject, but a Collider
Component.
Because Unity has designed these as shortcuts to make things more convenient, so you don’t need to write more code to achieve the same things. If these shortcuts weren’t available, you would have to access the GmaeObject from the Component first, then access the property: e.g. other.gameObject.name
, other.gameObject.tag
.