Dealing with raycast corner normals

When you do a raycast in 2D or 3D you sometimes get normals that are very unlike the shape you made. These appear the moment your raycast hits the edge of a collider and it gives an interpolated normal between the 2 surfaces that this edge is connected to.

  • How can I detect when the raycast I’m doing is returning a corner normal and not a surface normal?
  • Is there a way to raycast and hav only surface normals that are returned?

EDIT: Actually in 3D, the simple Raycast doesn’t have this problem. It’s only when using SphereCast or the likes that the normal gives back corner normals.

5176019--513707--upload_2019-11-14_19-57-23.png

I hate to resurrect this, but I’ve actually been struggling with this for weeks and just found a workaround (not quite a solution because I still don’t know why this happens) and hope that this helps someone else.

When I hit something in a BoxCast, I check the surface normal. If the dot product between my movement’s direction and the surface normal is close to -1, I can assume I’m either walking into a flat wall, or, a corner. In my project, I separate horizontal movement from vertical, so I only check this when moving in my “side pass” Either way, I perform a single Raycast (Collider.Raycast, so I’m only checking against that one collider). If I hit the obstacle again, I update my previous collision’s surface normal with the “true” surface normal, because Raycast doesn’t seem to have this issue, only box/capsule casts.

This may not catch every possible case, but so far so good for me! Hope this helps.

// In some cases, walking into a corner can result in a normal that is the inverse of the desired movement
if (pass == EPass.SIDE)
{
   float threshold = -0.98f;
   if (Vector3.Dot(direction, closestHit.normal) <= threshold)
   {
       // Raycast from the volume's center, into the collision point we know we hit before
       Vector3 rayOrigin = m_Collider.transform.position + m_Collider.center;
       Vector3 rayDirection = (closestHit.point - rayOrigin).normalized;
       float rayDistance = Vector3.Distance(closestHit.point, rayOrigin) + m_Params.ContactOffset;
       Ray ray = new Ray(rayOrigin, rayDirection);

       // If we were able to hit the collider, grab the "true" surface normal so the volume can project collisions correctly
       if (closestHit.collider.Raycast(ray, out RaycastHit hitInfo, Vector3.Distance(closestHit.point, rayOrigin) + Physics.defaultContactOffset))
       {
           closestHit.normal = hitInfo.normal;
       }
   }
}

6785876--785828--Before.gif 6785876--785831--After.gif