Search Unity

Raycast not finding object's collider

Discussion in 'Physics' started by Ninjaboi, May 2, 2015.

  1. Ninjaboi

    Ninjaboi

    Joined:
    May 2, 2015
    Posts:
    14
    This has been resolved, read my third post here if you're looking for solutions for the same problem or similar.

    TL;DR: ( Feel free to read the more detailed/winded description if this doesn't cut it ). I'm currently using Unity 5.0.0f4 on a Windows 7 x64 machine. I have two cubes ( ground, table ) and one capsule ( player ) in my scene. I have it to where the player can jump by pressed down space, a raycast is performed at the bottom center of the capsule collider ( this is min-center in code ) that shoots downward on the global Y axis for 0.1 units. If the raycast hits a collider on one of my two cubes, the player's velocity is set to 0 and an AddForce() is applied to make him jump.

    Normally this works, but for some reason sometimes the raycast doesn't hit the colliders even when my Debug.DrawRay() shows it to be going right into them. I've confirmed this with checking the hit.collider.name of what the raycast hits/doesn't hit when performing a jump. When this issue pops up, the player capsule will have a raycast below it going through the cube's collider but will not detect it and will therefore return a null reference on the name.

    Note: The capsule's rigidbody has rotation frozen completely, and all colliders in the scene are perfectly fitting around the meshes for their objects. The raycast is always correctly being performed from what I can observe. No objects in the scene are moving at high velocities or with other "extreme" variables being applied to them that I know of.

    I'm currently using Unity 5.0.0f4 on a Windows 7 x64 machine. My scene currently has two cubes and one capsule. One cube is the ground and has a scale of 0.1 on the Y axis, the other is the typical 1x1x1 primitive default that I'm using as a table/object to jump on. The capsule is the player. I currently have it to when the player presses the space key down, a short ( 0.1 unit ) raycast is performed at the bottom center or min-center of the capsule's collider and shoots downward on the global Y axis. This raycast is to check for any collider to be hit, but so far there's only two objects for it to hit in the scene which is my ground and table. When a collider has been found, the player gets to perform a jump and so the capsule's Y velocity is set to 0 and an AddForce() is used for the jump. Gravity is active for the capsule.

    The capsule's rigidbody rotation is frozen on all axis and is only ever changed when the player moves the mouse on the X axis, of which a transform.Rotate is performed on the Y axis rotation for looking around. The capsule's collider is perfectly fitted around the mesh of the object, and I can confirm that the raycast is being performed at the correct origin, direction, and distance. My objects are completely flat and have colliders that perfectly match their mesh, and using Debug.DrawRay I've been able to confirm without a doubt that the ray is in fact entering the colliders of both my ground and table.

    My problem is, although it usually can tell when there is a collider in its path, the raycast sometimes won't detect the colliders in my scene even when no apparent change has taken place. When the player fails to jump I've noticed the Physics.Raycast() returns a null reference for an object name of the colliding object the player will jump on. This means that when I am in-game and press space to jump, it should tell me what I jumped on in the debug log and sure enough my capsule will have his velocity set to 0 and the AddForce() is applied and he does his jump as expected. When my character fails to jump even though he is in fact on top of the ground or table, the debug log shows a null reference for the name and the velocity is NOT set to 0 and the AddForce() is NOT applied.

    I'm trying to be descriptive as I can here since I'm just not seeing how this could be happening. The physics engine isn't manipulating the rotation of my capsule, my objects in the scene that aren't the player are static, my capsule isn't going at a high velocity, everything that can be jumped on is flat, and I can confirm that the raycast calculated for jumping is being done correctly and is visually hitting colliders. This happens regardless of what Y rotation the capsule has, regardless of the position the capsule has in the scene, and regardless of the previous or current velocity of the capsule. Just for no obvious reason my raycast can't seem to find the collider it is clearly passing through, even though not even a few moments prior it found it no problem.

    I will say that the only thing that is consistent here is that whenever the raycast starts having trouble finding colliders, if I don't look around with the mouse or move in any other way outside of attempting to jump, it will always fail. On the flip side, if it is having no trouble finding the colliders and I don't look around or move in any way outside of jumping, it will always succeed. So this is where I assume it has something to do with either my capsule's orientation in the scene at that moment in the game, or the way I am applying force to the capsule to make him move in the scene. So that's why I posted in the physics section here.

    Here's the jumping code:
    Code (CSharp):
    1. // Jumping.
    2. if(Input.GetKeyDown(KeyCode.Space))
    3. {
    4.     // Check if on a ground/surface to jump on.
    5.     groundCheck = new Ray(new Vector3(GetComponent<Collider>().bounds.min.x + GetComponent<Collider>().bounds.extents.x,
    6.                                       GetComponent<Collider>().bounds.min.y,
    7.                                       GetComponent<Collider>().bounds.min.z + GetComponent<Collider>().bounds.extents.z),
    8.                           Vector3.down);
    9.     if(Physics.Raycast (groundCheck, out hit, 0.1f))
    10.     {
    11.         rb.velocity = new Vector3(rb.velocity.x, 0, rb.velocity.z);
    12.         rb.AddForce (transform.up * jumpPower );
    13.     }
    14.     Debug.Log (hit.collider.name);
    15. }
    Here's the movement code:
    Code (CSharp):
    1. // Movement.
    2. if(Input.GetKey(KeyCode.W))
    3. {
    4.     if(Input.GetKey (KeyCode.LeftShift))
    5.     {
    6.         rb.AddForce (transform.forward * Time.deltaTime * speed * 2f);
    7.     }
    8.     else
    9.     {
    10.         rb.AddForce (transform.forward * Time.deltaTime * speed );
    11.     }
    12. }
    13. if(Input.GetKey(KeyCode.S))
    14. {
    15.     if(Input.GetKey (KeyCode.LeftShift))
    16.     {
    17.         rb.AddForce (-transform.forward * Time.deltaTime * speed * 2f);
    18.     }
    19.     else
    20.     {
    21.         rb.AddForce (-transform.forward * Time.deltaTime * speed );
    22.     }
    23. }
    24. if(Input.GetKey(KeyCode.A))
    25. {
    26.     if(Input.GetKey (KeyCode.LeftShift))
    27.     {
    28.         rb.AddForce (-transform.right * Time.deltaTime * speed * 2f);
    29.     }
    30.     else
    31.     {
    32.         rb.AddForce (-transform.right * Time.deltaTime * speed );
    33.     }
    34. }
    35. if(Input.GetKey(KeyCode.D))
    36. {
    37.     if(Input.GetKey (KeyCode.LeftShift))
    38.     {
    39.         rb.AddForce (transform.right * Time.deltaTime * speed * 2f);
    40.     }
    41.     else
    42.     {
    43.         rb.AddForce (transform.right * Time.deltaTime * speed );
    44.     }
    45. }
    Here's the looking and movement/looking clamp code:
    Code (CSharp):
    1. // Restricting movement, jump, and fall velocity.
    2. Vector3 clampedVelocity = rb.velocity;
    3. clampedVelocity.x = Mathf.Clamp (clampedVelocity.x, -5.0f, 5.0f);
    4. clampedVelocity.z = Mathf.Clamp (clampedVelocity.z, -5.0f, 5.0f);
    5. clampedVelocity.y = Mathf.Clamp (clampedVelocity.y, -15.0f, 15.0f);
    6. rb.velocity = clampedVelocity;
    7.  
    8. // Looking and look restriction.
    9. transform.Rotate (0.0f, Input.GetAxis ("Mouse X") * 4.0f, 0.0f);
    10. cameraRotationY -= Input.GetAxis ("Mouse Y") * 4.0f;
    11. cameraRotationY = Mathf.Clamp (cameraRotationY, -90.0f, 90.0f);
    12. Camera.main.transform.localRotation = Quaternion.Euler (cameraRotationY, 0.0f, 0.0f);
    13. GetComponentInChildren<Light>().transform.localRotation = Quaternion.Euler (cameraRotationY, 0.0f, 0.0f);
    Finally, the debug raycast code:
    Code (CSharp):
    1. Debug.DrawRay (groundCheck.origin, groundCheck.direction * 0.1f);
    I was originally only going to post the jumping and raycast debug code, but given that I'm leaning towards this being an issue with how I'm moving/rotating my capsule I decided to add the appropriate code as well. I'm hoping this is the case and not me just stumbling on a bug in the Unity build, but if I did encounter a (known/unknown) bug I was hoping someone could maybe recommend/suggest alternatives or workarounds for me? If any additional information is needed I will provide it.
     
    Last edited: May 3, 2015
  2. Ninjaboi

    Ninjaboi

    Joined:
    May 2, 2015
    Posts:
    14
    Update: I've now tried making the raycast distance larger ( from 0.1 units to 0.5 units ) to no avail. I've also played around with moving my raycasting from FixedUpdate() to Update() and nothing changed. Although I find it hard to imagine, is there a failure-rate with the current raycast method? I'm wondering because someone posted not too long ago in a different forum ( http://forum.kerbalspaceprogram.com...hysics-RayCast-sometimes-passes-through-parts ) that makes me think there some inaccuracy going on. Although I've been reading that raycasting is only ever inaccurate with complex mesh colliders and not primitives like what I'm using, it was food for thought.

    On another note, I created a test project and had a box for the player and a box for the ground and was able to get it 100% accurate with the jumping with what I believe is the same way I did it in my current project. However I dramatically increased the fixed timestep in my test project ( from 0.02 to 0.005 ) so the accuracy of physics in the game probably benefited from this. I haven't seen anyone else say they had to change the defaults for what I thought was a rather straightforward process, but if I need to do it in my main project for accuracy then I will.
     
  3. Ninjaboi

    Ninjaboi

    Joined:
    May 2, 2015
    Posts:
    14
    Final Update: FIXED IT!

    So it turns out it wasn't my movement logic, nor my looking/rotation logic. The problem was that when you perform a raycast in Unity and the origin point of the ray starts off inside a collider, it ignores it/doesn't collide. This was strange at first for me when I was clearly creating my ray's origin at the exact bottom center of my object's collider, and my object's Y position always ended up going to an exact value when standing on my boxes. The thing is though is that my player object uses a rigidbody and uses gravity, plus the position of the object isn't an exact number but rather a floating point with some very slight inaccuracy/variability to be expected when having the physics engine take hold of it.

    So when my raycast was working, the player collider that the raycast uses for origin positioning was right above the ground/cube surface I wanted to jump off of. When my raycast was not working, the player collider was right below the initial face of the ground/cube surface. This meant it started off inside the object I wanted it to check collision with, and since Unity's raycasts ignore colliders they originate in this meant it would pass through that ground object and find nothing else to collide with, hence no jump.

    My test project I mentioned earlier most likely didn't have this issue come up because it uses a cube with a box collider for the player while the player in my main game uses a capsule collider, and that might have caused the slight inaccuracy in the position of the object due to the physics manipulation being acted upon it.


    So for future readers, here was the identifiable problem, the source of that problem, and the solution:

    Problem: The raycast used to check for colliders below my player for jumping was sometimes not hitting, even when I saw no noticeable change to my player's Y position and the Debug.DrawRay() placed the ray where it visually seemed correct and the line the ray had went inside the object it should be colliding with.

    Source: The player object uses a capsule collider and also has a rigidbody. The player object used gravity and was acted upon by other physics, and the ray's origin point started at the exact point at the bottom-center of the player's capsule collider. Since the capsule collider isn't flat on its bottom, and that physics were taking place on the player, the floating point for the collider/player position would sometimes have extremely slight variability that caused the ray's origin to be inside the object I wanted it to calculate collision on. This caused the ray to ignore/not calculate the collision with that collider. It's an intended behavior for Unity's raycasting as noted in the Physics.Raycast page.

    "Note: This function will return false if you cast a ray from inside a sphere to the outside; this is an intended behavior."

    Solution: There are a few ways to resolve this, and I will say the one I went with first.
    1. When calculating the ray's origin at the bottom-center of the player's collider, have it then tack on a very small additional value to bring it inside the player's collider and not just right outside of it. This tacked on value should also be added to the ray's distance/length so that the desired checking length is still the same. Doing this will keep your ray's check length the same, and when the player is acted on by physics his collider will ensure the ray's origin is safe ( like a turtle in his shell! ). For my specific case, my desired check length was 0.1 units, so I had the ray length 0.2 units and brought the ray inside the player's collider by 0.1 units ( total length - length inside = desired length ).
    2. Reverse the direction of your ray. http://answers.unity3d.com/questions/129715/collision-detection-if-raycast-source-is-inside-a.html . ( That's also the link that made me realize the issue at hand ). This involves having your ray start at its end point by setting its origin from where you originally intended to start it plus the distance it would have traveled in a raycast. With your ray's new reversed origin, you have it's direction reversed as well so that when a raycast is performed it shoots towards the starting origin point and ends up at it. The reason I didn't choose this method was because I can't see it resolving my current issue as my ray distance is quite small and reversing the origin would still leave it inside the objects I wish for it to collide with and thus ignore it the same way. However for longer rays or for other uses this might be the better way to go about this ( like for example your player object doesn't use a rigidbody or doesn't have a collider to protect it ).
    3. Just before performing the raycast, get the ray's origin and have it's position checked by all colliders the ray might end up hitting if it happened to start inside it. Do this by getting your ray.origin and grab each collider's bounds and dimensions and seeing if the ray's origin point falls inside a collider's bounds. While this would in fact solve the problem for me since it would basically rewrite the rule Unity has set in place for ignoring the origin's position check to se if it was inside a collider, I believe I found the reasoning behind the intended behavior: performance. Raycasting can get expensive as more colliders are added to the layer(s) it checks, the more complexity those colliders have, and how much distance/length the raycast has. Having it perform a pre-check to see if it starts inside a collider in the layers it will raycast in certainly doesn't seem good. Though this could be quite the opposite as if you check to see if the ray.origin is inside a collider before performing a raycast and it returns true, you can skip the raycast in many cases and can save on performance ( especially if the raycast had a ton of distance to cover ).

    So there you have it. This wasn't a bug in Unity, it was just me not knowing that Unity did not check if ray.origin's were inside a collider before performing or while performing raycasts. Hope this helps someone in the future who stumbles across any problem with rays or raycasting in their game. Again I used the first solution in that list as it was the simplest one out of the three that also fit well in the logic of my game.

    If you are worried about adding more length to your raycast for the first solution then don't worry: it doesn't have to be a x2 multiplier like what I did. I only did that because my raycast length is tiny and I could probably make that extra length smaller if I wanted to. The purpose is to get your ray.origin inside the protection of the owning player/object's collider so that physics won't let it ever go inside another collider that it would be otherwise checking. The performance impact is very tiny, so much so that I can't really even peg a difference in my case.
     
  4. sampenguin

    sampenguin

    Joined:
    Feb 1, 2011
    Posts:
    64
    Thanks for this post and updates, I was having a similar problem and this helped me to find a solution. :)
     
  5. stalyan77

    stalyan77

    Joined:
    Nov 24, 2013
    Posts:
    14
    thanks for your super detailed and clear explanations =)
     
  6. Polff

    Polff

    Joined:
    May 18, 2017
    Posts:
    30
    Don't know if that was possible in 2015 Unity, but you can just put the player and it's children on the ignore Raycast Layer ... fixes the problem as well. :)
     
  7. Dr9

    Dr9

    Joined:
    Jun 27, 2013
    Posts:
    3
    To anyone in the future: hitcollider needs a meshcollider like it says in the document. But what wasn't obvious (to me anyways) is that apparently SphereCollider and BoxCollider are not MeshColliders… I spent an hour trying to figure out WTF was wrong in a sample scene. Deleted the "SphereCollider" component, added generic MeshCollider (with type Sphere) and it works.
     
    McPeppergames and PeterLauris like this.