Search Unity

  1. Megacity Metro Demo now available. Download now.
    Dismiss Notice
  2. Unity support for visionOS is now available. Learn more in our blog post.
    Dismiss Notice

Mysterious NullReferenceException (I know, I'm sorry!)

Discussion in 'Scripting' started by JudahMantell, May 14, 2020.

  1. JudahMantell

    JudahMantell

    Joined:
    Feb 28, 2017
    Posts:
    476
    Hey! So yeah, I know there's probably a post like this multiple times a week, but I have no idea what's going on with this one. Everything works exactly like it should, but there are still errors.
    Technically I can ignore them, because there aren't any apparent bugs, but I think we all know that's a bad idea.
    I'm using VRTK for my VR interactions/pointers/etc. (pointerRenderer is a component specified earlier in the script.) In the code below, I tried to leave out things that I know have nothing to do with the error, work fine, and would just clutter this post.

    Any help/ideas would be greatly appreciated! Stay safe!

    The error I'm getting is this:
    NullReferenceException: Object reference not set to an instance of an object
    AbilityManager.pointingAtValidObject () (at Assets/Scripts/PlayerScripts/AbilityManager.cs:80)
    AbilityManager.Update () (at Assets/Scripts/PlayerScripts/AbilityManager.cs:96)

    And the code causing the error is this:
    Code (CSharp):
    1.  public bool pointingAtValidObject()
    2.     {
    3.         if (pointer.enabled && pointer.IsStateValid())
    4.         {
    5.            /*Specifically this line*/ if (pointerRenderer.GetDestinationHit() /* returns a raycast*/.collider.gameObject.GetComponent<ForceGrabObject>() && !pointerRenderer.GetDestinationHit().collider.gameObject.GetComponent<VRTK_InteractableObject>().IsGrabbed())
    6.             {
    7.                //CODE STUFF
    8.                 return true;
    9.             }
    10.             else
    11.                 return false;
    12.         }
    13.         else
    14.             return false;
    15.     }
    16.  
    17.     private void Update()
    18.     {
    19.         if (pointingAtValidObject())
    20.         {
    21.                if (controllerEvents.triggerPressed) // IS USING ABILITY
    22.                {
    23.                      triggerDown();
    24.                }
    25.         }
    26.         else
    27.             return;
    28.     }
     
  2. StarManta

    StarManta

    Joined:
    Oct 23, 2006
    Posts:
    8,773
    So that's a very complex line, and its complexity is going to make it difficult to debug. It's very likely that your raycast is not hitting anything, and thus some piece of that long long chain of things will be null. I recommend putting some of the work it's doing into earlier lines.

    (Also, "returns a raycast" doesn't make sense; there's no raycast object/class to return. Did you mean that it returns a RaycastHit? I'll write this on that assumption)
    (Also, if GetDestinationHit() actually does a Raycast, then you're doing an extra completely unnecessary raycast in this code. Raycasts are notoriously performance-draining, relative to a lot of other operations you can do in code, so you should avoid doing the same raycast repeatedly.)
    Code (csharp):
    1. RaycastHit rchit = pointerRenderer.GetDestinationHit();
    2. GameObject hitObject = rchit.collider.gameObject;
    3. bool hasForceGrab = hitObject.GetComponent<ForceGrabObject>()  != null;
    4. var intObj = rchit.collider.gameObject.GetComponent<VRTK_InteractableObject>();
    5. if (hasForceGrab && !intObj.IsGrabbed())
    I've made a point to not make changes that actually solve the problem - this just is separating the existing logic you have into different lines so that the NullReferenceException will point to a smaller number of things happening on one line, making it easier to debug.

    As I said, the smart money is that the raycast simply isn't hitting anything, and therefore rchit.collider would be null, in which case the NRE will now be triggered on line 2 of the above code. It could be that it's hitting an object that doesn't have an attached VRTK_InteractableObject, in which case line 4 would trigger a NRE. And so on.

    Long story short: don't try to squeeze a bunch of stuff onto one line.
     
  3. JudahMantell

    JudahMantell

    Joined:
    Feb 28, 2017
    Posts:
    476
    Wow, I was not expecting such a detailed reply, thank you so much!
    Yes, I meant RaycastHit, not raycast.
    I will try this when I can and update this post accordingly.
    Once again, thanks so much! :)
     
  4. JudahMantell

    JudahMantell

    Joined:
    Feb 28, 2017
    Posts:
    476
    Okay, so I did that, and it narrowed it down to just giving me the error on this line:
    Code (CSharp):
    1. GameObject hitObject = rchit.collider.gameObject;
    I thought it was because I didn't check hitObject != null before moving to the next line, so I did that:
    Code (CSharp):
    1.  
    2. RaycastHit rchit = pointerRenderer.GetDestinationHit();
    3. bool hasHitObject = rchit.collider.gameObject;
    4. if (hasHitObject)
    5. {
    6.      GameObject hitObject = rchit.collider.gameObject;
    7.      bool hasForceGrab = hitObject.GetComponent<ForceGrabObject>() != null;
    8.      var intObject = rchit.collider.gameObject.GetComponent<VRTK_InteractableObject>();
    9.      if (hasForceGrab && !intObject.IsGrabbed())
    10.      {
    11.           . . .
    12.      }
    13.  
    but still got the error on this line:
    Code (CSharp):
    1. bool hasHitObject = rchit.collider.gameObject;
    Any ideas? Thanks so much, and stay safe!
     
  5. TheCakeIsReal

    TheCakeIsReal

    Joined:
    Feb 10, 2018
    Posts:
    7
    Does the GetDestinationHit method handles the case where nothing is hit ? Because when looking at the doc on RaycastHit, if nothing is hit, then collider is null.

    Maybe you can check this condition instead ?

    Code (CSharp):
    1. if(rchit.collider != null)
     
  6. StarManta

    StarManta

    Joined:
    Oct 23, 2006
    Posts:
    8,773
    If you think about it: If what you would think of as "rchit.collider.gameObject" is null, then how could "rchit.collider" be anything but null?
     
  7. JudahMantell

    JudahMantell

    Joined:
    Feb 28, 2017
    Posts:
    476
    I'm sorry, I don't quite know what you mean.

    I changed it to
    Code (CSharp):
    1. if(rchit.collider != null)
    and that fixed it and got rid of the error, as per @TheCakeIsReal 's suggestion.

    Thank's so much!
     
  8. StarManta

    StarManta

    Joined:
    Oct 23, 2006
    Posts:
    8,773
    Let me rephrase: A Collider is a component that must always be attached to a GameObject - it can't exist on its own. So if the Collider exists, then collider.gameObject must exist.
    If the GameObject doesn't exist, then the Collider doesn't exist. So collider.gameObject will give you a NullReferenceException anytime it could possibly return null. So hit.collider is the thing you must null check.
     
  9. JudahMantell

    JudahMantell

    Joined:
    Feb 28, 2017
    Posts:
    476
    Ah, got it. I think that's what confused me to begin with. Thanks!