順調に開発が進んでいたのに、ある日急にスクリプトが動かなくなったり、
GameObjectにアタッチしたスクリプトのinspectorを確認したら、
勝手にDisableになっていた。
そんな経験ありませんか?
私が踏んだのは、NullReferenceExceptionが原因でした。
NullReferenceExceptionって?
公式のマニュアルはこちら。
まずは、意図的にNullReferenceExceptionを起こしてみましょう。
例えば、適当にスクリプトを作成し、空のGameObjectにアタッチしておきます。
スクリプトには、publicでButton型の変数を定義しておきます。
public Button button;
そして、Start関数の中で先ほどのbuttonのtextを変えようとします。
void Start(){
button.GetComponentInChildren<Text> ().text = "ほげる";
}
これをこのままコンパイルし、Unityで実行します。
Inspectorでbutton変数を設定せず「None」のままにしていたため、
buttonのtextに代入できずにNullReferenceExceptionとなります。
正常に動かすためには、Inspector上でbutton変数に、
Buttonオブジェクトを設定しておく必要があります。
NullReferenceExceptionが起きると?
NullReferenceExceptionがスクリプトの途中で発生した場合、
その行で処理が止まってしまい、その後の処理が走らなくなってしまいます。
テストのために、以下のようなスクリプトを作成しました。
using System.Collections;
using System.Collections.Generic;
using UnityEngine;
using UnityEngine.UI;
public class ButtonScript : MonoBehaviour {
public GameObject myObject;
public int count;
void Awake () {
myObject = GameObject.Find ("GameObject");
ShowName ();
AddCount ();
myObject.GetComponent<Button>().enabled = false;
Debug.Log ("ほげほげ");
}
public void ShowName(){
Debug.Log (myObject.name);
}
public void AddCount(){
count++;
Debug.Log (count);
}
}
Awake()の中で.Find()を使い、ゲームオブジェクトを取得します。
ShowName()は、myObjectオブジェクトの名称をコンソールに表示します。
AddCountは、int型の変数countをインクリメントし、
countの中身をコンソールに表示します。
その後、空のGameObjectである「myObject」に対して、
「GetComponent< Button>().enabled = false」を実行します。
「myObject」には「Button」コンポーネントは追加されていないので、
GetComponentは失敗し、NullReferenceExceptionとなります。
そのあとに「Debug.Log (“ほげほげ”)」とコードが続いています。
GetComponentは失敗しましたが、「Debug.Log (“ほげほげ”)」と言うコード自体には
問題がないはずなので、一見コンソールに「ほげほげ」と出力されそうです。
しかし、コンソールログには、Debug.Logの出力は行われません。
まとめ
先ほどのように、Null後のコードが動かなくても問題ない場合もありますが、
以下のようなスクリプトでもこのパターンは起こり得ます。
using System.Collections;
using System.Collections.Generic;
using UnityEngine;
using UnityEngine.UI;
public class ButtonScript : MonoBehaviour {
public AudioClip audioClip;
public AudioSource audioSource;
public float soundTime = 0;
void Awake () {
Debug.Log (audioClip.length);
soundTime = audioClip.length;
audioSource.clip = audioClip;
audioSource.Play ();
}
}
このスクリプトは、audioClipから音声ファイルの長さを取ろうとしていますが、
音声ファイルが設定されなかった場合、「UnassignedReferenceException」となり、
そこで処理が停止します。
また、audioSourceの指定をしなかった場合にも、
同様に「UnassignedReferenceException」となります。
結果として、soundTimeへの代入も行われず、audioSourceはPlay()も
実行されずに音声は再生されません。
何らかの理由で音声ファイルが設定されなくても、
後の処理がきちんと走るように、Nullチェックやtry-catchブロックを作りましょう。
Nullチェックの例
using System.Collections;
using System.Collections.Generic;
using UnityEngine;
using UnityEngine.UI;
public class ButtonScript : MonoBehaviour {
public AudioClip audioClip;
void Awake () {
myObject = GameObject.Find ("GameObject");
ShowName ();
AddCount ();
//myObject.GetComponent< Button> ().enabled = false;
if (audioClip != null) {
Debug.Log (audioClip.length);
} else {
Debug.Log ("audioClip is Null!");
}
if (audioSource != null) {
audioSource.clip = audioClip;
audioSource.Play ();
} else {
Debug.Log ("audioSource is Null!");
}
Debug.Log("ほげほげ");
}
このようにする事で、仮に「audioClip」に音声ファイルが設定されていなくても、
そこで処理が止まる事なくNullであると言うメッセージを表示する事ができます。
audioClipが設定されていない事をコンソールで確認でき、if文が終わったあとの
Debug.Logも出力する事ができる。
サンプルプロジェクト
NullCheckの動作を確認できるサンプルプロジェクトを、こちらにご用意しました。
入社3年目のエンジニアです。
主にiOS/Androidアプリの開発をしています。