Unityで「StartにGetComponentを書くとNullになる」理由と対処
Unityでよくあるトラブルとして、
StartでGetComponentしたのに、OnTriggerでNullReferenceExceptionが出る
という現象があります。
特に「プレハブをInstantiateして使う場合」に発生しやすいため、
実行順序を正しく理解しておくことが重要です。
想定する状況
- プレハブにスクリプトをアタッチしている
- スポナーから
Instantiate()で生成する Start()でGetComponent()を行っているOnTriggerEnter()内でその参照を使用している
実行順序
Unityでは、オブジェクト生成時の処理は次の順序で実行されます。
Instantiate
↓
Awake()
↓
OnEnable()
↓
(この時点で物理イベントが発生する可能性あり)
↓
Start()
ここで重要なのは、
OnTriggerEnterはStartより前に呼ばれる可能性がある
という点です。
問題が発生するコード
private AudioSource audioSource;
private void Start()
{
audioSource = GetComponent<AudioSource>();
}
private void OnTriggerEnter(Collider other)
{
audioSource.Play(); // nullの可能性あり
}
この場合、OnTriggerEnter が Start より先に呼ばれるとaudioSource は未取得のままとなり、例外が発生します。
正しい実装
private AudioSource audioSource;
private void Awake()
{
audioSource = GetComponent<AudioSource>();
}
private void OnTriggerEnter(Collider other)
{
audioSource.Play();
}
Awake は Start より先に必ず実行されるため、
この問題を回避できます。
使い分けの基準
処理の内容によって、記述場所を整理すると以下のようになります。
| 処理内容 | 記述場所 |
|---|---|
| GetComponent | Awake |
| 自分自身の初期化 | Awake |
| 他オブジェクトとの連携 | Start |
| 初期状態の設定 | Start |
なぜInstantiateで発生しやすいのか
シーンに配置されたオブジェクトでは、
すべての Awake → Start が順番に実行されるため、問題が表面化しにくくなります。
一方で、Instantiate() の場合は
- 生成直後に
Awakeが実行される - その後すぐに物理判定が行われる
Startはその後に実行される
という流れになるため、OnTriggerEnter が先に呼ばれるケースが発生します。
補足(防御的な書き方)
private void OnTriggerEnter(Collider other)
{
if (audioSource == null) return;
audioSource.Play();
}
実行順序の理解に加え、
このような防御コードを入れておくとトラブルを減らせます。
まとめ
OnTriggerEnterはStartより先に呼ばれる可能性があるStartで取得した参照は未初期化のまま使われることがあるGetComponentはAwakeに書くのが安全
この内容は、プレハブ生成を扱う際の基本的な注意点です。
特に「動的生成+物理イベント」を扱う場合は、必ず意識して実装する必要があります。



ディスカッション
コメント一覧
まだ、コメントがありません