「Startで取得したはずの参照がnullになる」「物理挙動がなぜかガクガクする」——Unity初心者を悩ませるバグの多くは、実は スクリプトがいつ・どの順序で実行されるか を知るだけで防げます。この一連の流れが「ライフサイクル」です。
Unityのスクリプトは、オブジェクトの誕生から破棄まで、決まった順序でイベント関数が自動的に呼ばれる仕組みになっています。この記事では、その中でも特に重要なAwake、Start、Update、FixedUpdate、LateUpdateを中心に、役割と使い分けを図解で整理します。
この記事でわかること
- ライフサイクル全体の流れ(一度きりの処理と毎フレームの処理)
AwakeとStartの正しい使い分けUpdate・FixedUpdate・LateUpdateの役割の違いと実例- パフォーマンスを落とさないための注意点
ライフサイクルイベントの全体像
Unityのスクリプトは、特定のイベントが発生したタイミングで自動的に呼び出される「イベント関数」を持っています。これらの関数は、オブジェクトがシーンにロードされてから破棄されるまで、決まった順序で実行されます。主要なイベント関数の実行順序は以下の通りです。
-
初期化 (Initialization)
Awake(): オブジェクトがロードされた直後に一度だけ呼ばれます。他のどの関数よりも先に実行されます。OnEnable(): オブジェクトがアクティブになった瞬間に呼ばれます。SetActive(true)のたびに 毎回 呼ばれる点がAwakeとの違いです。Start(): 最初のフレームのUpdateが呼ばれる直前に一度だけ呼ばれます。
-
物理演算 (Physics)
FixedUpdate(): 固定された時間間隔で呼ばれます。物理演算の処理に適しています。
-
ゲームロジック (Game Logic)
Update(): 毎フレーム呼ばれます。ゲームの主要なロジックや入力処理を記述します。LateUpdate(): 全てのUpdate関数が呼ばれた後に毎フレーム呼ばれます。カメラの追従処理などに使われます。
-
レンダリング (Rendering)
OnRenderObject()など: オブジェクトの描画に関連する処理です。
-
終了 (Teardown)
OnDisable(): オブジェクトが非アクティブになった瞬間に呼ばれます。OnDestroy(): オブジェクトが破棄される直前に呼ばれます。
全体の流れを図にすると以下のようになります。FixedUpdate・Update・LateUpdateの3つは毎フレーム繰り返し呼ばれ、Awake・Start・OnDestroyはオブジェクトごとに一度きりです。注意したいのはOnEnableとOnDisableで、この2つは オブジェクトが有効化・無効化されるたびに何度でも 呼ばれます(後半の実践で実際に確認します)。

補足: 複数のオブジェクトが存在する場合、 オブジェクト同士の
Awake()の呼ばれる順序は保証されませ ん 。「AのAwakeはBのAwakeより先」といった前提を置かないようにしましょう。どうしても順序を制御したい場合は、Edit > Project Settings > Script Execution Orderで明示的に指定できます。
主要なイベント関数の詳細
初期化:Awake() vs Start()
Awake()とStart()はどちらも初期化処理に使われますが、実行タイミングに重要な違いがあります。
| 関数 | 実行タイミング | 主な用途 |
|---|---|---|
Awake() | スクリプトインスタンスがロードされた直後。常にStart()より前。 | 自身のコンポーネントの参照取得や、他のオブジェクトの状態に依存しない初期化。 |
Start() | 最初のフレームのUpdate()直前。Awake()の後。 | 他のオブジェクトのAwake()が完了していることを前提とした初期化。 |
重要なのは、シーンロード時に存在するオブジェクトは、全員のAwake()が完了した後に各自のStart()が呼ばれるという点です。 そのため、スクリプト間で参照を渡し合うような場合は、Awake()で参照を確保し、Start()でその参照を使って処理を行うのが安全です。
ただしこの保証は「シーンロード時にすでに存在するオブジェクト同士」の話です。実行中にInstantiate()で生成したオブジェクトは、その瞬間にAwake()が走ります(詳しくは後半の実践で確認します)。
イメージとしては「Awake=まず全員が自分の身支度を整える時間」「Start=全員の準備が済んでから連携を始める時間」です。

