Vòng lặp game (Game loop) hoạt động thế nào?

Loop_Game-chipPhần cốt lõi của hầu hết các game chính là vòng lặp được dùng để cập nhật và hiển thị trạng thái của game. Trong bài viết này, tôi sẽ minh họa các phương pháp tạo vòng lặp game với ngôn ngữ javascript.

Xem Demo.

Vòng lặp cơ bản

Một vòng lặp game cơ bản bao gồm các việc được thực hiện theo thứ tự sau:

while(gameRunning)
{
	processInput(); // keyboard, mouse,...
	updateGame();
	draw();
	// checkGameOver();
}

Minh họa:

Basic Game Loop
Trong javascript, thay vì dùng vòng lặp, ta sẽ thay thế bằng setInterval() hoặc setTimeout(). Thông thường bạn chỉ cần xác định một giá trị interval thích hợp theo tốc độ của game.

function gameLoop()
{
	processInput();
	updateGame();
	draw();
	setTimeout(gameLoop,100); // 10 fps
}

Hợp lý hơn khi bạn muốn xác định rõ số khung hình/giây (fps):

const FPS = 60;
function gameLoop()
{
	// …
}
window.setInterval(gameLoop,1000/FPS);

Vòng lặp có tính toán thời gian

Tuy nhiên, không phải lúc nào công việc update và draw cũng hoàn thành trước khi lần gọi kế tiếp được thực hiện. Như vậy tốc độ của game sẽ không đồng nhất trên các thiết bị có cấu hình khác nhau.

Để giải quyết, ta sẽ sử dụng đến thời gian hệ thống để so sánh và quyết định thời điểm update/draw.

const FPS = 60;
const TICKS = 1000/FPS;
var lastUpdateTime;

function gameLoop()
{
	var now = Date.now();
	var diffTime = now - lastUpdateTime;
	if(diffTime >= TICKS)
		processInput();
		updateGame();
		lastUpdateTime = now;
	}
	draw();
	var sleepTime = TICKS - diffTime;
	if(sleepTime<0)
		sleepTime = 0;
	setTimeout(gameLoop,sleepTime);
}

Phương pháp trên chạy ổn với giá trị diffTime nhỏ hơn TICKS. Nghĩa là tốc độ của game không vượt quá giá trị TICKS cho phép. Tuy nhiên trong trường hợp diffTime lớn, việc cập nhật sẽ diễn ra chậm.

Giải pháp cuối cùng

Giải pháp của ta là sẽ thực hiện update với số lần dựa vào tỉ lệ diffTime/TICKS trong một lần lặp của game. Sẽ hiệu quả hơn nếu ta bỏ qua việc draw và thực hiện update liên tiếp vì sẽ giúp tăng tốc độ game để bù vào khoảng thời gian bị trì hoãn.

const FPS = 60;
const TICKS = 1000/FPS;

var lastUpdateTime;

function gameLoop()
{
	var diffTime = Date.now() - lastUpdateTime;
	var numOfUpdate = Math.floor(diffTime/TICKS);
	for(var i = 0;i < numOfUpdate;i++){
		processInput();
		updateGame();
	}
	if(diffTime >= TICKS)
		draw();

	lastUpdateTime += TICKS * numOfUpdate;
	diffTime -= TICKS * numOfUpdate;
	var sleepTime = TICKS - diffTime;
	setTimeout(gameLoop,sleepTime);
}

Nếu bạn sử dụng requestAnimationFrame cho vòng lặp game, bạn sẽ không cần quan tâm đến việc tính toán giá trị sleepTime.

Ví dụ hoàn chỉnh

Kiểm tra ví dụ này và so sánh với các phương pháp trước đó, thực hiện một vài công việc “quá tải” nào đó trên trình duyệt và bạn sẽ thấy khác biệt:

const FPS = 6;
const TICKS = 1000 / FPS;
var startTime;
var expectedCounter = 0;
var lastUpdateTime;
var actualCounter = 0;
var output;

function gameLoop()
{
    var diffTime = Date.now() - lastUpdateTime;
    var numOfUpdate = Math.floor(diffTime/TICKS);
    for(var i = 0;i < numOfUpdate;i++){
        updateGame();

    }
    if(diffTime >= TICKS)
        draw();

    lastUpdateTime += TICKS * numOfUpdate;
    diffTime -= TICKS * numOfUpdate;
    var sleepTime = TICKS - diffTime;
    setTimeout(gameLoop,sleepTime);
}

function updateGame() {
    actualCounter++;
}

function draw() {
    var s = "Actual updates: "+actualCounter;
    s += "<br/>Expected updates: "+Math.floor((Date.now()-startTime)/TICKS);
    output.innerHTML = s;
}
// onLoad
output = document.getElementById("output");
startTime = Date.now();
lastUpdateTime = startTime;
gameLoop();
 

Output:

Actual updates: 1323
Expected updates: 1323

YinYang’s Blog

17 bình luận về “Vòng lặp game (Game loop) hoạt động thế nào?

  1. FPS (Frames per second) là số khung hình được vẽ (hoặc được tạo ra, …) trong một giây. Hiện tại giá trị thường được sử dụng là 60fps, tương ứng với tốc độ refersh tối thiểu của các loại màn hình (60Hz).
    Số lượng frame được tạo ra cần xấp xỉ với tốc độ vẽ để đảm bảo tốc độ hiển thị trên màn hình là không quá nhanh hoặc chậm. Ví dụ khi bạn coi lại các phim của vua hề Chaplin sẽ thấy tốc độ chạy nhanh hơn bình thường. Do các camera thời đó chỉ có thể quay với tốc độ khá chậm (khoảng 14-24 fps) nhưng lại được chiếu với các máy chiếu có tốc độ cao hơn từ 2 -> 3 lần.

  2. Khi lập trình bạn sẽ gặp nhiều đoạn mã sử dụng const. Vậy bạn có thể đặt câu hỏi như sau: “Tại sao người ta định nghĩa một const mà ko sử dụng trực tiếp giá trị của nó khi tính toán?”. Bạn tự tìm hiểu xem sao nhé.

  3. Em chào anh.
    Em muốn tìm hiểu về html5 nhưng có lẽ cái bắt đầu vẫn là cái khó khăn nhất.
    Vậy anh có tài liệu nào cơ bản thì có thể share cho em được không vậy.
    Thêm nữa là nếu code html5 thì dùng SDK nào vậy anh(em muốn dùng VS2010 để viết).
    Mong anh hồi âm cảm ơn anh nhiều.

  4. Mình ko có tài liệu nào cả, chủ yếu là google thôi. Còn IDE thì mình xài notepad++, bạn có thể xài bất kì cái nào cũng được vì mình cũng chưa từng code bằng các IDE dành riêng cho javascript.

  5. Có, bạn có thể lên mạng tìm hiểu về WebSocket trong HTML5. Hiện tại thì cũng có một số server được viết sẵn (PHP, C#,…) nhưng bạn có thể tự viết 1 server để dùng cho những công việc cụ thể.

  6. Hii lại phải làm phiền anh 1 chút.
    Em đã thử làm server websocket nhưng không được
    Anh có đoạn code mẫu nào bằng C# không.
    Em có send lại key accept rồi mà client send data bên socket cũng không nhận, mà bên socket send data thì bên client cũng không nhận.\
    Đây là link code server anh xem giúp em với.(em dùng chrome để connect đến)
    http://www.mediafire.com/?afggk678rga03wn

Đã đóng bình luận.