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
Awake()
内部初始化 · 在 Start 之前
Start()
初始设置 · 第一帧
Update()
每帧逻辑 · Input、AI...
FixedUpdate()
物理 · 每 0.02s
LateUpdate()
所有 Update 之后 · Camera follow
OnEnable()
对象被激活时
OnDisable()
对象被禁用时
OnDestroy()
对象被销毁时
动手步骤
创建新的 C# 脚本
Assets → Create → C# Script。文件名必须与类名一致,否则会编译报错。
用 Awake 做内部初始化
用 Awake 缓存组件引用(_rb = GetComponent<Rigidbody>())。这里不要依赖其他对象。
用 Start 做跨对象初始化
需要访问其他对象时用 Start(它们的 Awake 此时已经跑完)。
区分 Update 与 FixedUpdate
输入和 AI → Update。物理力(AddForce、velocity)→ FixedUpdate,避免抖动。
在 OnDestroy 中清理
在 OnDestroy 中取消事件订阅、停止 Coroutine/定时器,防止泄漏。
交互模拟器
按 Play Scene,按顺序观察生命周期方法触发。试试 Disable 或 Destroy 看后续步骤。
Lifecycle Timeline
Console
点击 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>()