KamielDev
KamielDev

Reputation: 579

How to know which collider triggered the call to OnTriggerEnter in Unity (on a gameobject with multiple colliders)

A problem I recently had was the following.

I had a Player object with a Player script attached to it.

My gameobject hierarchy looked something like this:

enter image description here

Player (parent)           // Has Player.cs and a RigidBody attached to it.
    - GFX                 // Holds graphics like sprites
    - PhysicsHolder       // Empty object
        - Body            // Has a box trigger 2d defining the player body
        - Feet            // Has a box trigger 2d  defining the player feet area

I wanted to handle all the collision events for my player in Player.cs, so they were all in one place. The problem was, it was quite hard to differentiate between whether the Feet object or Body object triggered the call to OnTriggerEnter2D.

If you've set up your hierarchy like I have, both the Body and Feet triggers will call OnTriggerEnter2D in their parent script, and thus will call Player.OnTriggerEnter2D.

In OnCollisionEnter you get a reference to the collider which triggered the call with collision.other. You could then easily set a tag for each respective collider and do collision.other.CompareTag()

Since OnTriggerEnter doesn't have a Collision object as a param but a Collider2D, and a Collider2D doesn't have a reference to other or something similar, you can't do the same in a case where you want to handle a trigger event. Using this.CompareTag() in Player.cs would not suffice either, since you'd be comparing the tag of the parent Player object, and not the Feet or Body object.

I've since found a solution to this problem which I will provide here. Maybe it's of use to any of you who come across this problem.

Upvotes: 4

Views: 7493

Answers (1)

KamielDev
KamielDev

Reputation: 579

My solution was as follows:

First I added the following script to my project

using UnityEngine;
using UnityEngine.Events;

/// <summary>
/// Delegates the call to OnTrigger2D for this object to another object.
/// </summary>
public class OnTrigger2DDelegator : MonoBehaviour
{
    private Collider2D caller;

    private void Awake()
    {
        caller = GetComponent<Collider2D>();
    }

    [Tooltip("Which function should be called when trigger was entered.")]
    public UnityEvent<OnTriggerDelegation> Enter;

    [Tooltip("Which function should be called when trigger was exited.")]
    public UnityEvent<OnTriggerDelegation> Exit;

    void OnTriggerEnter2D(Collider2D other) => Enter.Invoke(new OnTriggerDelegation(caller, other));
    void OnTriggerExit2D(Collider2D other) => Exit.Invoke(new OnTriggerDelegation(caller, other));
}

/// <summary>
/// Stores which collider triggered this call and which collider belongs to the other object.
/// </summary>
public struct OnTriggerDelegation {

    /// <summary>
    /// Creates an OnTriggerDelegation struct.
    /// Stores which collider triggered this call and which collider belongs to the other object.
    /// </summary>
    /// <param name="caller">The trigger collider which triggered the call.</param>
    /// <param name="other">The collider which belongs to the other object.</param>
    public OnTriggerDelegation(Collider2D caller, Collider2D other)
    {
        Caller = caller;
        Other = other;
    }

    /// <summary>
    /// The trigger collider which triggered the call.
    /// </summary>
    public Collider2D Caller { get; private set; }

    /// <summary>
    /// The other collider.
    /// </summary>
    public Collider2D Other { get; private set; }
}

Most of the explanation is already there in the summaries I wrote, but here's some more detail.

Adding this script to for example, the Feet object (which remember, has the BoxCollider2D thats a trigger representing my player's feet), will add some fields to the Inspector. You can then drag in your Player object, and select which function should be ran on the Player object.

If we now add the following code to our Player.cs, we can handle the trigger event in Player.cs whilst having access to both the entered or exited and the incoming or outgoing colliders respectively.

public void OnFeetTriggerEnter(OnTriggerDelegation delegation)
{
    // will print out "Feet" in my example
    Debug.Log(delegation.Caller.name); 
    // will print out "Ground" in my example if the feet trigger was entered by an object called Ground.
    Debug.Log(delegation.Other.name); 
}

Selecting this method in the OnTrigger2DDelegator section in the inspector then yields something that looks like this:

added fields in the inspector

And voila! You can now add another function in Player.cs like OnBodyTriggerEnter, add another delegator to the body object and select the function in the inspector.

This lets you set up all your trigger events in the same file.

Note: If you have no need to access the entered or exited collider, but do want to be able to differentiate between calls to different triggers, you can use the same setup I've shown here, but skip the struct. Just replace UnityEvent<OnTriggerDelegation> with UnityEvent<Collider(2D)> and pass other to Enter.Invoke(). Then your Player.cs handling functions can look something like public void OnFeetTriggerEnter(Collision(2D) other)

Upvotes: 1

Related Questions