Unity Term Book
脚本与生命周期

Awake & Start

在 Unity 生命周期中,Awake 于对象创建时运行(即使 GameObject 被禁用);Start 在首次启用的那一帧运行,且场景中所有 Awake 都已结束。理清这一顺序能避免空引用与初始化竞态,是写 MonoBehaviour、做依赖注入、缓存组件引用与场景启动逻辑时必须掌握的基础时序。

想象一下...

Awake 是上班前的个人准备——换衣服、拿钥匙,不涉及别人。Start早会开场——所有人已经到齐(全部 Awake 跑完),所以可以分配跨团队工作(GameManager.Instance、查找其他对象…)。

概念详解

Awake() 在实例创建时恰好调用一次——即使组件 enabled = false。适合做内部引用: _rb = GetComponent<Rigidbody>()

Start() 在组件被 启用后的第一帧运行。因为场景中每个 Awake() 都已经跑完, Start 适合访问 Singleton、查找其他对象或注册事件。若先禁用再重新启用, Start 不会再次运行

执行顺序:Unity 先对所有脚本跑 Awake,再对 所有脚本跑 Start。脚本之间的相对顺序未定义,除非你在 Project Settings 中设置 Script Execution Order

运行时 Instantiate:动态生成的对象也会收到 Awake + Start。Awake 在 Instantiate() 返回前运行;Start 在下一帧开始时运行。

跨脚本的执行顺序

⚡ AWAKE 阶段(首帧之前)

GameManager.Awake()

_instance = this

PlayerCtrl.Awake()

_rb = GetComponent()

EnemyAI.Awake()

_nav = GetComponent()

🚀 START 阶段(第一帧)

GameManager.Start()

LoadLevel(1)

PlayerCtrl.Start()

GameManager.Instance ✅

EnemyAI.Start()

FindPlayer() ✅

🔄 Update 循环(每帧)

动手步骤

1

Awake:自我初始化

缓存 GetComponent<T>()、初始化私有字段、搭建 Singleton。不要调用其他对象。

2

Start:连接外部世界

使用 GameManager.InstanceFindObjectOfType<T>()、注册事件。

3

需要时使用 Script Execution Order

Edit → Project Settings → Script Execution Order → 把脚本拖得更高,让它更早运行。

4

避免跨脚本空引用

若 ScriptA.Awake 访问尚未 Awake 的 ScriptB,会得到 null。跨脚本访问放在 Start。

交互模拟器

向场景添加脚本并按 Load Scene,观察每个脚本先触发 Awake 再触发 Start。

Scripts in Scene

Execution Log

添加脚本后点击 Load Scene...

Phase: Idle|至少添加 2 个脚本并点击 Load Scene

代码示例

基础

在 Awake 中建立 Singleton;其他组件在 Start 中连接。

public class GameManager : MonoBehaviour
{
  public static GameManager Instance { get; private set; }

  void Awake()
  {
      // Singleton setup — runs before every Start()
      if (Instance != null) { Destroy(gameObject); return; }
      Instance = this;
      DontDestroyOnLoad(gameObject);
  }
}

public class PlayerController : MonoBehaviour
{
  private Rigidbody _rb;

  void Awake() => _rb = GetComponent<Rigidbody>();

  void Start()
  {
      // Safe: GameManager.Awake() already finished
      GameManager.Instance.RegisterPlayer(this);
  }
}

代码示例

进阶

用 [DefaultExecutionOrder] 特性控制 Awake 顺序。

using UnityEngine;

// Runs before other scripts (more negative = higher priority)
[DefaultExecutionOrder(-100)]
public class ServiceLocator : MonoBehaviour
{
  public static ServiceLocator Instance { get; private set; }
  private Dictionary<System.Type, MonoBehaviour> _services = new();

  void Awake()
  {
      Instance = this;
      // Auto-register every IService in the scene
      foreach (var svc in FindObjectsOfType<MonoBehaviour>())
          if (svc is IService)
              _services[svc.GetType()] = svc;
  }

  public T Get<T>() where T : MonoBehaviour =>
      _services.TryGetValue(typeof(T), out var s) ? s as T : null;
}

// Any script can fetch a service in Start:
// var gm = ServiceLocator.Instance.Get<GameManager>();

📌 快速记忆

  • Awake:准备自己——不需要别人
  • Start:连接场景——在所有 Awake 之后
  • 启用/禁用不会让 Start 再次运行
  • [DefaultExecutionOrder] 锁定顺序

⚠️ 常见错误

  • ❌ 在 Awake() 里访问 Singleton

    Singleton 可能尚未 Awake → NullReferenceException

    ✅ 在 Start() 中访问 Singleton

  • ❌ 在 Start() 中创建 Singleton

    其他脚本 Awake 结束时 Singleton 仍不存在 → null

    ✅ 始终在 Awake() 中创建 Singleton