# 🔁 Vòng lặp game và cấu trúc project Unity 2D

By Ngọc Vương Trương

## Vì sao hiểu vòng lặp game lại là việc đầu tiên

Đây là bài mở đầu của khóa, và cũng là bài đặt nền cho toàn bộ project capstone: một game 2D hoàn chỉnh mà bạn sẽ dựng dần qua từng bài, từ di chuyển, va chạm, tính điểm cho tới khi build lên Android và iOS. Mọi thứ bạn thêm vào sau này (nhân vật chạy, coin biến mất, điểm tăng, màn hình game over) đều chỉ là một mắt nhỏ trong cùng một vòng lặp: mỗi frame, Unity hỏi "có input mới không, cập nhật gì, vẽ lại gì?". Hiểu nhịp chạy đó thì bạn sẽ biết đặt code ở đâu và đặt lúc nào.

Sau bài này, bạn cần làm được:

- Giải thích được vòng lặp game gồm những bước nào và lặp liên tục ra sao.
- Phân biệt `Update`, `FixedUpdate`, `LateUpdate` và biết việc gì nên đặt ở hàm nào.
- Dùng `Time.deltaTime` để tốc độ di chuyển không phụ thuộc frame rate.
- Tạo được project Unity 2D với cấu trúc folder, scene và quy ước đặt tên rõ ràng.

## Một frame trong Unity diễn ra thế nào

Vòng lặp game (game loop) là chu trình chạy lặp đi lặp lại suốt thời gian game đang bật. Unity giấu phần lớn chi tiết bên trong engine, nhưng bạn có thể hình dung nó như sau:

```mermaid
flowchart TD
    A["📥<br/>Nhận input"] --> B["🧠<br/>Cập nhật logic<br/>Update"]
    B --> C["⚙️<br/>Cập nhật physics<br/>FixedUpdate"]
    C --> D["🎥<br/>Camera và UI<br/>LateUpdate"]
    D --> E["🖼️<br/>Render khung hình"]
    E --> A
```

Mỗi vòng như vậy tạo ra một frame. Số frame trong một giây (frame rate, fps) không cố định: máy yếu chạy 30 fps, máy mạnh chạy 120 fps. Đây là lý do quan trọng nhất khiến bạn phải cẩn thận với thời gian, và cũng là lý do bài này có phần `deltaTime`.

> **Note:** 
> Đừng bao giờ giả định "mỗi frame là 1/60 giây". Đó là giả định đúng trên máy của bạn và sai trên máy người chơi.

## Update, FixedUpdate và vòng đời của một script

Một script trong Unity không chạy từ trên xuống một lần rồi thôi. Nó được engine gọi qua các hàm vòng đời (lifecycle callbacks) theo đúng thứ tự dưới đây:

| Hàm | Khi nào chạy | Dùng cho việc gì |
| --- | --- | --- |
| `Awake` | Một lần, trước `Start`, kể cả khi script đang bị disable | Cache reference, khởi tạo dữ liệu nội bộ |
| `OnEnable` | Mỗi lần object được bật | Đăng ký event |
| `Start` | Một lần, ngay trước frame đầu tiên có `Update` | Đọc giá trị của object khác đã `Awake` xong |
| `FixedUpdate` | Theo timestep cố định, mặc định 0.02 giây (50 lần/giây) | Di chuyển `Rigidbody2D`, lực, va chạm |
| `Update` | Mỗi frame, đúng một lần | Đọc input, logic game, đếm thời gian |
| `LateUpdate` | Mỗi frame, sau tất cả `Update` | Camera follow, cập nhật UI theo vị trí nhân vật |
| `OnDestroy` | Khi object bị huỷ | Huỷ đăng ký, dọn dẹp |

Quy tắc dễ nhớ: **input và logic trong `Update`, physics trong `FixedUpdate`, camera trong `LateUpdate`.** Nếu đặt code di chuyển `Rigidbody2D` vào `Update`, nhân vật sẽ rung khi frame rate dao động, vì physics chỉ đồng bộ ở timestep cố định.

**Bài tập nhanh 1.** Bạn viết code làm coin xoay trong `Update` và code đẩy nhân vật bằng `Rigidbody2D.AddForce` trong `Update`. Chỗ nào sai?
Gợi ý: xoay coin bằng `transform` trong `Update` thì ổn, nhưng `AddForce` phải nằm trong `FixedUpdate`.

## deltaTime: cách để tốc độ không phụ thuộc frame rate

`Time.deltaTime` là thời gian (tính bằng giây) của frame vừa kết thúc. Nếu bạn muốn nhân vật đi 5 unit mỗi giây, đừng cộng 5 vào vị trí mỗi frame, hãy nhân với `Time.deltaTime`:

```csharp
void Update()
{
    // 5 unit/giây, bất kể máy chạy 30 hay 120 fps
    transform.position += Vector3.right * 5f * Time.deltaTime;
}
```

