Unity Term Book
スクリプティングとライフサイクル

MonoBehaviour

MonoBehaviour は Unity スクリプトの基底クラスです。ライフサイクルフックや Component へのアクセスを提供し、C# コードと Unity Engine をつなぎます。Awake・Start・Update・FixedUpdate・OnDestroy の使い分けと注意点を学べます。

想像してみてください...

MonoBehaviour は Unity との雇用契約のようです。サイン(MonoBehaviour を継承)すると、従業員(スクリプト)は自動で定例会議に呼ばれます:キックオフ(Awake)、初日(Start)、毎日のスタンドアップ(Update)、物理同期(FixedUpdate)、退職面談(OnDestroy)。継承がなければ、メソッドを定義しても呼ばれません。

概念の詳細

MonoBehaviour は Behaviour → Component → Object を継承します。この継承により Unity はリフレクション経由で magic methods(Awake、Start、Update…)を呼び出します—— オーバーライドや base 呼び出しは不要です。

主なライフサイクル: Awake はオブジェクト生成直後(無効でも)、 Start は有効化後の最初のフレーム、 Update は毎フレーム、FixedUpdate は Physics タイムステップ (既定 0.02s)で走ります。

パフォーマンス: 空の Update() でも リフレクション呼び出しのコストがあります。空の Update は削除。enabled = false で オブジェクト全体を破棄せずライフサイクルを一時停止できます。

制限: コンストラクタ( new MyScript())は使えません——AddComponent<T>() を使います。 スレッドセーフではない——MonoBehaviour API はメインスレッド専用です。

ダイアグラム:継承チェーンと magic methods

Object
→
Component
→
Behaviour
→
MonoBehaviour
→
YourScript

Awake()

内部初期化 · Start の前

Start()

初期セットアップ · 最初のフレーム

Update()

毎フレームのロジック · Input、AI...

FixedUpdate()

Physics · 0.02s ごと

LateUpdate()

全 Update の後 · Camera follow

OnEnable()

オブジェクト有効化時

OnDisable()

オブジェクト無効化時

OnDestroy()

オブジェクト破棄時

ハンズオン手順

1

新しい C# スクリプトを作成

Assets → Create → C# Script。コンパイルエラーを避けるためファイル名とクラス名を一致させます。

2

内部セットアップに Awake

コンポーネント参照のキャッシュに Awake(_rb = GetComponent<Rigidbody>())。他オブジェクトへの依存は避けます。

3

オブジェクト間セットアップに Start

他オブジェクトが必要なときは Start(相手の Awake は既に完了)。

4

Update と FixedUpdate を分ける

入力と AI → Update。物理力(AddForce、velocity)→ FixedUpdate でグリッチを防ぐ。

5

OnDestroy でクリーンアップ

イベント購読解除、Coroutine/タイマー停止を OnDestroy で行いリークを防ぎます。

インタラクティブシミュレーター

Play Scene を押し、ライフサイクルメソッドが順に発火するのを確認。Disable や Destroy も試せます。

Lifecycle Timeline

Console

Play を押して開始...

Frame: —|State: Inactive|Play 待ち...

コード例

基本

標準的な MonoBehaviour 構成と、各ライフサイクルメソッドの役割。

using UnityEngine;

public class PlayerController : MonoBehaviour
{
  [SerializeField] private float speed = 5f;
  private Rigidbody _rb;
  private Vector3   _inputDir;

  void Awake()
  {
      // Cache component — no dependency on other scripts
      _rb = GetComponent<Rigidbody>();
  }

  void Start()
  {
      // Setup that needs GameManager.Awake to have finished
      GameManager.Instance.RegisterPlayer(this);
  }

  void Update()
  {
      // Read input every frame
      float h = Input.GetAxisRaw("Horizontal");
      float v = Input.GetAxisRaw("Vertical");
      _inputDir = new Vector3(h, 0, v).normalized;
  }

  void FixedUpdate()
  {
      // Physics move — do not use Time.deltaTime here
      _rb.MovePosition(_rb.position + _inputDir * speed * Time.fixedDeltaTime);
  }

  void OnDestroy()
  {
      GameManager.Instance.UnregisterPlayer(this);
  }
}

コード例

上級

最適化:空の Update を避け、イベント購読は OnEnable/OnDisable で。

using UnityEngine;
using System;

// Full lifecycle component — pairs well with an event system
public class HealthComponent : MonoBehaviour
{
  [SerializeField] private int maxHp = 100;
  private int _currentHp;

  public event Action<int,int> OnHealthChanged;  // (current, max)
  public event Action           OnDied;

  void Awake() => _currentHp = maxHp;

  void OnEnable()
  {
      // Subscribe when the component becomes active
      GameEvents.OnDamageDealt += TakeDamage;
  }

  void OnDisable()
  {
      // Unsubscribe immediately — avoid calls while disabled
      GameEvents.OnDamageDealt -= TakeDamage;
  }

  public void TakeDamage(int amount)
  {
      _currentHp = Mathf.Max(0, _currentHp - amount);
      OnHealthChanged?.Invoke(_currentHp, maxHp);
      if (_currentHp == 0) OnDied?.Invoke();
  }
}

📌 要点

  • ▸Awake = 内部、Start = オブジェクト間、Update = 毎フレーム
  • ▸物理 → FixedUpdate(Update ではない)
  • ▸空の Update() にもコスト——未使用なら削除
  • ▸購読/解除は OnEnable/OnDisable

⚠️ よくあるミス

  • ❌ Update() 内で GetComponent

    遅い——毎フレーム検索

    ✅ Awake でキャッシュ:_rb = GetComponent<Rigidbody>()

  • ❌ コンストラクタ:new PlayerCtrl()

    MonoBehaviour は new 禁止——Unity が警告

    ✅ go.AddComponent<PlayerCtrl>()