Search Unity

onCollisionEnter does not work on a child colider

Discussion in 'Scripting' started by techmage, Dec 8, 2009.

  1. techmage

    techmage

    Joined:
    Oct 31, 2009
    Posts:
    2,133
    I want to put a script with an OnCollisionEnter function on a child object of another object that has a rigid body on it, but when the script is on the child object it doesn't register.

    To say it another way, I have two cubes, one cube is the child of the other cube and the root cube has a rigid body on it. OnCollisionEnter works on the root rigid body cube, but that same function put on the child cube, which is not rigid body and just has a collider, the onCollisionEnter function stops working.

    Now it works when the root cube is not a rigid body. If both cubes just have colliders, the oncollisionenter works on the child colliders. But if the root object has a rigidbody, it seems to disable the OnCollisionEnter function for all it's children.

    Is this just how the Unity engine is? Or am I doing something wrong? How do I get the onCollisionEnter function to register on a child object or rigidbody?

    Or do I need to do some workaround to only have such a function on the root cube?
     
  2. andeeeee

    andeeeee

    Joined:
    Jul 19, 2005
    Posts:
    8,768
    This is actually the correct behaviour. You need to have the script and the rigidbody on the parent object.
     
    ATate and SinanAkkoyun like this.
  3. techmage

    techmage

    Joined:
    Oct 31, 2009
    Posts:
    2,133
    Is there some way to sense if it is colliding with a child collider of the rigid body?
     
  4. andeeeee

    andeeeee

    Joined:
    Jul 19, 2005
    Posts:
    8,768
    The child colliders don't register collisions with each other. This allows you to make combinations of colliders to model irregular shapes. Are your child colliders supposed to be free-moving relative to the parent?
     
    SinanAkkoyun likes this.
  5. techmage

    techmage

    Joined:
    Oct 31, 2009
    Posts:
    2,133
    No they aren't free moving, I just need to know when it collides with one of the children.
     
  6. andeeeee

    andeeeee

    Joined:
    Jul 19, 2005
    Posts:
    8,768
    If you have the OnCollisionEnter on the parent, then collisions with any of the children will also be detected.
     
    BeyondMASC likes this.
  7. Treva

    Treva

    Joined:
    Aug 26, 2009
    Posts:
    17
    Is it possible to detect the collision at the child object rather than at the parent object? For example, if I want to detect a head shot.
     
    SinanAkkoyun likes this.
  8. andeeeee

    andeeeee

    Joined:
    Jul 19, 2005
    Posts:
    8,768
    The Collision object passed to OnCollisionEnter has a field representing the collider that was hit. If you have references to all your object's colliders (say with public variables) you can compare them with the collider in the Collision object:-
    Code (csharp):
    1. var headColl: Collider;
    2. var bodyColl: Collider;
    3. // etc...
    4.  
    5. function OnCollisionEnter(coll: Collision) {
    6.     if (coll == headColl) {
    7.         // Do the head shot thing
    8.     }
    9. }
     
    blakesnake100 and SinanAkkoyun like this.
  9. eekllc

    eekllc

    Joined:
    Dec 14, 2009
    Posts:
    19
    I think I'm having the same problem and I think that this answer, while true, isn't correct for what we're looking for. Or maybe it was and my issue is different.. either way...

    I have a parent object with a rigidbody and a script.
    Then I have a group of colliders as children of that. When OnCollisionEnter is called on the parent, coll will equal the collider of the *other* object that I hit. How can I get a reference to which child collider on the object running the script was hit?
     
  10. eekllc

    eekllc

    Joined:
    Dec 14, 2009
    Posts:
    19
    I figured it out. If anybody's interested:

    Code (csharp):
    1.  
    2. foreach (ContactPoint c in collision.contacts)
    3.         {
    4.             Debug.Log(c.thisCollider.name);
    5.         }
     
  11. tiz777

    tiz777

    Joined:
    Jan 20, 2010
    Posts:
    93
    Hei great! that helped me a lot!

    Many thanks for your post :)
     
  12. helioraanswer

    helioraanswer

    Joined:
    Apr 7, 2009
    Posts:
    412
    I can't get it to work.

    Code (csharp):
    1.  
    2. var headColl: Collider;
    3. var bodyColl: Collider;
    4. // etc...
    5.  
    6. function OnCollisionEnter(coll: Collision) {
    7.     if (coll == headColl) {
    8.         if(coll.gameObject.name == "Blast(Clone)")
    9.         {
    10.             //SendMessageUpwards("Destroy");
    11.             Destroy(gameObject);
    12.         }
    13.     }
    14.     if (coll == bodyColl) {
    15.         Destroy(gameObject);
    16.     }
    17. }
    18.  
    I have a human model which is the parent and has this script. And a simple capsule with capsule collider added as a child. It's a bit away from the model so you can hit it easily. For the variable I selected the name of that capsule object. But when you shoot it, or hit it nothing happens.

    What am I doing wrong?
     
  13. CapnCromulent

    CapnCromulent

    Joined:
    Sep 7, 2010
    Posts:
    45
    I know this is an old thread, but I ran into the same problem recently.

    andeee's advice is not quite right, but eekllc is.

    The gameObject, collider, and rigidbody in the Collision parameter all refer to the topmost object involved in the collision. To determine which sub-collider was hit, you need to look at the contact points's thisCollider or otherCollider depending on whether you're calling OnCollisionEnter on the target or the projectile.
     
    fwolf_unity likes this.
  14. buscommando

    buscommando

    Joined:
    Oct 24, 2012
    Posts:
    6
    Ok, but what if I'm using a trigger and not a collision?

    Is there a way to get the collision contacts from the collider in OnTriggerEnter?
     
  15. WillB

    WillB

    Joined:
    Jan 10, 2013
    Posts:
    3
    OnTriggerEnter doesn't give contact point info, only Collisions do.
     
    AmmarFan likes this.
  16. TriplePAF

    TriplePAF

    Joined:
    Aug 19, 2009
    Posts:
    246
    Hello,

    I have one parent gameobject with a rigidbody and 100 child block objects with each a box colliders on it. My other object is a simple sphere which can bounce against the block structure. The sphere contains a rigidbody with sphere collider. The OnCollisionEnter Function from the sphere is reporting due reading the contact normals othercollider object often itself back instead of 1 of the 100 child blocks. With other words: othercollider is for my setup not reliable. Is there some more reliable way that tells me which block was hit and only includes 1 rigidbody for my block structure setup.

    Thanks,

    Peter.
    Unity Pro 4.3.2
     
    Last edited: Jan 9, 2014
  17. TriplePAF

    TriplePAF

    Joined:
    Aug 19, 2009
    Posts:
    246
    I think that I found a suitable solution. Here comes the code snippet:

    Code (csharp):
    1. bool otherColliderFound;
    2.  
    3.     void OnCollisionEnter (Collision col)
    4.     {
    5.         otherColliderFound = false;
    6.  
    7.         if (col.gameObject.GetInstanceID () != gameObject.GetInstanceID ()) {
    8.             otherColliderFound = true;
    9.             ExecuteCollision (col);
    10.         }
    11.     }
    12.  
    13.     void OnCollisionStay (Collision col)
    14.     {
    15.  
    16.         if (!otherColliderFound  col.gameObject.GetInstanceID () != gameObject.GetInstanceID ()) {
    17.             ExecuteCollision (col);
    18.         }
    19.     }
    20.  
    21. void ExecuteCollision (Collision col)
    22.     {
    23.               //Do some collision stuff
    24.     }
    25.    
    26.    

    Peter.
     
    Last edited: Jan 9, 2014
  18. TriplePAF

    TriplePAF

    Joined:
    Aug 19, 2009
    Posts:
    246
    The above example is useless if you wan't to do some proper collision math with contact points in OnCollisionEnter. In my case it was possible to use a static batch without any rigidbody which provides accurate information about the contact points. Even here the othercollider object is not always populated. It seems a normal behaviour from Physics: http://gamedev.stackexchange.com/questions/40048/why-doesnt-unitys-oncollisionenter-give-me-surface-normals-and-whats-the-mos

    If you need more information about the other object then Physics provides then cast a ray to it with something like this (incomplete snippet). Please also note that the surface normal is not the same as the collision normal due for example more objects that are mostly hit (wall, floor and so on):

    Code (csharp):
    1.  
    2.  
    3. void OnCollisionEnter (Collision col)
    4. {
    5.             m_breakOutPhysicsObjectTest = false;
    6.  
    7.             for (int i = 0; i < col.contacts.Length; i++) {
    8.  
    9.                 if (Physics.Raycast (m_transform.position, (col.contacts[i].point - m_transform.position).normalized, out m_collisionhit, 5f, m_onCollisionRayLayerMask)) {
    10.                     m_contactPointNormal = m_collisionhit.normal;
    11.                     //Debug.DrawRay (m_transform.position, (col.contacts[i].point - m_transform.position).normalized, Color.red);
    12.                     //Debug.Log (m_collisionhit.transform.gameObject.name);
    13.                     //Debug.Break();
    14.                     m_breakOutPhysicsObjectTest = true;
    15.                 }
    16.  
    17.                 //Debug.Log (m_breakOutPhysicsObjectTest);
    18.  
    19.                 if (m_breakOutPhysicsObjectTest)
    20.                     break;
    21.             }
    22.  
    23.  

    Peter.
     
    Last edited: Feb 16, 2014
  19. Cheese_Dragon_Games

    Cheese_Dragon_Games

    Joined:
    Dec 20, 2013
    Posts:
    9
    @eekllc How do I use this, do i put it in onCollisionEnter?
     
  20. V4nKw15h

    V4nKw15h

    Joined:
    Dec 16, 2011
    Posts:
    47
    I too ran into this problem.

    I had a rigidbody and a collider on a root object, and the same on one of it's children. I also had OnCollisionStay in scripts both on the root and the child. I expected OnCollisionStay to be only called on the child's script (when the child collider hit something) but it was called on both the child script AND the root. This was not the behaviour I wanted and searching through all collision contact points seemed a bit like overkill.

    I found a much simpler solution. I created a new child, and moved the collider from the root object to this new child. I also moved the corresponding script containing OnCollisionStay to this new child.

    By moving all colliders off the root object, they each call OnCollisionStay on their respective scripts separately. There is no longer a script with OnCollisionStay on my root rigidbody object and so I'm no longer getting the double call, and can determine exactly which collider and child object is triggering OnCollisionStay.
     
  21. SinanAkkoyun

    SinanAkkoyun

    Joined:
    Aug 4, 2016
    Posts:
    6
    It don't works for me, I do have a rigidbody on my character and the colliders on the bones of the character and OnCollision don't fires, even if the script is attached to the gameobject with the rigidbody..
     
    Noxury likes this.
  22. NikitaVr

    NikitaVr

    Joined:
    Aug 25, 2013
    Posts:
    8
    I found this will give you the right child object, instead of the parent of the collider:

    Collision seems to refer to the collision with the parent, where as collision.collider is the specific collider, which lets
    you retrieve the child GameObject the collision happened with.

    Code (CSharp):
    1. private void OnCollisionEnter(Collision collision)
    2.     {
    3.         GameObject childObject = collision.collider.gameObject;
    4.     }
     
    BWs_Unity likes this.
  23. eskivor

    eskivor

    Joined:
    Aug 5, 2015
    Posts:
    13
    There's no logic at all, OnTriggerEnter works well in a child collider (unlike OnCollisionEnter)
     
  24. hippocoder

    hippocoder

    Digital Ape

    Joined:
    Apr 11, 2010
    Posts:
    29,723
    There is logic to it. Unity has the concept of compound colliders.

    A compound collider is automatically created for all child colliders where the parent has a rigidbody. This is documented in the manual. When a child also has a rigidbody, it is no longer a compound collider but a separate object in the physics system.

    And of course trigger enter will or will not work depending on if the entering object is a trigger, has a rigidbody or the entered body has one of the combinations.

    If you disagree, you should report a bug. If you meant that it's weird that a trigger needs at least one of them to have a rigidbody, then I agree, it appears weird, but it's consistent behaviour still.
     
  25. reimeguroinferno_unity

    reimeguroinferno_unity

    Joined:
    Mar 22, 2018
    Posts:
    1
    Hay so I have an empty object that is the parent of multiple mesh crafting a humanoid. I have a robot body on an empt so that I can access it with code to move my character. Now each child mesh has a mesh collider with cover ticked, and I want to use those collider so that the the parent empty will bump into other object to prevent it from moving.
     
  26. KingFruit

    KingFruit

    Joined:
    Sep 29, 2016
    Posts:
    1
    Thank you so much for this, solved the particular issue I was having
     
  27. BWs_Unity

    BWs_Unity

    Joined:
    Nov 24, 2020
    Posts:
    1
    Awesome man, perfect solution