Value type vs Reference type
In many programming languages, variables have what is called a "data type". The two primary data types are value types (int, float, bool, char, struct, ...) and reference type (instance of classes). While value types contains the value itself, references contains a memory address pointing to a portion of memory allocated to contain a set of values (similar to C/C++).
For example, Vector3
is a value type (a struct containing the coordinates and some functions) while components attached to your GameObject (including your custom scripts inheriting from MonoBehaviour
) are reference type.
When can I have a NullReferenceException?
NullReferenceException
are thrown when you try to access a reference variable that isn't referencing any object, hence it is null (memory address is pointing to 0).
Some common places a NullReferenceException
will be raised:
Manipulating a GameObject / Component that has not been specified in the inspector
// t is a reference to a Transform.
public Transform t ;
private void Awake()
{
// If you do not assign something to t
// (either from the Inspector or using GetComponent), t is null!
t.Translate();
}
Retrieving a component that isn't attached to the GameObject and then, trying to manipulate it:
private void Awake ()
{
// Here, you try to get the Collider component attached to your gameobject
Collider collider = gameObject.GetComponent<Collider>();
// But, if you haven't any collider attached to your gameobject,
// GetComponent won't find it and will return null, and you will get the exception.
collider.enabled = false ;
}
Accessing a GameObject that doesn't exist:
private void Start()
{
// Here, you try to get a gameobject in your scene
GameObject myGameObject = GameObject.Find("AGameObjectThatDoesntExist");
// If no object with the EXACT name "AGameObjectThatDoesntExist" exist in your scene,
// GameObject.Find will return null, and you will get the exception.
myGameObject.name = "NullReferenceException";
}
Note: Be carefull, GameObject.Find
, GameObject.FindWithTag
, GameObject.FindObjectOfType
only return gameObjects that are enabled in the hierarchy when the function is called.
Trying to use the result of a getter that's returning null
:
var fov = Camera.main.fieldOfView;
// main is null if no enabled cameras in the scene have the "MainCamera" tag.
var selection = EventSystem.current.firstSelectedGameObject;
// current is null if there's no active EventSystem in the scene.
var target = RenderTexture.active.width;
// active is null if the game is currently rendering straight to the window, not to a texture.
Accessing an element of a non-initialized array
private GameObject[] myObjects ; // Uninitialized array
private void Start()
{
for( int i = 0 ; i < myObjects.Length ; ++i )
Debug.Log( myObjects[i].name ) ;
}
Less common, but annoying if you don't know it about C# delegates:
delegate double MathAction(double num);
// Regular method that matches signature:
static double Double(double input)
{
return input * 2;
}
private void Awake()
{
MathAction ma ;
// Because you haven't "assigned" any method to the delegate,
// you will have a NullReferenceException
ma(1) ;
ma = Double ;
// Here, the delegate "contains" the Double method and
// won't throw an exception
ma(1) ;
}
How to fix ?
If you have understood the previous paragraphes, you know how to fix the error: make sure your variable is referencing (pointing to) an instance of a class (or containing at least one function for delegates).
Easier said than done? Yes, indeed. Here are some tips to avoid and identify the problem.
The "dirty" way : The try & catch method :
Collider collider = gameObject.GetComponent<Collider>();
try
{
collider.enabled = false ;
}
catch (System.NullReferenceException exception) {
Debug.LogError("Oops, there is no collider attached", this) ;
}
The "cleaner" way (IMHO) : The check
Collider collider = gameObject.GetComponent<Collider>();
if(collider != null)
{
// You can safely manipulate the collider here
collider.enabled = false;
}
else
{
Debug.LogError("Oops, there is no collider attached", this) ;
}
When facing an error you can't solve, it's always a good idea to find the cause of the problem. If you are "lazy" (or if the problem can be solved easily), use Debug.Log
to show on the console information which will help you identify what could cause the problem. A more complex way is to use the Breakpoints and the Debugger of your IDE.
Using Debug.Log
is quite useful to determine which function is called first for example. Especially if you have a function responsible for initializing fields. But don't forget to remove those Debug.Log
to avoid cluttering your console (and for performance reasons).
Another advice, don't hesitate to "cut" your function calls and add Debug.Log
to make some checks.
Instead of :
GameObject.Find("MyObject").GetComponent<MySuperComponent>().value = "foo" ;
Do this to check if every references are set :
GameObject myObject = GameObject.Find("MyObject") ;
Debug.Log( myObject ) ;
MySuperComponent superComponent = myObject.GetComponent<MySuperComponent>() ;
Debug.Log( superComponent ) ;
superComponent.value = "foo" ;
Even better :
GameObject myObject = GameObject.Find("MyObject") ;
if( myObject != null )
{
MySuperComponent superComponent = myObject.GetComponent<MySuperComponent>() ;
if( superComponent != null )
{
superComponent.value = "foo" ;
}
else
{
Debug.Log("No SuperComponent found onMyObject!");
}
}
else
{
Debug.Log("Can't find MyObject!", this ) ;
}
Sources:
- http://answers.unity3d.com/questions/47830/what-is-a-null-reference-exception-in-unity.html
- https://stackoverflow.com/questions/218384/what-is-a-nullpointerexception-and-how-do-i-fix-it/218510#218510
- https://support.unity3d.com/hc/en-us/articles/206369473-NullReferenceException
- https://unity3d.com/fr/learn/tutorials/topics/scripting/data-types