Can't get trigger calls from children

Using child objects with separate colliders is a critical game function as it enables one to set the children on separate layers to adjust what they collide with. Moreover, this functionality is critical to use with triggers, so that you can have things like an item pickup that collides with the world, but enemies and NPC’s walk right through it, but it triggers a command when a player touches it.

For the most part, the only things I want to actually block movement is the world itself. All other objects and characters will pass right through each other and rely on trigger calls to detect when they “touch” another object. I rely heavily on using child objects on separate layers to achieve this.

However, I am having problems with getting OnTigger calls to work with children.
I’ve been running some tests currently using an object that uses only one layer just to be sure, and I’m only getting OnTrigger calls in certain situations.

First of all, I can’t get any OnTrigger calls when using a CharacterController, and this seems to clearly be a bug since the whole intent of a Character Controller is to use it in place of a rigidbody; if I have add a dummy rigidbody just to get OnTrigger calls then it defeats the intent of a CharacterController.

Second of all, on an object with no ridigbody nor characterController (eg, a pickup item) the OnTrigger functions will not get called when the trigger collider is on a child of that object. It gets called if the trigger is on the parent object, but not when its on the child. (This really feels like a bug.) The child triggers will get called if the object has a rigidbody, but I don’t really need a rigidbody on an object that is just supposed to sit there.

How then do I have an object get trigger calls using separate layers?

There are collider callbacks for CharacterControllers directly, such as this:

https://docs.unity3d.com/ScriptReference/MonoBehaviour.OnControllerColliderHit.html

You can always stick Kinematic Rigidbodies on sub-parts of your hierarchy to get trigger calls.

In fact, they even suggest it in the docs:

https://docs.unity3d.com/ScriptReference/Collider.OnTriggerEnter.html

Finally, you can always discriminate in the collider callback to figure out what part of you contacted in your child parts.

8167928--1062830--Screenshot-mwfizz-132984093426890650.png

See attached package, but the gist of it is:

using UnityEngine;

// @kurtdekker
// To use:
//    - put this at the root, where the Rigidbody is
//    - put colliders on here, OR on any child objects
//    - drag body part colliders into public fields

public class ReportWhatBodyPart : MonoBehaviour
{
    [Header( "Fill these out appropriately:")]
    public Collider MyLegs;
    public Collider MyBody;
    public Collider MyHead;

    [Header( "To see what happens.")]
    public UnityEngine.UI.Text TextOutput;

    void Log( string s)
    {
        TextOutput.text += "\n" + s;
    }
    void OnCollisionEnter( Collision collision)
    {
        Log( "Collision with " + collision.collider.name + "! Details of contacts:");
        foreach( var contact in collision.contacts)
        {
            var colliderOnMe = contact.thisCollider;

            if (colliderOnMe == MyLegs)
            {
                Log( "I collided on my legs at" + contact.point);
                continue;
            }
            if (colliderOnMe == MyBody)
            {
                Log( "I collided on my body at" + contact.point);
                continue;
            }
            if (colliderOnMe == MyHead)
            {
                Log( "I banged my head at " + contact.point);
                continue;
            }
            Log( "hit something else!");
        }
    }
}

Full package enclosed.

8167928–1062827–DetectingBodypartCollisions.unitypackage (5.94 KB)

1 Like

One, those don’t provide “Enter” and “Exit” calls.
Two, those don’t provide trigger collision.
But more to the point, those don’t provide “Enter” and “Exit” calls for trigger collision.

As for your example:
That’s quite the setup you have there, but I’m not looking to get calls from specific children. I just want the children to have their triggers call the script in the parent, the same as will happen if I have a rigidbody in the given object.

It just feels dumb; I have a (dummy) rigidbody in my player, and I can get triggers calls in my separate object when the player touches it, but not if the triggers are in a child of that object, unless I put another dummy rigidbody inside that separate object.
I have to add a worthless rigidbody that does absolutely nothing on a stationary object just to get it to register the triggers in its children.
I mean, if I need some kind of component so that Unity knows to treat all the child objects as one unified trigger, then why can’t I add a component that does just that, instead of a whole rigidbody?

1 Like

My understanding is that the rigidbody is the component that actually calls these collision methods.

They’re not called by some other magic thing simply “because” there is a rigidbody. It’s the rigidbody doing the calls.

If anyone still has this issue consider this approach:

  • create a script that will attach onto the same GameObject that contains the collider you want events from
  • create public ‘Action’ events that you can subscribe to from another script
  • Add each “On Trigger” method, inside each method: call your Action event
public class ColliderEventConduit : MonoBehaviour
{
    public Action OnMouseExitEvent;
    public Action OnMouseOverEvent;
    public Action OnMouseDownEvent;
    public Action OnDestroyEvent;
    public Action OnDisableEvent;
    public Action OnEnableEvent;

    private void OnMouseExit() => OnMouseExitEvent?.Invoke();
    private void OnMouseOver() => OnMouseOverEvent?.Invoke();
    public void OnMouseDown() => OnMouseDownEvent?.Invoke();
    private void OnDestroy() => OnDestroyEvent?.Invoke();
    private void OnDisable() => OnDisableEvent?.Invoke();
    private void OnEnable() => OnEnableEvent?.Invoke();
}

then from another script you can subscribe to the events like this (don’t forget to unsubscribe!):

private void OnEnable()
    {
        ColliderEventConduit eventConduit = _primitive.AddComponent<ColliderEventConduit>();
        eventConduit.OnMouseExitEvent += this.OnMouseExit;
        eventConduit.OnMouseOverEvent += this.OnMouseOver;
        eventConduit.OnMouseDownEvent += this.OnMouseDown;
    }

    private void OnDestroy()
    {
        if (_primitive != null && _primitive.TryGetComponent<ColliderEventConduit>(out ColliderEventConduit eventConduit))
        {
            eventConduit.OnMouseExitEvent -= this.OnMouseExit;
            eventConduit.OnMouseOverEvent -= this.OnMouseOver;
            eventConduit.OnMouseDownEvent -= this.OnMouseDown;
        }
    }