Với frame dài 0.1 giây, nhân vật dịch 0.5 unit; với frame 0.016 giây, nhân vật dịch 0.08 unit. Cộng lại trong một giây, cả hai đều ra 5 unit. Đó chính là "độc lập frame rate".

Vài chi tiết đi kèm:

- Trong `FixedUpdate`, dùng `Time.fixedDeltaTime` (hoặc để Unity tự lo cho physics), không nhân `deltaTime`.
- `Time.timeScale` làm chậm hoặc dừng game và `Time.deltaTime` cũng bị ảnh hưởng theo. Menu pause muốn chạy thật thì dùng `Time.unscaledDeltaTime`.
- Đừng nhân `deltaTime` hai lần trong cùng một phép tính, đó là lỗi rất hay gặp khiến nhân vật bò rất chậm.

**Bài tập nhanh 2.** Đồng hồ đếm ngược của màn chơi nên trừ thời gian trong `Update` bằng `Time.deltaTime` hay `Time.fixedDeltaTime`? Vì sao?
Gợi ý: dùng `Time.deltaTime` trong `Update`, vì đồng hồ là logic game, không phải physics.

## Dựng cấu trúc project 2D gọn gàng

Một project lộn xộn sẽ làm bạn mất thời gian đúng lúc cần nhất: lúc debug. Ngay từ đầu, hãy chọn template **2D** khi tạo project trong Unity Hub, rồi giữ cấu trúc folder như sau trong thư mục `Assets`:

```tree
Assets/
  Scenes/
    Game.unity
  Scripts/
    Player/
      PlayerController.cs
    Core/
      GameManager.cs
  Prefabs/
    Player.prefab
    Coin.prefab
  Art/
    Sprites/
  Audio/
```

Ba quy ước nên theo ngay từ frame đầu tiên:

1. **Mỗi loại asset một folder.** `Scenes`, `Scripts`, `Prefabs`, `Art`, `Audio`. Sau này thêm `UI` hay `Settings` cũng theo đúng mẫu đó.
2. **Prefab cho object lặp lại.** Nhân vật, coin, chướng ngại vật đều nên là prefab, vì bạn sẽ spawn chúng nhiều lần và muốn sửa một chỗ áp dụng cho tất cả.
3. **Chỉ thao tác file bên trong Unity.** Mỗi asset đi kèm một file `.meta` giữ thông tin liên kết. Di chuyển hay đổi tên file bằng File Explorer bên ngoài sẽ làm đứt liên kết đó, và bạn sẽ thấy các ô `Missing` trong Inspector. Dùng cửa sổ Project của Unity là an toàn.

**Bài tập nhanh 3.** Bạn tạo một sprite đồng xu mới. Nó nên nằm ở đâu, và khi nào thì tạo thêm prefab từ nó?
Gợi ý: ảnh gốc để trong `Art/Sprites/`, còn prefab `Coin.prefab` trong `Prefabs/` khi bạn cần gắn script, collider và spawn nó nhiều lần trong scene.

## Bốn lỗi làm hỏng vòng lặp và cấu trúc project

- **Quên `Time.deltaTime`.** Triệu chứng: game chạy nhanh trên máy mạnh, chậm trên máy yếu. Đây là lỗi số một của người mới.
- **Nhân `deltaTime` hai lần.** Triệu chứng: nhân vật di chuyển chậm bất thường, kiểu 5 unit/giây thành 0.08 unit/giây.
- **Đặt physics vào `Update`.** Triệu chứng: nhân vật rung, đôi khi xuyên tường khi frame rate tụt. Chuyển sang `FixedUpdate`.
- **Đổi tên hoặc di chuyển asset ngoài Unity.** Triệu chứng: hàng loạt reference thành `Missing`. Sửa được nhưng rất mất thời gian, nên tránh từ đầu.

Một mẹo nhỏ: khi cần kiểm tra tốc độ, bật chế độ xem fps trong Game view và thử chạy game ở độ phân giải khác. Nếu cảm giác di chuyển vẫn như cũ, tức là bạn đã dùng `deltaTime` đúng.

## Chốt lại và chuẩn bị cho bài di chuyển nhân vật

Bạn vừa nắm được nhịp tim của mọi game Unity: vòng lặp frame, thứ tự các hàm vòng đời, và cách dùng `Time.deltaTime` để mọi thứ chạy đúng trên mọi thiết bị. Cộng thêm một cấu trúc project gọn gàng, bạn đã có nền đủ chắc để bắt đầu viết gameplay thật.

Điền nhanh để tự kiểm tra:

Unity gọi hàm _____ mỗi frame, còn physics được cập nhật trong _____.
Muốn chuyển động độc lập frame rate, ta nhân tốc độ với _____.
Fixed timestep mặc định của Unity là _____ giây, tức 50 lần mỗi giây.
Camera follow nên đặt trong _____ để chạy sau khi nhân vật đã di chuyển.
Mỗi asset trong Unity đi kèm một file _____ giữ thông tin liên kết.

