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() ✅
动手步骤
Awake:自我初始化
缓存 GetComponent<T>()、初始化私有字段、搭建 Singleton。不要调用其他对象。
Start:连接外部世界
使用 GameManager.Instance、FindObjectOfType<T>()、注册事件。
需要时使用 Script Execution Order
Edit → Project Settings → Script Execution Order → 把脚本拖得更高,让它更早运行。
避免跨脚本空引用
若 ScriptA.Awake 访问尚未 Awake 的 ScriptB,会得到 null。跨脚本访问放在 Start。
交互模拟器
向场景添加脚本并按 Load Scene,观察每个脚本先触发 Awake 再触发 Start。
Scripts in Scene
Execution Log
添加脚本后点击 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