Lập trình
Finite State Machine (FSM) và ứng dụng trong Unity

Giới thiệu
Trong lập trình game, Finite State Machine (FSM) hay Máy trạng thái hữu hạn là mô hình giúp mô tả hành vi phức tạp của nhân vật thông qua các trạng thái riêng biệt và chuyển đổi có điều kiện giữa chúng.
Thay vì viết hàng loạt if-else hoặc switch-case trong Update(), FSM giúp mã nguồn của bạn gọn gàng, dễ mở rộng và dễ bảo trì hơn rất nhiều.
Bài viết này sẽ hướng dẫn anh cách:
Hiểu rõ khái niệm và cấu trúc của FSM
Triển khai FSM trong Unity bằng C#
Áp dụng FSM để xây dựng hành vi AI của Enemy (Idle, Patrol, Chase)
Phần 1: Khái niệm cơ bản
FSM là gì?
FSM (Finite State Machine) là mô hình logic có một tập hữu hạn trạng thái (States).
Một đối tượng chỉ có thể ở một trạng thái tại một thời điểm, và chỉ khi điều kiện kích hoạt (transition condition) thỏa mãn, nó mới chuyển sang trạng thái khác.
Ví dụ trong game:
Enemy có 3 trạng thái:
Idle,Patrol,Chase.Khi người chơi tiến lại gần → chuyển từ
IdlesangChase.Khi mất tầm nhìn → quay về
Idle.
Ưu điểm của FSM
✅ Tách biệt logic rõ ràng
✅ Dễ mở rộng và tái sử dụng
✅ Dễ debug và kiểm soát hành vi nhân vật
✅ Tương thích tốt với hệ thống component của Unity

Phần 2: Cấu trúc và ví dụ code
Cấu trúc thư mục gợi ý:
Scripts/
├── FSM/
│ ├── State.cs
│ ├── StateMachine.cs
│ ├── IdleState.cs
│ ├── PatrolState.cs
│ └── ChaseState.cs
└── Enemy.cs
Base State Class
public abstract class State {
protected Enemy enemy;
protected StateMachine stateMachine;
public State(Enemy enemy, StateMachine stateMachine) {
this.enemy = enemy;
this.stateMachine = stateMachine;
}
public virtual void Enter() { }
public virtual void LogicUpdate() { }
public virtual void Exit() { }
}
State Machine Class
public class StateMachine {
public State CurrentState { get; private set; }
public void Initialize(State startState) {
CurrentState = startState;
CurrentState.Enter();
}
public void ChangeState(State newState) {
CurrentState.Exit();
CurrentState = newState;
CurrentState.Enter();
}
public void Update() {
CurrentState.LogicUpdate();
}
}
Enemy.cs
public class Enemy : MonoBehaviour {
public Transform player;
public float chaseRange = 5f;
private StateMachine stateMachine;
public IdleState idleState { get; private set; }
public PatrolState patrolState { get; private set; }
public ChaseState chaseState { get; private set; }
void Start() {
stateMachine = new StateMachine();
idleState = new IdleState(this, stateMachine);
patrolState = new PatrolState(this, stateMachine);
chaseState = new ChaseState(this, stateMachine);
stateMachine.Initialize(idleState);
}
void Update() {
stateMachine.Update();
}
}
Phần 3: Triển khai từng trạng thái
IdleState.cs
public class IdleState : State {
private float idleTime = 2f;
private float timer;
public IdleState(Enemy enemy, StateMachine stateMachine) : base(enemy, stateMachine) { }
public override void Enter() {
timer = 0;
Debug.Log("Enter Idle");
}
public override void LogicUpdate() {
timer += Time.deltaTime;
if (Vector3.Distance(enemy.transform.position, enemy.player.position) < enemy.chaseRange)
stateMachine.ChangeState(enemy.chaseState);
else if (timer > idleTime)
stateMachine.ChangeState(enemy.patrolState);
}
}
PatrolState.cs
public class PatrolState : State {
private Vector3 patrolPoint;
private float moveSpeed = 2f;
public PatrolState(Enemy enemy, StateMachine stateMachine) : base(enemy, stateMachine) { }
public override void Enter() {
patrolPoint = enemy.transform.position + new Vector3(Random.Range(-3, 3), 0, Random.Range(-3, 3));
Debug.Log("Enter Patrol");
}
public override void LogicUpdate() {
enemy.transform.position = Vector3.MoveTowards(enemy.transform.position, patrolPoint, moveSpeed * Time.deltaTime);
if (Vector3.Distance(enemy.transform.position, enemy.player.position) < enemy.chaseRange)
stateMachine.ChangeState(enemy.chaseState);
else if (Vector3.Distance(enemy.transform.position, patrolPoint) < 0.2f)
stateMachine.ChangeState(enemy.idleState);
}
}
ChaseState.cs
public class ChaseState : State {
private float moveSpeed = 3.5f;
public ChaseState(Enemy enemy, StateMachine stateMachine) : base(enemy, stateMachine) { }
public override void Enter() {
Debug.Log("Enter Chase");
}
public override void LogicUpdate() {
enemy.transform.position = Vector3.MoveTowards(
enemy.transform.position,
enemy.player.position,
moveSpeed * Time.deltaTime
);
if (Vector3.Distance(enemy.transform.position, enemy.player.position) > enemy.chaseRange + 2)
stateMachine.ChangeState(enemy.idleState);
}
}
Phần 4: Tổng kết & ứng dụng
Ưu điểm chính
Cấu trúc logic tách biệt rõ ràng
Dễ thêm hành vi mới mà không ảnh hưởng code cũ
Có thể tái sử dụng FSM cho nhiều đối tượng khác nhau
Ứng dụng thực tế
FSM được dùng phổ biến trong:
Enemy AI Behavior
Menu Navigation System
Quest/Dialogue System
Game State Management
Lưu ý: FSM là nền tảng, nhưng khi dự án phức tạp hơn, bạn có thể kết hợp với Behavior Tree hoặc Utility AI để đạt kết quả tối ưu hơn.
Kết luận
FSM là một mô hình tổ chức hành vi hiệu quả và dễ bảo trì trong Unity.
Dù bạn xây AI cho kẻ địch, nhân vật, hay menu, FSM luôn là công cụ đáng tin cậy để kiểm soát trạng thái và luồng logic.
🎯 “Control your states — or your states will control your game.”
Tài liệu tham khảo
Bài viết liên quan
Những câu chuyện tương tự mà bạn có thể hứng thú khám phá thêm.
Tôi bắt đầu làm game từ con số 0
Có lẽ ai cũng từng có một giấc mơ nào đó. Với tôi, giấc mơ ấy là tạo ra một trò chơi của riêng mình — một thế giới nhỏ nơi người khác có thể cười, có thể buồn, có thể tìm thấy một phần của chính họ. Nhưng có một vấn đề: tôi không biết gì cả. Không lập trình, không đồ họa, không âm thanh, thậm chí còn chưa hiểu nổi “engine game” là gì. Và thế là hành trình bắt đầu — từ con số 0 thật sự.
Hiểu rõ Dead Zone và Hard Limit trong Cinemachine Camera của Unity
Dead Zone và Hard Limit là hai khái niệm quan trọng trong Cinemachine giúp camera di chuyển tự nhiên, mượt mà và không vượt khỏi khu vực bản đồ. Bài viết này sẽ giúp bạn hiểu rõ cách hoạt động và thiết lập chúng hiệu quả trong game 2D.
Trò chuyện cùng nhau
Chia sẻ suy nghĩ của bạn về bài viết này.