MonoBehaviour
MonoBehaviour là lớp cơ sở của mọi script Unity — cung cấp vòng đời (lifecycle), truy cập Component, và cầu nối giữa code C# của bạn với Unity Engine.
Hãy tưởng tượng...
MonoBehaviour giống như hợp đồng lao động với Unity. Khi ký hợp đồng (kế thừa MonoBehaviour), nhân viên (script) được tự động gọi vào các buổi họp định kỳ: họp khai mạc (Awake), họp ngày đầu (Start), họp hàng ngày (Update), họp vật lý (FixedUpdate), và họp kết thúc (OnDestroy). Không kế thừa = không được gọi, dù có định nghĩa method.
Khái niệm chi tiết
MonoBehaviour kế thừa từ
Behaviour → Component → Object. Việc kế thừa cho phép Unity gọi các
magic methods (Awake, Start, Update…) tự động
thông qua reflection — bạn không cần override hay gọi base method.
Lifecycle quan trọng:
Awake chạy ngay khi object được tạo (kể cả disabled),
Start chạy frame đầu sau khi object được enable,
Update mỗi frame, FixedUpdate theo Physics timestep (mặc định
0.02s).
Hiệu năng: Empty Update() vẫn tốn chi
phí gọi hàm qua reflection. Xóa Update rỗng. Dùng enabled = false để dừng
lifecycle thay vì destroy toàn bộ object.
Hạn chế: Không thể dùng constructor (
new MyScript()) — phải dùng AddComponent<T>(). Không
thread-safe — toàn bộ MonoBehaviour API chỉ chạy trên main thread.
Sơ đồ: Chuỗi kế thừa & Magic Methods
Awake()
Khởi tạo nội bộ · Trước Start
Start()
Setup ban đầu · Frame đầu tiên
Update()
Logic hàng frame · Input, AI...
FixedUpdate()
Physics · 0.02s/lần
LateUpdate()
Sau tất cả Update · Camera follow
OnEnable()
Khi object được kích hoạt
OnDisable()
Khi object bị vô hiệu hóa
OnDestroy()
Khi object bị hủy
Hướng dẫn thực hành
Tạo script C# mới
Assets → Create → C# Script. Đặt tên đúng với class name để tránh lỗi compile.
Dùng Awake cho khởi tạo nội bộ
Awake để cache component references (_rb = GetComponent<Rigidbody>()). Tránh phụ thuộc vào object khác ở đây.
Dùng Start cho setup liên-object
Start khi cần tham chiếu đến object khác (đảm bảo Awake của họ đã chạy xong).
Phân biệt Update vs FixedUpdate
Input và AI → Update. Lực vật lý (AddForce, velocity) → FixedUpdate để tránh glitch.
Dọn dẹp trong OnDestroy
Hủy subscription event, Coroutine, timer trong OnDestroy để tránh memory leak.
Trình mô phỏng tương tác
Nhấn Play Scene để xem thứ tự gọi lifecycle methods. Nhấn Disable hoặc Destroy để xem các bước tiếp theo.
Lifecycle Timeline
Console
Nhấn Play để bắt đầu...
Ví dụ Code
Cơ bảnCấu trúc MonoBehaviour chuẩn với đầy đủ lifecycle methods và lý do dùng từng cái.
using UnityEngine;
public class PlayerController : MonoBehaviour
{
[SerializeField] private float speed = 5f;
private Rigidbody _rb;
private Vector3 _inputDir;
void Awake()
{
// Cache component — không phụ thuộc script khác
_rb = GetComponent<Rigidbody>();
}
void Start()
{
// Setup cần đảm bảo Awake của GameManager đã chạy
GameManager.Instance.RegisterPlayer(this);
}
void Update()
{
// Đọc input mỗi frame
float h = Input.GetAxisRaw("Horizontal");
float v = Input.GetAxisRaw("Vertical");
_inputDir = new Vector3(h, 0, v).normalized;
}
void FixedUpdate()
{
// Di chuyển physics — không dùng Time.deltaTime ở đây
_rb.MovePosition(_rb.position + _inputDir * speed * Time.fixedDeltaTime);
}
void OnDestroy()
{
GameManager.Instance.UnregisterPlayer(this);
}
}Ví dụ Code
Nâng caoTối ưu hoá: tránh Update rỗng, dùng OnEnable/OnDisable thay Enable flag thủ công.
using UnityEngine;
using System;
// Component có lifecycle đầy đủ — kết hợp với 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 khi component được kích hoạt
GameEvents.OnDamageDealt += TakeDamage;
}
void OnDisable()
{
// Unsubscribe ngay — tránh gọi khi disabled
GameEvents.OnDamageDealt -= TakeDamage;
}
public void TakeDamage(int amount)
{
_currentHp = Mathf.Max(0, _currentHp - amount);
OnHealthChanged?.Invoke(_currentHp, maxHp);
if (_currentHp == 0) OnDied?.Invoke();
}
}📌 Ghi nhớ nhanh
- ▸Awake = nội bộ, Start = liên-object, Update = hàng frame
- ▸Physics → FixedUpdate, không Update
- ▸Empty Update() vẫn tốn chi phí — xóa nếu không dùng
- ▸OnEnable/OnDisable dùng để subscribe/unsubscribe event
⚠️ Lỗi thường gặp
❌ GetComponent trong Update()
Chậm — tìm kiếm component mỗi frame, rất tốn kém
✅ Cache trong Awake: _rb = GetComponent<Rigidbody>()
❌ Dùng constructor: new PlayerCtrl()
MonoBehaviour không cho phép dùng new — sẽ throw warning
✅ go.AddComponent<PlayerCtrl>()