Distinguishing between trigger colliders of the same object hierarchy


Júlio Rodrigues · — Last update ·

Your character has a shield, armor, and helmet. The character got hit by an enemy. A game mechanic requires differentiating what part of the character has been hit. A hit to the helmet is really different than a hit to the shield. How to make this distinction if the character has a hierarchy of objects and it behaves as a compound collider? What if you have two colliders of different types in the same game object? I have some solutions to offer you.

For this article, we're assuming that all your colliders triggers. If you're using regular collisions there are other ways to achieve this but we won't mention them here, unfortunately.

If you aren't sure of what are compound colliders or if your collision stops working while you are applying the advice of this article, a read of our practical troubleshooting guide to fix collisions in Unity might solve your problems.

The limited usability of the OnTriggerEnter() method parameter—Collider

This article wouldn't exist if the OnTriggerEnter() method contained a parameter for not only the other object but also the "self" collider, that is, the collider that this MonoBehavior is in charge of. What we'll need to do is figure out a way to use this other parameter to extract the information we need.

There are basically two different ways to do that. The first way is to invert this responsibility, in our case, if we have an OnTriggerEnter() in the enemy script, the other would be ourselves (the player character). And the second way to do it would be by having a different MonoBehaviour for each of the different colliders we need to distinguish among them.

Evaluating from the enemy's point of view

For that idea, we're using pretty normal unity scripting through tags. Just attach this component to all the entities that can do harm (or interact with in any way) to the player. For more information on how to use tags please read our article on that subject.

public class EnemyBullet : MonoBehaviour {

    private void OnTriggerEnter (Collider other) {
        if (other.CompareTag ("Helmet")) {
            // todo: insert game logic
        } else if (other.CompareTag ("Armor")) {
            // todo: insert game logic
        } else if (other.CompareTag ("Shield")) {
            // todo: insert game logic
        }
    }

}

The same way we're using tags, we could also be using components. If you're concentrating data on the player object, health points, for example, you could find it with the not so common GetComponentInParent() from the GameObject class. That would look like this

public class EnemyBullet : MonoBehaviour {

    public int power;

    private void OnTriggerEnter (Collider other) {
        if (other.CompareTag ("Helmet")) {
            other.gameObject.GetComponentInParent<Player>().Damage(power * 3);
        } else if (other.CompareTag ("Armor")) {
            // todo: insert game logic
            other.gameObject.GetComponentInParent<Player>().Damage((int) (power));
        } else if (other.CompareTag ("Shield")) {
            // todo: insert game logic
            other.gameObject.GetComponentInParent<Player>().Damage((int) (power * 0.5f));
        }
    }

}

There's an improvement that can be made to this snippet, namely, creating constants for all the string literals. If you want to see how this can be done and more on tags please check our article on that subject

If you can't help yourself but feel bad about all these repeated uses of GetComponentInParent(), please have in mind that Unity is not strictly inheritance-based object oriented. Since we have component as first-class citizens it's natural for us to be looking for them much more frequently than in a non-composition based framework.

Using multiple scripts (MonoBehaviours)

Instead of having a big chain of ifs inside our OnTriggerEnter() method, we'll create a script for each collider that we need to distinguish. For this to work you can't have more than one collider per game object since we can't differentiate between them without shooting ourselves in the foot (see the reason why in the next section).

Tagged object hierarchy sample
public class DistinguishHelmet : MonoBehaviour {

    private Player player;

    // Use this for initialization
    void Start () {
        player = GetComponentInParent<Player>();
    }

    private void OnTriggerEnter(Collider other) {
        var power = other.gameObject.GetComponent<HarmfulThing>().Power;
        player.Damage(power * 3);
    }

}
public class DistinguishShield : MonoBehaviour {

    private Player player;

    // Use this for initialization
    void Start () {
        player = GetComponentInParent<Player>();
    }

    private void OnTriggerEnter(Collider other) {
        var power = other.gameObject.GetComponent<HarmfulThing>().Power;
        player.Damage((int) (power * 0.5f));
    }

}

If this idea is implemented this way you'll end up with lots of code repetition. This is the kind of thing that can actually benefit from inheritance and the creation of an abstract class. If you're not familiarized with the subject you can start reading about it in the Programming Guide from Microsoft (creators of the language).

One last point is that you don't necessarily need to have multiple scripts to achieve this, it's just a matter of identifying them somehow, a tag should do it.

Please, do NOT compare the Collider type

If you look for a solution on how to do this on the internet, you might end up finding that you could do something like

if (other.GetType() == typeof(BoxCollider))

The reason for that not being a good solution even though it works is that it shouldn't matter for your treatment of gameplay logic whether someone used a BoxCollider or a SpherCollider, both are doing the same thing in the point of view of this functionality, they just say whether there was an intersection or not.

More than conceptually wrong this can also lead to a maintenance issue when you forget (or your game designer) that you're relying on the collider type to control gameplay logic and later change it for better accuracy and you'll end up with a VERY SUBTLE BUG that's extremely hard to catch when your source code base gets big—and it will.

How to choose the best way to do it for your case

The main difference between the two solutions is that in one we're concentrating all the knowledge about how to handle the trigger intersection in one script, belonging to the object hierarchy that actually has the data that needs to be manipulated, and in the other way we need to look for this component using the Collider other parameter.

It depends solely on what "direction of thought" you prefer, since in terms of performance both are very similar. If I had to choose I'd go with the first one as my default solution and if the OnTriggerEnter() starts to get too big I'd break it in more methods or in very specialized classes.

The more code your game has the more time you'll need to spend thinking in the best abstractions for organizing your code to keep it maintainable.

Now that you can distinguish between multiple colliders in the same game object hierarchy let me know what are you building on [email protected], I'm always excited to see what people are building using Unity, you can send me questions too!

If this guide was helpful for you, your game dev friends might like it too, please spread the word! And be sure to subscribe to our email list to get tutorials like this right into your inbox.

Detailed Unity Tutorials in your inbox

Subscribe if you want to receive articles and tutorials like this one right in your inbox. 
Classify in:
  • trigger detection
  • scripting
  • unity
© Bladecast 2018. All rights reserved.