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

Unity UI PhysicsRaycaster System.NullReferenceException Bug

Discussion in 'UGUI & TextMesh Pro' started by WangShibo17, Jun 26, 2018.

  1. WangShibo17

    WangShibo17

    Joined:
    Oct 28, 2017
    Posts:
    13
    upload_2018-6-26_16-49-45.png

    I add a PhysicsRaycaster to main camera. and sometime this exception happened.
    Not sure this is a UI bug or a Physics bug
     
  2. FernandoHC

    FernandoHC

    Joined:
    Feb 6, 2018
    Posts:
    333
    If the issue happens only sometimes I would check if the collided gameobject still exists and is not destroyed(being destroyed).
     
  3. WangShibo17

    WangShibo17

    Joined:
    Oct 28, 2017
    Posts:
    13
    Code (CSharp):
    1.         public override void Raycast(PointerEventData eventData, List<RaycastResult> resultAppendList)
    2.         {
    3.             // Cull ray casts that are outside of the view rect. (case 636595)
    4.             if (eventCamera == null || !eventCamera.pixelRect.Contains(eventData.position))
    5.                 return;
    6.  
    7.             Ray ray;
    8.             float distanceToClipPlane;
    9.             ComputeRayAndDistance(eventData, out ray, out distanceToClipPlane);
    10.  
    11.             int hitCount = 0;
    12.  
    13.             if (m_MaxRayIntersections == 0)
    14.             {
    15.                 if (ReflectionMethodsCache.Singleton.raycast3DAll == null)
    16.                     return;
    17.  
    18.                 m_Hits = ReflectionMethodsCache.Singleton.raycast3DAll(ray, distanceToClipPlane, finalEventMask);
    19.                 hitCount = m_Hits.Length;
    20.             }
    21.             else
    22.             {
    23.                 if (ReflectionMethodsCache.Singleton.getRaycastNonAlloc == null)
    24.                     return;
    25.  
    26.                 if (m_LastMaxRayIntersections != m_MaxRayIntersections)
    27.                 {
    28.                     m_Hits = new RaycastHit[m_MaxRayIntersections];
    29.                     m_LastMaxRayIntersections = m_MaxRayIntersections;
    30.                 }
    31.  
    32.                 hitCount = ReflectionMethodsCache.Singleton.getRaycastNonAlloc(ray, m_Hits, distanceToClipPlane, finalEventMask);
    33.             }
    34.  
    35.             if (hitCount > 1)
    36.                 System.Array.Sort(m_Hits, (r1, r2) => r1.distance.CompareTo(r2.distance));
    37.  
    38.             if (hitCount != 0)
    39.             {
    40.                 for (int b = 0, bmax = hitCount; b < bmax; ++b)
    41.                 {
    42.                     var result = new RaycastResult
    43.                     {
    44.                         gameObject = m_Hits[b].collider.gameObject,
    45.                         module = this,
    46.                         distance = m_Hits[b].distance,
    47.                         worldPosition = m_Hits[b].point,
    48.                         worldNormal = m_Hits[b].normal,
    49.                         screenPosition = eventData.position,
    50.                         index = resultAppendList.Count,
    51.                         sortingLayer = 0,
    52.                         sortingOrder = 0
    53.                     };
    54.                     resultAppendList.Add(result);
    55.                 }
    56.             }
    57.         }
    I think if the collided gameobject has been destroyed, hitCount should not be 2.
    or I must check each RaycastResult after called Physics.RaycastNonAlloc ? that is weird I think.
     
  4. daniel_martins

    daniel_martins

    Joined:
    Jan 3, 2018
    Posts:
    5
    I've also encoutered this issue and I think I found its cause and one solution.
    This issue only occurs when you use a value of 'Max Ray Intersection' above zero, and even if you don't get a NullReferenceException, your raycast results will potentially be invalid.
    Here is the code for the Raycast method (from the file 'PhysicsRaycaster.cs') with some comments explaining the cause of the error and how I've managed to fix it.
    The comments I inserted start with 'NOTE:'.

    Code (CSharp):
    1. public override void Raycast(PointerEventData eventData, List<RaycastResult> resultAppendList)
    2. {
    3.     // Cull ray casts that are outside of the view rect. (case 636595)
    4.     if (eventCamera == null || !eventCamera.pixelRect.Contains(eventData.position))
    5.         return;
    6.     Ray ray;
    7.     float distanceToClipPlane;
    8.     ComputeRayAndDistance(eventData, out ray, out distanceToClipPlane);
    9.     int hitCount = 0;
    10.     if (m_MaxRayIntersections == 0)
    11.     {
    12.         if (ReflectionMethodsCache.Singleton.raycast3DAll == null)
    13.             return;
    14.         m_Hits = ReflectionMethodsCache.Singleton.raycast3DAll(ray, distanceToClipPlane, finalEventMask);
    15.         hitCount = m_Hits.Length;
    16.     }
    17.     // NOTE: When using 'Max Ray Intersection' with value above 0, the code follows this else branch.
    18.     else
    19.     {
    20.         if (ReflectionMethodsCache.Singleton.getRaycastNonAlloc == null)
    21.             return;
    22.  
    23.         if (m_LastMaxRayIntersections != m_MaxRayIntersections)
    24.         {
    25.             m_Hits = new RaycastHit[m_MaxRayIntersections];
    26.             m_LastMaxRayIntersections = m_MaxRayIntersections;
    27.         }
    28.  
    29.         // NOTE: Here is the fix for the problem.
    30.         // Reset the array so that the raycast hits have a distance with the maximum value.
    31.         // Once the array is sorted, the valid hits will be the first ones on the array and the ones that get returned.
    32.         // If the array does not get sorted (only one valid hit), then it is already on the first position of the array.
    33.         // -- start of fix --
    34.         for(int i = 0; i < m_MaxRayIntersections; i++)
    35.             m_Hits[i].distance = float.MaxValue;
    36.         // -- end of fix --
    37.  
    38.         // NOTE: First part of the problem.
    39.         // The getRaycastNonAlloc returns the number of hits found (goes into 'hitCount').
    40.         // The hits information is stored in the 'm_Hits', which is an array of structures.
    41.         // Because the method getRaycastNonAlloc does not reset the array information (it simply inserts the hits found starting at index 0), we may end up in two situations:
    42.         //  1) Initially, while the array is not populated with valid hits, we will get NullReferenceExceptions.
    43.         //  2) As more hits appear, the entire array will be populated and then the NullReferenceExceptions stop, but the invalid data begins.
    44.         hitCount = ReflectionMethodsCache.Singleton.getRaycastNonAlloc(ray, m_Hits, distanceToClipPlane, finalEventMask);
    45.     }
    46.  
    47.     // NOTE:
    48.     // Here lies the second part of the problem.
    49.     // Case 1)
    50.     //  Initially, the array will have hits with the following field values:
    51.     //    distance = 0
    52.     //    collider = null
    53.     //  Because we sort the hits by distance, we will get the null colliders on the front of the array and we will try to access them (search tag #null-collider).
    54.     // Case 2)
    55.     //  After some iterations, the m_Hits will be filled with non-null-colliders, but since the array is not properly reset, some of that data may be from previous frames.
    56.     //  Therefore, on the long run, you will end up receiving old hits that occured closer to the camera.
    57.     // Final note:
    58.     //  Note that, these errors will only occur when you have several hits (because the array only gets sorted by distance when hitCount is more than one).
    59.     if (hitCount > 1)
    60.         System.Array.Sort(m_Hits, (r1, r2) => r1.distance.CompareTo(r2.distance));
    61.  
    62.     if (hitCount != 0)
    63.     {
    64.         for (int b = 0, bmax = hitCount; b < bmax; ++b)
    65.         {
    66.             var result = new RaycastResult
    67.             {
    68.                 gameObject = m_Hits[b].collider.gameObject, // #null-collider
    69.                 module = this,
    70.                 distance = m_Hits[b].distance,
    71.                 worldPosition = m_Hits[b].point,
    72.                 worldNormal = m_Hits[b].normal,
    73.                 screenPosition = eventData.position,
    74.                 index = resultAppendList.Count,
    75.                 sortingLayer = 0,
    76.                 sortingOrder = 0
    77.             };
    78.             resultAppendList.Add(result);
    79.         }
    80.     }
    81. }
    82.  
     
    _creatio_ likes this.
  5. _creatio_

    _creatio_

    Joined:
    Mar 21, 2013
    Posts:
    43
    I'm having this issue too. Got stuck with Unity 2018.3.13f1, so can't confirm if it exists on newer versions of Unity.

    The issue reproduces on the overlapping colliders.
    Setting maxRayIntersections to 0 fixes it in my case as it is relatively cheap for me, but this workaround should be used with care (about the performance).

    P.S. Thanks a ton @daniel_martins
     
    Last edited: May 28, 2019