チュートリアル:Unityで親子クラスのライフサイクルを正しく扱う
では今回の記事内容をベースに、チュートリアル形式に整理します。
読者が実際に手を動かして「親クラスの Start が呼ばれない問題」を体験しながら学べる流れにしました。
このチュートリアルでは、基底クラスと派生クラスを使った継承を題材に、Unity のライフサイクルメソッド(Awake / Start / Update)の挙動を理解します。
最後には、正しい設計パターンを習得することが目標です。
ステップ1:親クラスを作る
まずは BaseMover というクラスを作り、Rigidbody を取得して毎フレーム前進する処理を書きます。
using UnityEngine;
public class BaseMover : MonoBehaviour
{
protected Rigidbody _rb;
protected void Start()
{
Debug.Log("BaseMover.Start 呼ばれた");
_rb = GetComponent<Rigidbody>();
}
protected void Update()
{
_rb.AddForce(Vector3.forward);
}
}
実行
- Cube を作成
- Rigidbody をアタッチ
- BaseMover をアタッチ
- 再生
→ 前に進むのを確認できます。
ステップ2:派生クラスを作る
次に PlayerMover クラスを作成し、BaseMover を継承させます。
using UnityEngine;
public class PlayerMover : BaseMover
{
protected void Start()
{
Debug.Log("PlayerMover.Start 呼ばれた");
// 親を呼んでいない!
}
}
Cube に PlayerMover をアタッチ(BaseMover は削除)して再生してみましょう。
結果
- ログには 「PlayerMover.Start 呼ばれた」 だけが表示される
- 親の Start は呼ばれず、_rb が null のまま
- Update で NullReferenceException が発生
ステップ3:解決策を試す
方法A:base.Start() を呼ぶ
using UnityEngine;
public class BaseMover : MonoBehaviour
{
protected Rigidbody _rb;
protected virtual void Start() // ← 仮想化して拡張を許容
{
Debug.Log("BaseMover.Start 呼ばれた");
_rb = GetComponent<Rigidbody>();
}
protected void Update()
{
_rb.AddForce(Vector3.forward);
}
}
public class PlayerMover : BaseMover
{
protected override void Start()
{
base.Start(); // ← 親の初期化を必ず通す
// 子の初期化…
Debug.Log("PlayerMover.Start 呼ばれた");
}
}
再生すると、今度は Rigidbody が初期化され、前進する動作が復活します。
方法B:Awake に寄せる(おすすめ)
[RequireComponent(typeof(Rigidbody))]
public class BaseMover : MonoBehaviour
{
protected Rigidbody _rb;
protected virtual void Awake()
{
_rb = GetComponent<Rigidbody>();
}
}
これなら、子に Start を書いても親の初期化が抜け落ちることはありません。
方法C:子に Start を書かない
もし子で初期化が不要なら、単純に 子の Start を削除すれば親の Start がそのまま呼ばれます。
ステップ4:図解で理解
ライフサイクルの呼ばれ方を図で整理してみましょう。
親と子に Start がある(親は呼ばれない)

子に Start がない(親が呼ばれる)

子で base.Start() を呼ぶ(両方呼ばれる)

よくあるハマりポイント
症状 | 原因 | 解決策 |
---|---|---|
NullReferenceException が出る | 子に Start を書いて親が呼ばれない | base.Start() を呼ぶ or 子の Start を削除 |
Rigidbody を付け忘れ | インスペクタで未設定 | [RequireComponent(typeof(Rigidbody))] を親に付ける |
Awake / Start のタイミングが混乱する | 初期化を Start に書いている | 依存コンポーネントの取得は Awake に寄せる |
チュートリアルまとめ
- Unity のライフサイクルは 親を自動で呼ばない
- 子に Start を書いたら 必ず base.Start() を呼ぶ
- 依存コンポーネントは Awake に寄せると安全
- 子に初期化が不要なら Start を書かないのが最もシンプル
これで「親の Start が呼ばれず null になる」問題を再現・解決できました。
ディスカッション
コメント一覧
まだ、コメントがありません