Unity Term Book
脚本与生命周期

MonoBehaviour

MonoBehaviour 是每个 Unity 脚本的基类,提供生命周期钩子、Component 访问,以及 C# 代码与引擎之间的桥梁。几乎所有玩法逻辑都挂在它上面;理解 Awake、Start、Update 与启用禁用行为,是写可靠组件、协作 Prefab 变体以及调试场景问题的必备入门起点知识。

想象一下...

MonoBehaviour 就像一份与 Unity 签订的雇佣合同。一旦你签字(继承 MonoBehaviour),员工(你的脚本)就会被自动邀请参加定期会议:开工(Awake)、第一天(Start)、每日站会(Update)、物理同步(FixedUpdate),以及离职面谈(OnDestroy)。没有继承 = 永远不会被调用,即使你定义了这些方法。

概念详解

MonoBehaviour 继承 Behaviour → Component → Object。这条继承链让 Unity 能通过 reflection 调用 magic methods(Awake、Start、Update…)——你不需要 override,也不需要调用基类方法。

关键生命周期: Awake 在对象创建时立刻运行(即使被禁用), Start 在对象启用后的第一帧运行, Update 每帧运行,FixedUpdate 按 Physics 时间步运行(默认 0.02s)。

性能:空的 Update() 仍会 产生一次 reflection 调用。删除空的 Update。用 enabled = false 暂停 生命周期,而不是销毁整个对象。

限制:不能使用构造函数( new MyScript())——请用 AddComponent<T>()。非线程安全—— MonoBehaviour API 只能在主线程上运行。

示意图:继承链与 magic methods

Object
Component
Behaviour
MonoBehaviour
YourScript

Awake()

内部初始化 · 在 Start 之前

Start()

初始设置 · 第一帧

Update()

每帧逻辑 · Input、AI...

FixedUpdate()

物理 · 每 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 中清理

OnDestroy 中取消事件订阅、停止 Coroutine/定时器,防止泄漏。

交互模拟器

按 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>()