Explore all courses in this category.
Explore all courses in this category.
Nguyễn Đình Cường
0 comment
09 Nov, 2026
Game rắn săn mồi là bài tập tuyệt vời để hiểu cách một trò chơi hoạt động: vòng lặp game, trạng thái, nhập liệu và phát hiện va chạm. Bài này hướng dẫn bạn làm game hoàn chỉnh với thẻ canvas của HTML5, không cần thư viện nào.
Mọi game đều xoay quanh một vòng lặp lặp đi lặp lại: cập nhật trạng thái (con rắn đi một bước, kiểm tra ăn mồi, kiểm tra thua) rồi vẽ lại màn hình. Với rắn săn mồi, trạng thái gồm: danh sách các ô thân rắn, hướng đi, vị trí con mồi và điểm số. Ta chia màn hình thành lưới ô vuông, nên mọi vị trí chỉ là cặp số nguyên (x, y).
<canvas id="game" width="400" height="400"></canvas>
<p>Điểm: <span id="score">0</span></p>canvas { display: block; margin: 20px auto; background: #0f172a; border-radius: 8px; }
p { text-align: center; font-family: system-ui, sans-serif; }const canvas = document.getElementById('game');
const ctx = canvas.getContext('2d');
const scoreEl = document.getElementById('score');
const TILE = 20; // kích thước mỗi ô
const COLS = canvas.width / TILE; // 20 cột
const ROWS = canvas.height / TILE; // 20 hàng
let snake, direction, nextDirection, food, score, timer, running;
function reset() {
snake = [{ x: 10, y: 10 }, { x: 9, y: 10 }, { x: 8, y: 10 }];
direction = { x: 1, y: 0 };
nextDirection = direction;
score = 0;
scoreEl.textContent = score;
placeFood();
running = true;
}
function placeFood() {
while (true) {
const pos = {
x: Math.floor(Math.random() * COLS),
y: Math.floor(Math.random() * ROWS),
};
// tránh đặt mồi lên thân rắn
if (!snake.some((s) => s.x === pos.x && s.y === pos.y)) {
food = pos;
return;
}
}
}Mỗi nhịp, ta tính vị trí đầu mới bằng cách cộng hướng đi, thêm đầu mới vào đầu mảng. Nếu ăn mồi thì giữ nguyên đuôi (rắn dài ra), nếu không thì bỏ ô cuối (rắn dịch chuyển).
function update() {
direction = nextDirection;
const head = {
x: snake[0].x + direction.x,
y: snake[0].y + direction.y,
};
// Va chạm tường
if (head.x < 0 || head.x >= COLS || head.y < 0 || head.y >= ROWS) {
return gameOver();
}
// Va chạm thân
if (snake.some((s) => s.x === head.x && s.y === head.y)) {
return gameOver();
}
snake.unshift(head);
if (head.x === food.x && head.y === food.y) {
score++;
scoreEl.textContent = score;
placeFood();
} else {
snake.pop();
}
}
function gameOver() {
running = false;
clearInterval(timer);
ctx.fillStyle = 'rgba(0,0,0,0.6)';
ctx.fillRect(0, 0, canvas.width, canvas.height);
ctx.fillStyle = '#fff';
ctx.font = '24px system-ui';
ctx.textAlign = 'center';
ctx.fillText('Thua rồi! Nhấn Space để chơi lại', canvas.width / 2, canvas.height / 2);
}function draw() {
ctx.clearRect(0, 0, canvas.width, canvas.height);
ctx.fillStyle = '#ef4444';
ctx.fillRect(food.x * TILE, food.y * TILE, TILE - 2, TILE - 2);
snake.forEach((part, i) => {
ctx.fillStyle = i === 0 ? '#22c55e' : '#4ade80';
ctx.fillRect(part.x * TILE, part.y * TILE, TILE - 2, TILE - 2);
});
}
function tick() {
update();
if (running) draw();
}Một lỗi kinh điển: nếu người chơi bấm hai phím rất nhanh trong cùng một nhịp (ví dụ phải rồi lên rồi trái), rắn có thể quay đầu 180 độ và tự cắn mình. Cách tránh là so sánh với hướng đã thực sự dùng ở nhịp trước (biến direction) chứ không phải hướng vừa bấm.
document.addEventListener('keydown', (e) => {
const keys = {
ArrowUp: { x: 0, y: -1 },
ArrowDown: { x: 0, y: 1 },
ArrowLeft: { x: -1, y: 0 },
ArrowRight: { x: 1, y: 0 },
};
if (keys[e.key]) {
e.preventDefault();
const d = keys[e.key];
// không cho quay ngược lại
if (d.x !== -direction.x || d.y !== -direction.y) {
nextDirection = d;
}
} else if (e.code === 'Space' && !running) {
start();
}
});
function start() {
reset();
clearInterval(timer);
timer = setInterval(tick, 120);
draw();
}
start();setInterval với chu kỳ ngắn hơn.localStorage.requestAnimationFrame với bộ đếm thời gian để chuyển động mượt hơn.Chưa đầy 100 dòng, bạn đã có một game đầy đủ vòng lặp, nhập liệu, va chạm và điểm số. Cấu trúc "trạng thái, cập nhật, vẽ" bạn vừa dùng là xương sống của game ở mọi nền tảng, từ canvas đến Unity.
Nếu muốn chuyển từ game web đơn giản sang làm game chuyên nghiệp hơn, hãy xem các khóa học tại codebem.com.
Nguyễn Đình Cường
0 comment