Search Unity

Bug MissingReferenceException in graphic raycaster.

Discussion in 'UGUI & TextMesh Pro' started by WNP78, Feb 6, 2021.

  1. WNP78

    WNP78

    Joined:
    Oct 8, 2016
    Posts:
    5
    Bug observed in Unity 2020.2.1f1

    The end result is that if a Graphic is created in a certain way and then destroyed, it will leave a reference to itself behind after death in the graphic raycaster, throwing a MissingReferenceException every time the raycaster runs, rendering the UI unusable until the game is restarted.

    This is a bug caused by the Graphic.raycastTarget setter not checking to see if the object is active before registering itself with GraphicRegistry.RegisterRaycastGraphicForCanvas. This is paired with GraphicRegistry.UnregisterGraphicForCanvas assuming that if there is no list for graphics, then there will be no list for raycast graphics, so the raycast graphic is not removed when the object is destroyed.

    To reproduce, create an empty canvas in a blank scene and attach this script:
    Code (CSharp):
    1. using UnityEngine;
    2. using UnityEngine.UI;
    3.  
    4. /// <summary>
    5. /// Bug with Graphic.set_raycastTarget and GraphicRegistry.UnregisterGraphicForCanvas observed in Unity 2020.2.1f1
    6. /// To reproduce:
    7. ///  - Create a canvas in an empty scene
    8. ///  - Attach this script
    9. ///  - Enter play mode
    10. ///  - Interact with the Game window with your mouse and observe logged errors
    11. ///  - Other UI elements using graphic raycasting will also no longer work.
    12. /// </summary>
    13. [RequireComponent(typeof(RectTransform))]
    14. [RequireComponent(typeof(Canvas))]
    15. [RequireComponent(typeof(CanvasScaler))]
    16. [RequireComponent(typeof(GraphicRaycaster))]
    17. public class UIBugTestCase : MonoBehaviour
    18. {
    19.     private void Start()
    20.     {
    21.         // Set up the object - an inactive graphic on a canvas. (In this case an Image)
    22.         var obj = new GameObject("Image");
    23.         obj.AddComponent<RectTransform>();
    24.         obj.AddComponent<CanvasRenderer>();
    25.         var img = obj.AddComponent<Image>();
    26.         // raycastTarget is set to false initially so it can be changed later to true.
    27.         img.raycastTarget = false;
    28.         obj.transform.SetParent(this.transform);
    29.         obj.SetActive(false);
    30.  
    31.         // Now the object is inactive, we set raycastTarget to true, and it erroneously registers itself with
    32.         // GraphicRegistry.RegisterRaycastGraphicForCanvas (even though it is inactive)
    33.         img.raycastTarget = true;
    34.  
    35.         // Destroy the object.
    36.         Destroy(obj);
    37.         /*
    38.          * When it is destroyed (at any point in the future provided it isn't activated), it still calls
    39.          * GraphicRegistry.UnregisterGraphicForCanvas which usually calls GraphicRegistry.UnregisterRaycastGraphicForCanvas.
    40.          * However as it is inactive, it's not in the canvas graphic list, only the canvas raycast graphic list.
    41.          * UnregisterGraphicForCanvas assumes that the raycast graphics are a subset of the normal graphics list (not
    42.          * true because of the raycastTarget = true bug). This causes it to not call UnregisterRaycastGraphicForCanvas,
    43.          * leaving the destroyed object in the raycast graphic list, which throws errors in the graphic raycaster.
    44.          *
    45.          * At this point, if the bug hasn't been fixed then interacting with the Game window in play mode with the mouse should throw:
    46.          * MissingReferenceException: The object of type 'CanvasRenderer' has been destroyed but you are still trying to access it.
    47.          */
    48.  
    49.         /* Suggested fixes:
    50.          *
    51.          * Graphic.raycastTarget: setter should not call GraphicRegistry functions if IsActive() is false.
    52.          *
    53.          * GraphicRegistry.UnregisterGraphicForCanvas: call UnregisterRaycastGraphicForCanvas regardless of whether a list for
    54.          * the current canvas is found - better defensive design and prevents future issues like this.
    55.          */
    56.     }
    57. }
     
  2. wechat_os_Qy0xg-Vl6qvCgq6O27ipr_zGY

    wechat_os_Qy0xg-Vl6qvCgq6O27ipr_zGY

    Joined:
    Dec 2, 2019
    Posts:
    1
    Thanks so much for this suggestion,I solved my problem by this .here is my step and code
    Wrong step :
    - your image is deactive,and this image.raycasttarget = false
    - set image.raycastTarget = true when image is deactive
    - destroy this image or destroy the parent of this image
    Wrong code(lua code):
    if img then
    img.raycastTarget = true
    end
    ----------------------------------
    Right code (check image is active ,if true then you can set):
    if img and obj.gameObject.activeInHierarchy == true then
    img.raycastTarget = true
    end