Unityで急にスクリプトが動作しなくなった時の話

シェアする

順調に開発が進んでいたのに、ある日急にスクリプトが動かなくなったり、
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の動作を確認できるサンプルプロジェクトを、こちらにご用意しました。

Contribute to ChatLune/blog-unity-NullCheck development by creating an account on GitHub.

入社二年目のエンジニアです。
Unity C# Java Android CakePHP などを勉強中です。

スポンサーリンク

シェアする

フォローする