Component
Các khối chức năng độc lập gắn vào GameObject — mỗi Component định nghĩa một khía cạnh hành vi, giúp xây dựng game theo nguyên tắc Composition over Inheritance.
Hãy tưởng tượng...
Component giống như module phụ kiện trên xe đua. Thân xe (GameObject) tự nó không làm gì — nhưng khi gắn vào động cơ (Rigidbody), bánh xe (Collider), hệ thống lái (Script) và đèn (Light), xe bắt đầu có hành vi hoàn chỉnh. Quan trọng là mỗi module hoạt động độc lập, tháo đèn ra không ảnh hưởng động cơ.
Khái niệm chi tiết
Component là trái tim của kiến trúc Unity. Thay vì tạo cây kế thừa khổng lồ, Unity khuyến khích kết hợp các component nhỏ độc lập — Composition over Inheritance.
Mọi Component kế thừa từ UnityEngine.Component, nên đều có thể truy cập
gameObject (object chứa nó) và transform.
Để lấy tham chiếu: GetComponent<T>(). Tìm ở Children:
GetComponentInChildren<T>(). Tìm ở Parent:
GetComponentInParent<T>().
Kỹ năng quan trọng: biết
khi nào nên tách script lớn thành nhiều Component nhỏ. Thay vì
PlayerController 500 dòng → chia thành PlayerMovement,
PlayerCombat, PlayerHealth.
Composition vs Inheritance
❌ Inheritance (Phức tạp)
✅ Composition (Unity)
Hướng dẫn thực hành
Gắn Component từ Inspector
Chọn GO → Add Component → tìm tên (Rigidbody, Audio Source...).
Cache bằng GetComponent trong Awake()
_rb = GetComponent<Rigidbody>(); lưu vào biến private.
Bật/tắt Component
component.enabled = false/true — không xóa, chỉ tạm vô hiệu.
Dùng Interface để giao tiếp lỏng lẻo
Tránh phụ thuộc trực tiếp giữa các script — dùng IDamageable, IInteractable...
Trình mô phỏng tương tác
Bật/tắt các Component để xem nhân vật thay đổi hành vi. Quan sát mô tả theo tổ hợp đang bật.
Chọn Components để gắn vào GameObject "Player"
Hành vi hiện tại
Ví dụ Code
Cơ bảnusing UnityEngine;
public class ComponentBasics : MonoBehaviour
{
// Cache: lấy component 1 lần, không lấy mỗi frame
private Rigidbody _rb;
private Collider _col;
private Renderer _renderer;
void Awake()
{
_rb = GetComponent<Rigidbody>();
_col = GetComponent<Collider>();
_renderer = GetComponent<Renderer>();
}
void Start()
{
// Kiểm tra null trước khi dùng (an toàn)
if (_rb != null) _rb.mass = 2f;
_renderer.enabled = false; // ẩn vật thể
_col.enabled = false; // tắt va chạm
}
public void MakeInvincible()
{
// Tắt script AI (không xóa component)
EnemyAI ai = GetComponent<EnemyAI>();
if (ai != null) ai.enabled = false;
}
}Ví dụ Code
Nâng caousing UnityEngine;
// Interface: "hợp đồng" cho mọi component có thể nhận damage
public interface IDamageable
{
void TakeDamage(int amount);
}
// Component Health: chỉ quản lý máu, không biết về combat
public class Health : MonoBehaviour, IDamageable
{
[SerializeField] private int maxHP = 100;
private int _currentHP;
void Awake() => _currentHP = maxHP;
public void TakeDamage(int amount)
{
_currentHP -= amount;
if (_currentHP <= 0) Destroy(gameObject);
}
}
// Component Bullet: gây damage mà không cần biết Health tồn tại
public class Bullet : MonoBehaviour
{
[SerializeField] private int damage = 25;
void OnCollisionEnter(Collision col)
{
// Hỏi: object này có IDamageable không? → loose coupling
col.gameObject.GetComponent<IDamageable>()?.TakeDamage(damage);
Destroy(gameObject);
}
}📌 Ghi nhớ nhanh
- ▸Component = module độc lập gắn vào GameObject
- ▸Dùng Composition (nhiều component nhỏ) thay vì kế thừa dài
- ▸Luôn cache bằng biến private trong
Awake() - ▸Dùng Interface để components giao tiếp không phụ thuộc trực tiếp
⚠️ Lỗi thường gặp
❌ Một script làm quá nhiều việc
Script 500 dòng, quản lý cả di chuyển, máu, súng, UI
✅ Tách thành PlayerMovement, PlayerHealth, PlayerWeapon
❌ GetComponent<>() trong Update()
NullReferenceException nếu component chưa gắn
✅ Cache trong Awake() và kiểm tra null trước khi dùng