using UnityEngine;
public class PlayerController : MonoBehaviour
{
private Rigidbody rb;
private GameManager gameManager;
// 自身のコンポーネント取得はAwakeで行う
void Awake()
{
rb = GetComponent<Rigidbody>();
Debug.Log("Awake: Rigidbodyを取得しました。");
}
// 他のオブジェクトの初期化が完了している前提の処理はStartで行う
void Start()
{
gameManager = FindFirstObjectByType<GameManager>();
gameManager.RegisterPlayer(this);
Debug.Log("Start: GameManagerにプレイヤーを登録しました。");
}
}
補足: 以前は
FindObjectOfType<T>()が定番でしたが、Unity 2023.1以降(Unity 6を含む)では非推奨となり、より高速なFindFirstObjectByType<T>()の使用が推奨されて います。古い記事やサンプルコードを参考にする際は読み替えてください。
更新処理:Update() vs FixedUpdate() vs LateUpdate()
これらの関数は毎フレーム、またはそれに近い頻度で呼ばれますが、用途が明確に異なります。
| 関数 | 実行タイミング | 主な用途 |
|---|---|---|
FixedUpdate() | 固定の時間間隔(デフォルトは0.02秒)。フレームレートに依存しない。 | Rigidbodyを使った物理演算(力の追加など)。 |
Update() | 毎フレーム。フレームレートに依存して実行間隔が変わる。 | 入力処理、時間経過による移動、ゲームロジック全般。 |
LateUpdate() | 全てのUpdate()が完了した後。 | カメラの追従、キャラクターのIK(インバースキネマティクス)など。 |
2つの違いは「呼ばれる間隔」をタイムラインで見ると分かりやすいです。

FixedUpdate()は物理エンジンの更新タイミングと同期しているため、Rigidbodyに力を加 えるなどの物理的な処理は必ずここで行います。一方、Update()はフレームごとに呼ばれるため、入力の取得や見た目の更新など、物理演算と直接関係ない処理に適しています。Time.deltaTimeを乗算することで、フレームレートの変動に影響されない動きを実装できます。
補足: 「毎フレーム
Update→FixedUpdateが1回ずつ」ではない点に注意してください。フレームレートが高いと、FixedUpdate()が 1回も呼ばれないフレーム が発生し、逆にフレームレートが低いと 1フレームに複数回 呼ばれます。「入力の検知はUpdate()で行い、物理的な移動はFixedUpdate()で行う」という使い分けは、この仕組みが理由です(Input.GetKeyDownなどをFixedUpdate()で読むと入力を取りこぼすことがあります)。
実際の開発シーンに当てはめると、使い分けはこうなります。「プレイヤーの見ている世界に関わること」はUpdate、「物理エンジンに任せること」はFixedUpdate、と覚えておくと迷いません。

LateUpdate()は、他のオブジェクトのUpdate()での動きが全て完 了した後に実行したい処理に使います。例えば、プレイヤーキャラクターがUpdate()で移動した後に、そのプレイヤーをカメラが追いかける、といった処理に最適です。これにより、カメラの動きがカクつくのを防げます。
using UnityEngine;
public class CameraFollow : MonoBehaviour
{
public Transform target;
public Vector3 offset;
// ターゲットの移動が完了した後にカメラ位置を更新する
void LateUpdate()
{
if (target != null)
{
transform.position = target.position + offset;
}
}
}
実践:呼ばれる順序を3つのケースで観察する
表で順序を覚えるより、ログで実際に観察する方が確実に身につきます。ここでは、アクションゲームの敵・RPGのNPC・シューティングの弾など、どのジャンルでも毎日起こる3つのケースを1つのスクリプトで観察します。
- ケース1:シーンロード——最初からシーンに置いてあるオブジェクトはどう初期化される?
- ケース2:SetActiveのオン/オフ——ポーズメニューの開閉やオブジェクトの出し入れで何が呼ばれる?
- ケース3:実行中のInstantiate——弾や敵をスポーンした瞬間はどうなる?

