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. We have updated the language to the Editor Terms based on feedback from our employees and community. Learn more.
    Dismiss Notice

Dealing with raycast corner normals

Discussion in 'Physics' started by FeastSC2, Nov 14, 2019.

  1. FeastSC2

    FeastSC2

    Joined:
    Sep 30, 2016
    Posts:
    974
    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.

    upload_2019-11-14_19-57-23.png
     
    Last edited: Nov 15, 2019
  2. SmittyA

    SmittyA

    Joined:
    May 21, 2013
    Posts:
    5
    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.

    Code (CSharp):
    1.  
    2. // In some cases, walking into a corner can result in a normal that is the inverse of the desired movement
    3. if (pass == EPass.SIDE)
    4. {
    5.    float threshold = -0.98f;
    6.    if (Vector3.Dot(direction, closestHit.normal) <= threshold)
    7.    {
    8.        // Raycast from the volume's center, into the collision point we know we hit before
    9.        Vector3 rayOrigin = m_Collider.transform.position + m_Collider.center;
    10.        Vector3 rayDirection = (closestHit.point - rayOrigin).normalized;
    11.        float rayDistance = Vector3.Distance(closestHit.point, rayOrigin) + m_Params.ContactOffset;
    12.        Ray ray = new Ray(rayOrigin, rayDirection);
    13.  
    14.        // If we were able to hit the collider, grab the "true" surface normal so the volume can project collisions correctly
    15.        if (closestHit.collider.Raycast(ray, out RaycastHit hitInfo, Vector3.Distance(closestHit.point, rayOrigin) + Physics.defaultContactOffset))
    16.        {
    17.            closestHit.normal = hitInfo.normal;
    18.        }
    19.    }
    20. }
    Before.gif After.gif