Once upon a time there was a little boy living with his mother alone between the mountains. They lived a peaceful life. One day the boy’s mother got very sick. The boy called for a doctor, the best in the valley. When the doctor finally came, he examined the woman. Unfortunately, there was only one medicine for her illness and this medicine was very hard to get. The only known location was far away from there, on very dangerous lands, full of traps and monsters. Without thinking too much, the boy took his wooden sword, backpack and he went on a trip where… NullReferenceException.
If there’s one thing that is common for most of Unity games, this thing is NullReferenceException. This little fella can be quite a nuisance, especially on mobile platform, where you most probably want to have a script call optimization set to Fast But No Exceptions. That’s because any exception will cause your application crash immediately. Is this worth the risk? It depends on the time your game requires to execute all the scripts in each frame. The more time it needs, the more you will benefit from enabling the optimization.
What NullReferenceException is?
Before killing the dragon, we must fight the dragon. Before fighting the dragon, I would say that we have to get to know the dragon. NullReferenceException is so common issue, that is has its own Unity documentation page! But let’s compare it with official .NET documentation definition.
Official:
In this article we will talk about techniques that will help you minimize NullReferenceException occurrence possibility.
Unity:
A
NullReferenceException
happens when you try to access a reference variable that isn’t referencing any object. If a reference variable isn’t referencing an object, then it’ll be treated asnull
.
So this means that if you have a reference (field or variable) that is not pointing to any object (is null) and you try to access it (access parameter, field, or execute a method of this non-existing object), then you will receive a NullReferenceException.
GameObject go = null; go.SetActive(true); // NullReferenceException!
But… there’s one subtle, yet important, difference with NullReferenceException when working with Unity objects. Normally, you may do a check if an object is null before trying to do anything with it:
GameObject go = null; if (go != null) { // This will be executed only when 'go' is not null go.SetActive(true); }
But Unity objects are a bit different. Unity objects can report themselves as null even if they are still referencing an object!
No, that’s not a bug! Some referenced objects are scene objects. Imagine what happens if a scene is being replaced by another scene – all regular scene objects have to be destroyed. Since Unity cannot update your valid references inside your code (they still will be valid after destroying the scene), it is faking a null check for those that you are still referring to, so it will (should) stop you from use these any longer. In case you do try to use any of these objects, you may receive a message like this one:
It is also an exception that will crash your app, so be warned!
Fail Fast
The worst and yet the most common case of NullReferenceException is when your component expects some other objects to be passed into it through the inspector, but for some reason somebody didn’t do it.
public class Star : MonoBehaviour { public Text scoreText; void OnTriggerEnter(Collider other) { if (other.tag == "Player") { scoreText.text = "scored!"; } } }
The example above is a simplified situation when you want to display a label when player touches the star. Collider other is guaranteed to be a valid reference (based on documentation), but what we cannot be sure about, is the scoreText field that should be visible in the inspector. What if somebody forgets to assign it? You will know about it only after trying to touch the star. That’s definitely too late!
We can handle these kind of situation using Asserts (we talked about asserts before). Asserts will tell us if something is wrong, and it’s a wise choice if all the checks can be done right after the scene is launched. Let’s try to add an assert like this:
public class Star : MonoBehaviour { public Text scoreText; void OnEnable() { Assert.IsNotNull(scoreText, "ScoreText is not set!"); } void OnTriggerEnter(Collider other) { if (other.tag == "Player") { scoreText.text = "scored!"; } } }
This code will yield an error right after the scene is started. We no longer have to wait for the player to touch the star. That way we can check if everything is OK with the scene just by launching it!
Do I have to add an assert for each inspector object? Yes! You cannot trust that someone doesn’t break your scene references.
Public field is not bulletproof
Have you noticed that public fields can be accessed from any place? Yep, this can be bad. This means that any other script may get to it and set it to whatever it wants to. Assert won’t help us if other script will decide to replace our reference in the middle of playing the game.
To make it a little more bulletproof, we can make it private. Private fields can be displayed and assigned within the inspector, but a field like that needs a [SerializeField] annotation:
public class Star : MonoBehaviour { [SerializeField] private Text _scoreText; void OnEnable() { Assert.IsNotNull(scoreText, "ScoreText is not set!"); } void OnTriggerEnter(Collider other) { if (other.tag == "Player") { scoreText.text = "scored!"; } } }
Now, the field cannot be accessed outside the current object what makes the risk of unexpected NullReferenceException significantly lower.
To be continued…
You can read the second part of this article here.