Search Unity

  1. Welcome to the Unity Forums! Please take the time to read our Code of Conduct to familiarize yourself with the forum rules and how to post constructively.
  2. Dismiss Notice

Why Does Raycast Hit Return Different Transform and Collider?

Discussion in 'Scripting' started by John-B, Mar 25, 2020.

  1. John-B

    John-B

    Joined:
    Nov 14, 2009
    Posts:
    1,251
    I'm doing a Physics.Raycast, and am running into a problem because the transform it returns doesn't match the collider. In other words, hit.transform.name <> hit.collider.name. In this particular case, the transform it returns doesn't even have its own collider; the collider it's hitting is attached to its child. So why is the collider name it's returning correct, but the transform name is a parent object with no collider of its own? I don't think I've ever seen a hit transform not match a hit collider, I've always assumed they identify the same object.
     
    Last edited: Mar 25, 2020
  2. Chris-Trueman

    Chris-Trueman

    Joined:
    Oct 10, 2014
    Posts:
    1,256
    From the docs:

    collider: The Collider that was hit.
    transform: The Transform of the rigidbody or collider that was hit.

    So if the collider doesn't have a rigidbody it will return the parent if it has a rigidbody, I assume.
     
    PraetorBlue, Bunny83 and Kurt-Dekker like this.
  3. Baste

    Baste

    Joined:
    Jan 24, 2013
    Posts:
    6,194
    Yup! This relates to compound Colliders, scroll down on this page to read about it.

    In essence, any time you're interacting with a compound collider, the transform is the collider's transform, and the rigidbody is the parent rigidbody.
     
    Bunny83 likes this.
  4. John-B

    John-B

    Joined:
    Nov 14, 2009
    Posts:
    1,251
    I don't understand what a raycast has to do with rigidbodies. According to the script API, a raycast returns "True if the ray intersects with a Collider, otherwise false." There's no mention of RBs. There doesn't need to be a RB attached for a raycast to hit a collider, at least in my experience.
     
  5. Baste

    Baste

    Joined:
    Jan 24, 2013
    Posts:
    6,194
    If you read the thing I posted a link to, that'd probably clear things up.

    A shorter version, if you don't have the time; If a collider has a rigidbody in it's parent, the collider is treated as a part of that rigidbody, meaning that collisions and such affect that rigidbody, and raycasts hit that rigidbody.

    If it doesn't have a rigidbody, then no rigidbody is taken into account. Obviously.
     
  6. lordofduct

    lordofduct

    Joined:
    Oct 3, 2011
    Posts:
    8,380
    Note, this all relates to how the physics engine sorts its colliders internally. It's not just one giant list of colliders. They're actually categorized and a tree is made of them based on their locations. Since the tree is sorted by location it means you can optimize your physics tests (only need to test those objects near where the physics incident occurred).

    Static colliders, those without a rigidbody attached, are in their own tree. This tree seldom has to update because well... it's static.

    Dynamic colliders, those with a rigidbody attached, are in another tree. This tree is designed to support updating since its members are dynamic and move. The sorting of this tree is based on the rigidbody, and not on the collider, so when you cast through this tree you're going to get back the rigidbody. Why the rigidbody? It's because that's what deals with the state of the compound body, if it's asleep, velocity, mass, etc. And these properties are what effect how it is sorted in its tree, so of course it's the dependent object.

    Now of course we still get the collider back as well. And you might ask why it doesn't just use THAT transform instead of the rigidbody. All the information is there and available... true. But the concept of the 'Rigidbody' is that the body is a compound object. Its colliders are what make up it.

    A wall is a static body, if I want the position of the wall, I want the position of the wall.

    A person is a dynamic body, if I want a position of the person, I want the position of its compound body... not its arm. If I wanted its arm, I'd look at its arm.

    And that's how this result works... the collision is on the body, so the related transform is the bodies transform. If I want to know the transform of the specific collider... well:
    collision.collider.transform

    You just explicity say you want that transform.

    ...

    Honestly Collision technically doesn't even need the 'transform' property. You could have just accessed them directly on the related RB or Collider explicitly. But really it serves as a short cut to those because otherwise if you just want "the transform of the body struck" (where body means the compound entity may that be the RB or a single Collider) you'd have to write an if/else to deal with if an RB is there or not.

    And actually... that's EXACTLY what Collision.transform is underneath. It's just a forwarding property that connects to the RB or Collider if RB is null or not:
    Code (csharp):
    1.  
    2.     /// <summary>
    3.     ///   <para>The Transform of the object we hit (Read Only).</para>
    4.     /// </summary>
    5.     public Transform transform
    6.     {
    7.       get
    8.       {
    9.         return !((Object) this.rigidbody != (Object) null) ? this.collider.transform : this.rigidbody.transform;
    10.       }
    11.     }
    12.  
    (from UnityEngine.Collision source)

    ...

    TLDR;

    the physics engine prioritizes the "compound body" over the "collider", and just happens to treat a lone collider as an independent body.
     
    Last edited: Mar 25, 2020
  7. John-B

    John-B

    Joined:
    Nov 14, 2009
    Posts:
    1,251
    Thanks! I get the gist of it, but I'm still not sure how to solve my problem. I don't have a collision to work with, I only have a collider that is returned by a raycast.

    The code is for two objects, one being dragged and one stationary (neither static), to snap together. While dragging, I do a Physics.OverlapSphere to see if there are any eligible "connectors" in the vicinity. Connectors are children of other objects, and have colliders attached. Physics.OverlapSphere returns an array of colliders that its raycast hits. I go through that array to find a connector (by name). I need to know the position/rotation of that connector, NOT its parent, so I can snap the connector on the piece being dragged to the stationary connector. I can identify the hit collider by its name, but the name of the corresponding transform does not necessarily match. How do I get the transform of the child object the raycast hit?
     
  8. Chris-Trueman

    Chris-Trueman

    Joined:
    Oct 10, 2014
    Posts:
    1,256
    RaycastHit.collider.transform, should be the way to do it.
     
  9. John-B

    John-B

    Joined:
    Nov 14, 2009
    Posts:
    1,251
    That's it, though Unity updated that to hit.GetComponent.<Collider>().transform

    I'd tried doing it directly using hit.GetComponent.<Transform>(),since hit is a collider, but that doesn't work. I hadn't tried using GetComponent to get the collider from a collider.
     
  10. Chris-Trueman

    Chris-Trueman

    Joined:
    Oct 10, 2014
    Posts:
    1,256
    RaycastHit is a struct so if your trying to do GetCompnent on it, it shouldn't work and give a compiler error.

    How exactly are you are you doing the raycast? Can you please post the code you are using?
     
  11. thieum

    thieum

    Joined:
    Apr 8, 2011
    Posts:
    59
    Bumping into this issue with compound / nested colliders with rigidbodies.
    As reported, the raycast fails to detect children colliders and return the parent, in best case.
    I just found that the QueryTrigger parameter solves it all, as if children colliders were treated as "triggers"!
    So, try this method instead:
    Code (CSharp):
    1. if (Physics.Raycast(ray, out hit, 20.0f, mask, QueryTriggerInteraction.Collide)){
    A layermask is now required (use -1 value to include all layers by default), because QueryTrigger is the 5th argument.