観察用のスクリプトはこれだけです。名前を付けて、空のGameObjectに貼り付けます。
using UnityEngine;
public class LifecycleLogger : MonoBehaviour
{
void Awake() { Debug.Log($"{name}: Awake"); }
void OnEnable() { Debug.Log($"{name}: OnEnable"); }
void Start() { Debug.Log($"{name}: Start"); }
void OnDisable() { Debug.Log($"{name}: OnDisable"); }
void OnDestroy() { Debug.Log($"{name}: OnDestroy"); }
}
ケース1では、これを付けたオブジェクトをEnemyA・EnemyBと2つシーンに置いて再生してみてください。EnemyA: Awake→EnemyB: Awake→(全員のAwakeが済んでから)EnemyA: Start→EnemyB: Startのように、 Awakeが全員分そろってからStartが始まる ことが確認できます。
ケース2では、再生中にInspectorでEnemyAのチェックを外して、また付けてみてください。ログに出るのはOnDisableとOnEnableだけです。AwakeとStartは 最初の1回しか呼ばれません 。「非表示から復帰するたびに初期化したい」処理をStartに書いてしまい、2回目以降に動かない——というバグは、この違いを知らないことが原因です。
ケース3では 、次のスクリプトで再生中に弾を生成してみます。
using UnityEngine;
public class Spawner : MonoBehaviour
{
[SerializeField] private GameObject bulletPrefab;
void Update()
{
if (Input.GetKeyDown(KeyCode.Space))
{
Debug.Log("--- Instantiate直前 ---");
Instantiate(bulletPrefab);
Debug.Log("--- Instantiate直後 ---");
}
}
}
ログは「直前 → Awake → OnEnable → 直後 →(次のフレームの頭で)Start」の順になります。つまり Instantiate()の行が終わった時点で、生成物のAwakeはもう実行済み です。一方Startは次のUpdateの直前まで遅れるので、「生成した直後に相手のStartの結果を使う」コードは動きません。スポーン処理のバグの多くはここから生まれます。
ポイントは2つです。 「Awake→Start全員そろって」の保証はシーンロード時の話 であること、そして OnEnable/OnDisableだけは何度でも呼ばれる こと。この2つを自分の目で確認しておけば、初期化バグの原因調査はぐっと速くなります。
パフォーマンスに関する注意点
Update()やFixedUpdate()は非常に頻繁に呼び出されるため、これらの関数内に重い処理を記述すると、ゲーム全体のパフォーマンスに深刻な影響を与えます。特に以下の点に注意しましょう。
Update()の乱用を避ける: 毎フレーム実行する必要のない処理は、Update()から分離しましょう。例えば、特定の条件が満たされた時だけ実行する処理は、イベ ントドリブンな設計やコルーチンを検討します。GetComponentのキャッシュ:Update()内でGetComponent<T>()を呼び出すのは非常に非効率です。必要なコンポーネントはAwake()やStart()で一度だけ取得し、変数に保持(キャッシュ)しておきましょう。- カスタムアップデートマネージャー: 数百、数千といった大量のオブジェクトが
Update()を持つ場合、UnityのネイティブコードとC#コード間の呼び出しオーバーヘッドが問題になることがあります。このような場合、更新処理を管理するシングルトンクラス(カスタムアップデートマネージャー)を作成し、そこから各オブジェクトの更新メソッドを呼び出すことで、パフォーマンスを改善できる場合があります。
おまけ:先に知っておくと良いこと
ライフサイクルを押さえたら、次はこのあたりの知識が視野に入ってきます。今は「そういうものがある」と知っておくだけで十分です。
- イベント関数はまだまだある: 今回紹介した以外にも、他のオブジェクトとぶつかった瞬間に呼ばれる
OnTriggerEnter/OnCollisionEnterなど、物理イベント系の関数があります。OnTriggerとOnCollisionの記事 で解説しています。 - 時間差の処理はコルーチンが得意: 「3秒後に実行」「徐々にフェードアウト」のような処理は、
Updateでタイマーを自作するより コルーチン を使うとシンプルに書けます。コルーチンの記事 を参照してください。 Updateは数が増えると重くなる: オブジェクトが数千個の規模になると、Update呼び出しそのもののオーバーヘッドが無視できなくなります。その対策が カスタムアップデートマネージャーの記事 で紹介している手法です。
まとめ
Unityのライフサイクルを理解することは、初心者からプロフェッショナルまで、全てのUnity開発者にとって不可欠な知識です。各イベント関数がいつ、なぜ呼ばれるのかを把握することで、より信頼性が高く、パフォーマンスの良いコードを書くことができます。
- 初期化:
Awake()で自身の準備を整え、Start()で他のオブジェクトとの連携を開始する。 - 更新: 物理演算は
FixedUpdate()、入力やロジックはUpdate()、追従処理はLateUpdate()と使い分ける。 - パフォーマンス:
Update()内の重い処理は避け、コンポーネントのキャッシュを徹底する。
これらの基本をしっかりと押さえ、効率的なUnity開発の第一歩を踏み出しましょう。