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

Newb. I'm having trouble understanding 'Null Reference Exception'.

Discussion in 'Scripting' started by demiankz, Apr 29, 2014.

  1. demiankz

    demiankz

    Joined:
    Mar 20, 2014
    Posts:
    10
    I'm getting a null reference exception when I run a script that calls a second script which then drops a bunch of prefab cubes into the scene. The individual functions work fine if they're both in the "GameBrain" script. I only get the Null Reference when I split them up and call one script from the other.

    My apologies for the poorly formatted code.

    This script is attached to my 'player' game object:

    public class GameBrain : MonoBehaviour
    {
    public CubeDropper other;

    void Start ()
    {
    other.DropCubes();
    }
    }​


    This script is unassigned:

    public class CubeDropper : MonoBehaviour
    {
    public void DropCubes()
    {
    GameObject prefab = Resources.Load ("MultiCube") as GameObject;
    for (int i=0;i<100;i++)
    {
    GameObject go=Instantiate(prefab) as GameObject;
    go.transform.position=new Vector3(0,i*5,0);
    }
    }
    }​

    I'm looking to understand concepts as much as technique here, so if anyone cares to pass along a little knowledge that a newb programmer might be able to parse, I'd appreciate it.

    I know this is a simple thing (and a question that comes up regularly), I just can't seem to figure it out.
     
  2. Suddoha

    Suddoha

    Joined:
    Nov 9, 2013
    Posts:
    2,824
    For the code you can use codetags, i.e. [c.o.d.e] your code here [/c.o.d.e] without the dots

    The null reference exception is caused when you try to use your variable 'other' without having an object assigned to it.
    It doesn't have a object it references to, that means it references null. It basically says "i don't have an object that i'm pointing too".

    You can fix this with:

    Code (csharp):
    1.  
    2. public CubeDropper other;
    3.  
    4. void Start()  // or Awake()
    5. {
    6.      other = new CubeDropper();  // with the new keyword, you create a new 'real' object,
    7. //declaring a variable like  public CubeDropper other only created a reference which references to nothing but 'null'.
    8.      other.DropCubes(); // now this is possible
    9. }
    10.  
     
    Last edited: Apr 29, 2014
  3. KelsoMRK

    KelsoMRK

    Joined:
    Jul 18, 2010
    Posts:
    5,539
    This code won't work because CubeDropper inherits from MonoBehaviour.
     
  4. Suddoha

    Suddoha

    Joined:
    Nov 9, 2013
    Posts:
    2,824
    Ye, i didn't check that one you're totally right but he can just remove it. That shouldn't be the problem.
     
  5. demiankz

    demiankz

    Joined:
    Mar 20, 2014
    Posts:
    10
    Well, Suddoha's fix did work, but yes, Unity threw a warning. Unsure how to get things to perform without the warning. Anyway, thanks.

    So, if I understand what's going on here,

    Code (csharp):
    1.  other =  new CubeDropper();
    instantiates a new instance of the CubeDropper script? Is that what didn't exist in the "broken" code? The CubeDropper script itself? Why would I need to make a new instance of a script that already exists? And why isn't this missing instantiation concept made more apparent in the discussions surrounding calling functions between scripts? Especially in the Unity docs. Did I completely miss this?

    It is a question that appears over and over and over again and it seems like if someone were to say, when you declare a variable, you need to also instantiate it before you can use it, then this null reference exception issue might appear less frequently. Especially for newbs. Am I totally off base here?
     
  6. Suddoha

    Suddoha

    Joined:
    Nov 9, 2013
    Posts:
    2,824
    As KelsoMRK mentioned, this doesn't work if the script inherits from MonoBehaviour. If it doesn't this should work, at least it did for me unless i forgot to copy something. :S

    If you want to assign both scripts to an gameObject, you have to keep the inheritance and set your variable to the active script instance on the object.

    Code (csharp):
    1.  
    2.  other = gameObject.GetComponent<CubeDropper>();
    3.  
    Now this should work, if not i should go to bed. lol

    And no, you're not totally off base but a variable of a reference type always needs to have an object assigned if you want to work with it. The first post with the new was my bad, of course the script instance does exist on your gameObject and you can assign it with the line above.
     
    Last edited: Apr 29, 2014
  7. demiankz

    demiankz

    Joined:
    Mar 20, 2014
    Posts:
    10
    Code (csharp):
    1.  other = gameObject.GetComponent<CubeDropper>();
    Thank you for taking the time to explain and help. That new code doesn't seem to work. Throwing another null reference exception.
     
  8. bigmisterb

    bigmisterb

    Joined:
    Nov 6, 2010
    Posts:
    4,221
    this is because the gameobject does not already have one....

    Code (csharp):
    1.  
    2. if(gameObject.GetComponent<CubeDropper>() ==  null) gameObject.AddComponent<CubeDropper>();
    3. other = gameObject.GetComponent<CubeDropper>();
    4.  
     
  9. Suddoha

    Suddoha

    Joined:
    Nov 9, 2013
    Posts:
    2,824
    Well ye, the scripts have to be attached to the gameobject. Either via script just like bigmisterb's code or manually attach it to your gameobject before running this code, i thought you'd already done that and i also mentioned that. :p
     
  10. KelsoMRK

    KelsoMRK

    Joined:
    Jul 18, 2010
    Posts:
    5,539
    AddComponent returns the component to you so the second call to GetComponent is unnecessary in this case.
     
  11. demiankz

    demiankz

    Joined:
    Mar 20, 2014
    Posts:
    10
    Thank you everyone who's taking the time to help me understand this. But at this point I'm more confused than I was before. Honestly, all I'm trying to do is call one script from another, which I'm gathering is a simple enough feat for someone who understands the concepts of how c# works. Apparently, I don't. Right now, I have two scripts:

    1. GameBrain.cs (assigned to a game object called player)
    2. CubeDropper.cs (unassigned)

    Code (csharp):
    1.  
    2. public class GameBrain : MonoBehaviour
    3. {
    4.     public CubeDropper other;
    5.  
    6.     void Start ()
    7.     {
    8.         other = new CubeDropper();
    9.        
    10.         other.DropCubes();
    11.     }
    12. }
    13.  
    Code (csharp):
    1.  
    2. public class CubeDropper : MonoBehaviour
    3. {
    4.     public void DropCubes()
    5.     {
    6.         GameObject prefab = Resources.Load ("MultiCube") as GameObject;
    7.         for (int i=0;i<100;i++)
    8.         {
    9.             GameObject go=Instantiate(prefab) as GameObject;
    10.             go.transform.position=new Vector3(0,i*5,1f);
    11.         }
    12.     }
    13. }
    14.  
    That code all works. But yes, Unity throws a warning over the other = new line. But it does work. So, if it works, why is Unity throwing a warning? Why does Unity allow this "bad" code to execute?

    Also, Suddoha, the GameBrain script is attached to my game object, the CubeDropper script isn't. And I can't manually attach it in Unity. And Kelso's script doesn't work. Null Reference Exception.
     
  12. KelsoMRK

    KelsoMRK

    Joined:
    Jul 18, 2010
    Posts:
    5,539
    Make CubeDropper not inherit from MonoBehaviour. If you're not attaching it to anything there's no reason to have that derivation.
     
  13. demiankz

    demiankz

    Joined:
    Mar 20, 2014
    Posts:
    10
    When I delete the : MonoBehavior from the script I get the following error:

    Error: The name 'Instantiate' does not exist in the current context.

    So, it would seem that I need to inherit from MonoBehaviour because I'm instantiating those prefabs as GameObjects, yes?
     
  14. KelsoMRK

    KelsoMRK

    Joined:
    Jul 18, 2010
    Posts:
    5,539
    No - just qualify it with Object.Instantiate
     
  15. maleone0487

    maleone0487

    Joined:
    Feb 23, 2011
    Posts:
    59
    Is it the "Null Reference exception" error you don't understand? If so, all that means is "there isn't anything there". Essentially you created a variable that is a reference to something, but nothing was actually plugged into the variable slot. You've created the box, but nothing has been put into it.

    It seems like another issue is the way you are creating the object. Normally you create objects in C# using the new Keyword, but since Monobehaviours must be added to GameObjects, Unity prevents you from being able to create a script that is a Monobehaviour without also adding it to a GameObject.
     
  16. demiankz

    demiankz

    Joined:
    Mar 20, 2014
    Posts:
    10
    KelsoMRK, I get that you're a coding genius. And that this is all plain as day to you, but I literally have about 12 hours total experience coding in c#. So, none of what you're suggesting is helping me understand what is going on. You're telling me to do things that, when I try them, don't work. Most likely because I'm doing them wrong.

    So, this is what I did.

    Code (csharp):
    1.  
    2. public class GameBrain : MonoBehaviour
    3. {
    4.     public CubeDropper other;
    5.  
    6.     void Start ()
    7.     {
    8.  
    9.         other = gameObject.GetComponent<CubeDropper>();    
    10.         other.DropCubes(); // now this is possible
    11.     }
    12. }
    13.  
    Code (csharp):
    1.  
    2. public class CubeDropper// : MonoBehaviour
    3. {
    4.     public void DropCubes()
    5.     {
    6.         GameObject prefab = Resources.Load ("MultiCube") as GameObject;
    7.         for (int i=0;i<100;i++)
    8.         {
    9.             GameObject go=Object.instantiate(prefab) as GameObject;
    10.             go.transform.position=new Vector3(0,i*5,1f);
    11.         }
    12.     }
    13. }
    14.  
    And when I run it, I still get Error: The name 'Instantiate' does not exist in the current context.

    Obviously, I have no idea what I'm doing.
     
  17. demiankz

    demiankz

    Joined:
    Mar 20, 2014
    Posts:
    10
    Maleone0487, I now understand the Null Reference Exception, and I'm pretty sure I get why that was being thrown. Thank you for trying to clarify though.

    The issue now seems to be that, yes, I am creating some object or variable in the CubeDropper script incorrectly. So, how do I reconcile needing to inherit 'Instantiate' from MonoBehaviour without being able to create a script that inherits from MonoBehaviour? Should I just attach the script to the game object and be done with it?

    Honestly, I was just trying to complete an exercise where one script calls another (simple in pretty much any other programming language in the world, yes?), but it has blown up into some sort of weird, deep theory lesson on the inner workings of Unity.
     
  18. Suddoha

    Suddoha

    Joined:
    Nov 9, 2013
    Posts:
    2,824
    So, do you actually plan to attach the CubeDropper Script to any gameObject or not?
    It's getting a bit confusing.

    If you want to attach it:
    - inherit from MonoBehaviour and attach it to a gameObject (for example the same as your GameBrain) and use gameObject.GetComponent<CubeDropper>() in order to access it's attributes and functions.

    If you don't want to attach it:

    - no need to inherit from Monobehaviour, you could inherit from ScriptableObject which offers some of the 'basic' functions that come with MonoBehaviour
    - you cannot use GetComponent<CubeDropper>()

    In case your function does not manipulate or accesses any instance-variables, you can make a static function out of it - this way, you can access the function via the class name without having even one instance of the class and you can also make it inherit from MonoBehaviour (but as people mentioned above, just don't do that when you do not want to attach it, rather inherit ScriptableObjects then)

    If you don't want to do it that way, there's still the solution with the new keyword which creates an individual object of the class, but in this case the script can neither inherit from MonoBehaviour nor ScriptableObject. As your function doesn't do anything with member variables, you could just go with a static function too as there's no point to create an instance of your code at this point.

    As for the instantiate error: it is not inherited from MonoBehaviour, it's a class function of the Object,GameObject etc classes; The function starts with a capital I, i guess you just had a typo there;
     
  19. KelsoMRK

    KelsoMRK

    Joined:
    Jul 18, 2010
    Posts:
    5,539
    instantiate isn't the same as Instantiate. Method and variable names are case sensitive.

    You're also mixing approaches now. If CubeDropper doesn't inherit from MonoBehaviour then you can't (and don't have to) access it via GetComponent, you can simply create it by saying new CubeDropper()

    You might want to run through some basic C# tutorials/courses and then do the tutorials in the Learn section of this site - they're immensely helpful.
     
  20. demiankz

    demiankz

    Joined:
    Mar 20, 2014
    Posts:
    10
    > You might want to run through some basic C# tutorials/courses and then do the tutorials in the Learn section of this site - they're immensely helpful.

    Agreed. Thanks again for trying to help.
     
  21. demiankz

    demiankz

    Joined:
    Mar 20, 2014
    Posts:
    10
    Suddoha, thank you for your help. Yes, it seems like I did have a typo - lowercase i instead of uppercase I. Another frustrating aspect of coding for a newb.

    But to answer your question, no, CubeDropper was unassigned. I was simply trying to understand how to call one script from another. I'm beginning to vaguely understand how that happens. As you said, however, this whole thread became very confusing. I'm having trouble clearly applying most of the concepts we've discussed. Regardless, everything works now, so I guess I'll come back to this concept at a later date.
     
  22. bigmisterb

    bigmisterb

    Joined:
    Nov 6, 2010
    Posts:
    4,221
    OK, so lets demystify this.

    You basically want a game controller to drop cubes. I am not sure why you have 2 separate scripts for this. This can all be done in the same script:

    Code (csharp):
    1.  
    2. using UnityEngine;
    3. using System.Collections;
    4.  
    5. public class Cubit : MonoBehaviour {
    6.  
    7.     GameObject prefab;
    8.  
    9.     // Use this for initialization
    10.     void Start () {
    11.         DropPrefabs ();
    12.     }
    13.  
    14.     // Update is called once per frame
    15.     void DropPrefabs () {
    16.         if (prefab == null) return;
    17.  
    18.         for (int i=0; i<100; i++) {
    19.             GameObject go = (GameObject)Object.Instantiate (prefab);
    20.             go.transform.position=new Vector3(0,i*5,1f);
    21.         }
    22.     }
    23. }
    24.  
    Simply attach that script to a "game controller" and assign the prefab you want. (either from the library or an existing game object)


    Now, if you simply HAVE to have 2 scripts. I would suggest using static variables or methods:
    Code (csharp):
    1.  
    2. using UnityEngine;
    3. using System.Collections;
    4.  
    5. public class Cubit : MonoBehaviour {
    6.  
    7.     public class CubeDropper{
    8.         public static void DropCubes(GameObject prefab){
    9.             if (prefab == null) return;
    10.            
    11.             for (int i=0; i<100; i++) {
    12.                 GameObject go = (GameObject)Object.Instantiate (prefab);
    13.                 go.transform.position=new Vector3(0,i*5,1f);
    14.             }
    15.         }
    16.     }
    17.  
    18.  
    19.     GameObject prefab;
    20.  
    21.     // Use this for initialization
    22.     void Start () {
    23.         CubeDropper.DropCubes (prefab);
    24.     }
    25. }
    26.  

    Static (Singleton) variables and methods can be called as a single reference. This means that you can call it from the class you don't have to make an object to do so. In the example, I call CubeDropper.DropCubes. CubeDropper is a class, not an object. (Yes, that is a big point, classes and objects are not the same thing.)

    If I had "public static float x = 10;" then I could call it the same way as either: "CubeDropper.x = 11;" or "float y = CubeDropper.x;" The later would make y whatever that single value is currently. (hence, Singleton)

    Notes: Assigning the class inside of another class makes it invisible to any other class but that one. (always make them public even though they cannot be seen.) You could also make it a class by it's self, where you could call it from any other class.
     
  23. KelsoMRK

    KelsoMRK

    Joined:
    Jul 18, 2010
    Posts:
    5,539
    Um - that's not really true. A public nested class like that could be accessed via Cubit.CubeDropper. Also - if you're going to nest a class in an effort to keep everything in the same script then you could just as easily implement the DropCubes method in Cubit instead. It's not really buying you anything.

    http://msdn.microsoft.com/en-us/library/ms173120.aspx

    The upshot of this issue is - GameObjects have Components. A Script is a Component. If you declare CubeDropper as inheriting from MonoBehaviour then you have to attach it to a GameObject somewhere in the world. You can then either find that GameObject and Get the Component or you can assign the variable via the Inspector. If you don't do either one of these things before attempting to access that variable then you will get a NullReferenceException because your variable has a value of null.
     
    Last edited: May 1, 2014