Bài tiếp theo sẽ dùng chính `Update` và `deltaTime` vừa học để đọc input và cho nhân vật di chuyển, bước đầu tiên của gameplay trong project capstone.

Front: Vòng lặp game là gì?
Back: Chu trình lặp liên tục: nhận input, cập nhật trạng thái, render, rồi lặp lại.

Front: Hàm Update chạy khi nào?
Back: Mỗi frame, đúng một lần.

Front: FixedUpdate khác Update ở điểm nào?
Back: FixedUpdate chạy theo timestep cố định và là nơi xử lý physics như Rigidbody2D.

Front: Time.deltaTime là gì?
Back: Thời gian tính bằng giây của frame vừa kết thúc.

Front: Vì sao phải nhân tốc độ với Time.deltaTime?
Back: Để tốc độ di chuyển không phụ thuộc frame rate của thiết bị.

Front: Thứ tự giữa Awake và Start?
Back: Awake chạy trước Start; Awake vẫn chạy khi script bị disable, Start thì không.

Front: Hàm nào phù hợp để camera follow nhân vật?
Back: LateUpdate, vì nó chạy sau khi mọi Update đã cập nhật vị trí.

Front: Time.timeScale ảnh hưởng đến điều gì?
Back: Làm chậm hoặc dừng thời gian game và kéo theo Time.deltaTime; Time.unscaledDeltaTime thì không bị ảnh hưởng.

Front: Fixed timestep mặc định của Unity là bao nhiêu?
Back: 0.02 giây, tức 50 lần mỗi giây.

Front: Unity lưu metadata của asset ở đâu?
Back: Trong file .meta đi kèm mỗi asset.

Front: Vì sao không nên đổi tên hay di chuyển asset ngoài Unity?
Back: File .meta không được cập nhật, các reference trong scene và prefab sẽ thành Missing.

Front: Prefab dùng để làm gì?
Back: Làm template tái sử dụng cho object lặp lại như player, coin, chướng ngại vật.

Front: Nhân vật speed 5 unit/giây, một frame dài 0.1 giây thì dịch được bao nhiêu?
Back: 0.5 unit, vì 5 nhân 0.1.

Front: Code đọc input nên đặt ở hàm nào?
Back: Update, cùng với phần lớn logic game.

---

## Contents

Part of [🕹️ Unity cho người mới: tự làm game 2D và đóng gói lên iOS, Android](https://everwise.study/courses/unity-cho-nguoi-moi-tu-lam-game-2d-va-ong-goi-len-ios-android.md)

- **🎮 Gameplay 2D cốt lõi trong Unity**
  - [🔁 Vòng lặp game và cấu trúc project Unity 2D](https://everwise.study/courses/unity-cho-nguoi-moi-tu-lam-game-2d-va-ong-goi-len-ios-android/vong-lap-game-va-cau-truc-project-unity-2d.md) — this lesson
  - [🕹️ Nhận input và di chuyển nhân vật](https://everwise.study/courses/unity-cho-nguoi-moi-tu-lam-game-2d-va-ong-goi-len-ios-android/nhan-input-va-di-chuyen-nhan-vat.md) (sign-in required)
  - [🧱 Capstone: dựng prototype với va chạm và tính điểm](https://everwise.study/courses/unity-cho-nguoi-moi-tu-lam-game-2d-va-ong-goi-len-ios-android/capstone-prototype-gameplay-2d.md)
  - [✅ Checkpoint: gameplay 2D cốt lõi](https://everwise.study/courses/unity-cho-nguoi-moi-tu-lam-game-2d-va-ong-goi-len-ios-android/checkpoint-gameplay-2d-cot-loi.md)
- **📦 Hoàn thiện và đóng gói game capstone**
  - [⚖️ Cân bằng độ khó và nhịp độ chơi](https://everwise.study/courses/unity-cho-nguoi-moi-tu-lam-game-2d-va-ong-goi-len-ios-android/can-bang-do-kho-va-nhip-do-choi.md)
  - [🏁 Capstone: hoàn thiện vòng chơi, UI điểm và game over](https://everwise.study/courses/unity-cho-nguoi-moi-tu-lam-game-2d-va-ong-goi-len-ios-android/capstone-hoan-thien-vong-choi-ui-diem-game-over.md) (premium content)
  - [📱 Build bản chơi thử trên Android và iOS](https://everwise.study/courses/unity-cho-nguoi-moi-tu-lam-game-2d-va-ong-goi-len-ios-android/build-ban-choi-thu-android-ios.md)
  - [✅ Checkpoint: hoàn thiện và đóng gói](https://everwise.study/courses/unity-cho-nguoi-moi-tu-lam-game-2d-va-ong-goi-len-ios-android/checkpoint-hoan-thien-va-dong-goi.md)
- [Course cheatsheet](https://everwise.study/courses/unity-cho-nguoi-moi-tu-lam-game-2d-va-ong-goi-len-ios-android/cheatsheet.md) (premium content)
