Ты когда-нибудь мечтал написать свою игру? А как насчёт той самой классики — Змейки? 😎 Сегодня мы с тобой шаг за шагом создадим настоящую ретро-игру прямо в браузере. Без библиотек, без фреймворков — только HTML, CSS и JavaScript.
⚠️ Спойлер: ты не просто запустишь змейку, ты поймёшь, как она работает.
А если вдруг тебе покажется, что это сложно — спокойствие, только спокойствие, всё объясним с мемами и примерами. Поехали! 🚀
✨ Демо проекта | 📂 Скачать исходный код на GitHub
🤖 В приложении Кодик вы можете изучить HTML, CSS и JavaScript, а также много других курсов, чтобы начать свой путь в веб-разработке! 🌱
HTML — это скелет. В нём мы размещаем:
canvas
— наш холст, на котором будем рисовать змейку и еду;
div
с кнопками для начала и конца игры;
счётчик очков.
<canvas id="game" width="320" height="320"></canvas>
Немного магии стилей:
чёрный фон
канвас с рамкой и закруглёнными краями
адаптация под мобильные устройства (touch-action)
Кнопки, заголовки, экраны старта и окончания тоже стилизованы, чтобы игра выглядела приятно.
Теперь подробно разберём весь код игры.
const canvas = document.getElementById("game");
const ctx = canvas.getContext("2d");
canvas
— получаем доступ к элементу канваса. ctx
— это то, с помощью чего мы будем рисовать.
Также мы объявляем переменные: размер клетки, направление змеи, очки, скорость и состояние игры.
const scale = 20;
const rows = canvas.height / scale;
const columns = canvas.width / scale;
let direction = { x: 1, y: 0 };
let nextDirection = { x: 1, y: 0 };
let speed = 250;
let score = 0;
class Snake {
constructor() {
this.body = [
{ x: 5, y: 5 },
{ x: 4, y: 5 },
];
}
update() {
const head = { ...this.body[0] };
head.x += direction.x;
head.y += direction.y;
this.body.unshift(head);
if (head.x === fruit.x && head.y === fruit.y) {
score++;
if (speed > 80) speed -= 5;
placeFruit();
} else {
this.body.pop();
}
}
draw() {
this.body.forEach((segment, i) => {
ctx.fillStyle = i === 0 ? "#58a6ff" : "#3fb950";
ctx.beginPath();
ctx.roundRect(
segment.x * scale + 2,
segment.y * scale + 2,
scale - 4,
scale - 4,
6
);
ctx.fill();
});
}
checkCollision() {
const [head, ...body] = this.body;
return (
head.x < 0 ||
head.x >= columns ||
head.y < 0 ||
head.y >= rows ||
body.some((s) => s.x === head.x && s.y === head.y)
);
}
}
Объяснение:
update()
— двигаем змею и проверяем, съела ли она фрукт;
draw()
— отрисовываем каждый сегмент;
checkCollision()
— проверка столкновений с границами или самой собой.
function placeFruit() {
fruit = {
x: Math.floor(Math.random() * columns),
y: Math.floor(Math.random() * rows),
};
if (snake.body.some((s) => s.x === fruit.x && s.y === fruit.y)) {
placeFruit();
}
}
Еда ставится случайно. Если она попала на змею — генерируем заново.
function gameLoop(time = 0) {
if (!playing) return;
const delta = time - lastTime;
if (delta > speed) {
direction = nextDirection;
ctx.clearRect(0, 0, canvas.width, canvas.height);
snake.update();
if (snake.checkCollision()) {
endGame();
return;
}
drawFruit();
snake.draw();
lastTime = time;
}
requestAnimationFrame(gameLoop);
}
Каждый "кадр":
очищаем канвас
двигаем змею
проверяем на смерть
рисуем еду и змею
Клавиатура:
window.addEventListener("keydown", (e) => {
if (e.key === "ArrowUp" && direction.y !== 1) nextDirection = { x: 0, y: -1 };
if (e.key === "ArrowDown" && direction.y !== -1) nextDirection = { x: 0, y: 1 };
if (e.key === "ArrowLeft" && direction.x !== 1) nextDirection = { x: -1, y: 0 };
if (e.key === "ArrowRight" && direction.x !== -1) nextDirection = { x: 1, y: 0 };
});
Свайпы:
canvas.addEventListener("touchstart", (e) => { ... });
canvas.addEventListener("touchend", (e) => { ... });
function endGame() {
playing = false;
canvas.style.display = "none";
scoreDisplay.style.display = "none";
gameOverScreen.style.display = "flex";
finalScore.textContent = `Ты набрал ${score} очков`;
}
Ты не просто написал код — ты понял, как работает игровая логика. Это огромный шаг!
Хочешь изучать больше? Залетай в Кодик — там куча практики, объяснений на простом языке и весёлых заданий.
Не нашли нужной статьи?
Напишите нам и ее сделаем!