Update & Lifecycle
Vòng lặp game chính của Unity: Update mỗi frame, FixedUpdate theo Physics timestep cố định, LateUpdate sau mọi Update — hãy chọn đúng hook cho từng việc.
Hãy tưởng tượng...
Update = đồng hồ kim chạy nhanh (tùy FPS) — dùng cho mắt thường thấy được: di chuyển, input, AI. FixedUpdate = đồng hồ điện tử nhịp đều (0.02s/lần) — dùng cho vật lý để không bị jitter. LateUpdate = chạy sau cùng — Camera đợi tất cả vật thể di chuyển xong mới đi theo, tránh giật hình.
Khái niệm chi tiết
Update() gọi mỗi frame — tần suất thay đổi theo FPS
(30, 60, 120…). Dùng Time.deltaTime để nhân với mọi giá trị di chuyển/tốc độ
— đảm bảo kết quả như nhau dù FPS khác nhau.
FixedUpdate() gọi theo Time.fixedDeltaTime
(mặc định 0.02s = 50 lần/giây). Không phụ thuộc FPS. Dùng cho:
Rigidbody.AddForce(), Rigidbody.velocity, Physics queries. Một
frame render có thể ứng với 0, 1, hoặc nhiều FixedUpdate.
LateUpdate() gọi sau tất cả Update() của mọi script trong frame đó. Lý tưởng cho Camera follow (đảm bảo player đã di chuyển), IK (Inverse Kinematics), và bất kỳ logic nào cần “đọc” kết quả sau Update.
OnGUI() cũ, Update vs coroutine: logic chạy một lần hoặc có delay → dùng Coroutine thay Update để tránh check điều kiện mỗi frame.
Sơ đồ: Thứ tự trong một Frame
FixedUpdate() × N
N = số lần timestep tích lũy từ frame trước — thường 0 hoặc 1, có thể nhiều hơn nếu lag
Update() — tất cả script
Input, AI, logic game, animation trigger
LateUpdate() — tất cả script
Camera follow, IK, post-Update logic
Hướng dẫn thực hành
Luôn nhân Time.deltaTime trong Update
transform.position += speed * Time.deltaTime * direction — tốc độ đơn vị/giây, không phụ thuộc FPS.
Physics forces → FixedUpdate
Đặt AddForce(), velocity trong FixedUpdate. Đọc Input trong Update rồi apply trong FixedUpdate.
Camera follow → LateUpdate
Camera script dùng LateUpdate để đảm bảo player đã move xong, tránh camera lag 1 frame.
Xóa Update không cần thiết
Ngay cả Update rỗng gây overhead. Dùng Event hoặc Coroutine cho logic không cần chạy mỗi frame.
Trình mô phỏng tương tác
Nhấn Run để xem từng bước trong vòng lặp frame. Điều chỉnh FPS và Physics rate.
FixedUpdate
0
calls
Update
0
calls
LateUpdate
0
calls
Render
0
frames
Ví dụ Code
Cơ bảnPhân chia đúng logic giữa Update, FixedUpdate và LateUpdate.
public class PlayerMovement : MonoBehaviour
{
[SerializeField] private float speed = 5f;
[SerializeField] private float jumpForce = 8f;
private Rigidbody _rb;
private Vector3 _moveDir;
private bool _jumpRequest;
void Awake() => _rb = GetComponent<Rigidbody>();
void Update()
{
// Input — đọc mỗi frame, lưu để FixedUpdate dùng
float h = Input.GetAxisRaw("Horizontal");
float v = Input.GetAxisRaw("Vertical");
_moveDir = new Vector3(h, 0, v).normalized;
if (Input.GetButtonDown("Jump")) _jumpRequest = true;
}
void FixedUpdate()
{
// Apply lực vật lý — không dùng Time.deltaTime ở đây
_rb.velocity = new Vector3(_moveDir.x * speed, _rb.velocity.y, _moveDir.z * speed);
if (_jumpRequest)
{
_rb.AddForce(Vector3.up * jumpForce, ForceMode.Impulse);
_jumpRequest = false;
}
}
}
public class CameraFollow : MonoBehaviour
{
[SerializeField] private Transform target;
[SerializeField] private Vector3 offset = new Vector3(0, 5, -8);
void LateUpdate()
{
// Chạy sau target.Update() — vị trí target đã finalize
transform.position = Vector3.Lerp(transform.position,
target.position + offset, Time.deltaTime * 5f);
}
}Ví dụ Code
Nâng caoDùng Coroutine thay Update cho logic không cần chạy mỗi frame.
public class EnemyAI : MonoBehaviour
{
private Transform _player;
private float _attackCooldown = 2f;
void Start()
{
_player = GameObject.FindWithTag("Player").transform;
// AI tick mỗi 0.2s thay vì mỗi frame → tiết kiệm CPU
StartCoroutine(AIUpdateLoop());
StartCoroutine(AttackLoop());
}
// Chạy logic AI 5 lần/giây thay vì 60 lần/giây
private IEnumerator AIUpdateLoop()
{
var wait = new WaitForSeconds(0.2f);
while (true)
{
UpdatePathfinding();
yield return wait;
}
}
private IEnumerator AttackLoop()
{
while (true)
{
yield return new WaitForSeconds(_attackCooldown);
if (Vector3.Distance(transform.position, _player.position) < 3f)
Attack();
}
}
}📌 Ghi nhớ nhanh
- ▸
* Time.deltaTimecho mọi di chuyển trong Update - ▸Physics force/velocity → FixedUpdate (không cần deltaTime)
- ▸Camera follow → LateUpdate (đợi player di chuyển xong)
- ▸Logic thưa hơn → Coroutine + WaitForSeconds
⚠️ Lỗi thường gặp
❌ Di chuyển transform trong FixedUpdate không nhân Time.fixedDeltaTime
Tốc độ phụ thuộc physics rate — không nhất quán
✅ Dùng * Time.fixedDeltaTime hoặc dùng MovePosition()
❌ Đọc Input trong FixedUpdate
GetKeyDown có thể bị bỏ lỡ nếu frame rất nhanh
✅ Đọc Input trong Update, lưu biến, apply trong FixedUpdate