Как создать игру «Змейка» на HTML/CSS/JavaScript

Классическая игра «Змейка» — отличный учебный проект для улучшения навыков программирования и решения задач. В этой статье показано, как собрать рабочую версию в браузере, используя HTML, CSS и JavaScript с canvas.
В игре вы управляете змейкой, которая перемещается по полю, растёт при поедании еды и проигрывает при столкновении со стеной или своим хвостом.
Что будет в этом руководстве
- Быстрое создание UI с canvas
- Отрисовка и представление сегментов змейки
- Управление клавишами, движение и игровой цикл
- Появление еды и механизм поедания
- Логика роста и завершения игры
- Дополнения: тестовые сценарии, критерии приёмки, чеклисты и подсказки по улучшению
Важно: все размеры в примерах выражены в пикселях (px). Для адаптивности можно масштабировать поле и размеры сегментов по отношению к ширине окна.
Как создать интерфейс canvas
Используйте HTML и CSS для размещения canvas, на котором будет происходить игра. Ниже — базовые шаги и минимальная структура файлов. Для полного исходного кода можно завести репозиторий на GitHub.
Создайте файл
index.html.Добавьте базовую структуру HTML. Пример:
Snake Game
- Внутри
bodyдобавьте заголовок и элемент canvas:
Snake Game
- Создайте файл
styles.cssрядом сindex.htmlи добавьте простые стили для поля. Пример:
#game {
width: 400px;
height: 400px;
margin: 0 auto;
background-color: #eee;
}
h2 {
text-align: center;
font-family: Arial, sans-serif;
font-size: 36px;
}
canvas {
display: block;
}- Откройте
index.htmlв браузере, чтобы увидеть пустой canvas.
Как отрисовать змейку
Змейка состоит из квадратных сегментов. В самом начале длина — один сегмент.
- Подключите JavaScript-файл в конец
body:
- В
script.jsполучите элемент canvas и контекст 2D:
var canvas = document.getElementById('snake');
var canvas2d = canvas.getContext('2d');
canvas.width = 400;
canvas.height = 400;- Объявите игровые переменные:
var gameEnded = false;
var snakeSegments = [];
var snakeLength = 1;
var snakeX = 0;
var snakeY = 0;- Функция, добавляющая текущую позицию головы в массив сегментов:
function moveSnake() {
snakeSegments.unshift({ x: snakeX, y: snakeY });
}- Функция отрисовки змейки. Мы используем цвет чёрный и квадрат 10px на 10px для каждого сегмента:
function drawSnake() {
canvas2d.fillStyle = '#000';
canvas2d.clearRect(0, 0, canvas.width, canvas.height);
for (var i = 0; i < snakeSegments.length; i++) {
canvas2d.fillRect(snakeSegments[i].x, snakeSegments[i].y, 10, 10);
}
}- Игровой цикл, который обновляет состояние каждые 100 мс:
function gameLoop() {
moveSnake();
drawSnake();
// здесь позже добавим spawnDots(), checkCollision() и т.д.
if (!gameEnded) {
setTimeout(gameLoop, 100);
}
}
// запуск
gameLoop();Откройте страницу — вы увидите один маленький чёрный квадратик в начальной позиции.
Как заставить змейку двигаться
Добавьте логику для смены направления и обновления координат.
- Инициализируйте направление движения (в пикселях на кадр):
var directionX = 10;
var directionY = 0;- Обработчик клавиш для стрелок:
document.onkeydown = function(event) {
switch (event.keyCode) {
case 37: // Left
directionX = -10; directionY = 0; break;
case 38: // Up
directionX = 0; directionY = -10; break;
case 39: // Right
directionX = 10; directionY = 0; break;
case 40: // Down
directionX = 0; directionY = 10; break;
}
};- Обновите функцию moveSnake, чтобы голова смещалась по направлению, и чтобы хвост обрезался до текущей длины:
function moveSnake() {
snakeSegments.unshift({ x: snakeX, y: snakeY });
snakeX += directionX;
snakeY += directionY;
while (snakeSegments.length > snakeLength) {
snakeSegments.pop();
}
}Если не очищать canvas в drawSnake, то при каждом кадре останутся следы (старые прямоугольники). Поэтому мы делаем clearRect в начале отрисовки.
Как добавить еду на поле
Еда — массив точек (dots). При поедании змейка увеличивается.
- Объявите массив точек:
var dots = [];- Функция спавна точек случайно на поле, ограничивая максимум 10 точек:
function spawnDots() {
if (dots.length < 10) {
var dotX = Math.floor(Math.random() * canvas.width / 10) * 10;
var dotY = Math.floor(Math.random() * canvas.height / 10) * 10;
dots.push({ x: dotX, y: dotY });
}
}Примечание: здесь мы выравниваем координаты по сетке 10px, чтобы еда совпадала с шагом змейки.
- Отрисовка точек в drawSnake или в отдельной функции:
function drawDots() {
for (var i = 0; i < dots.length; i++) {
canvas2d.fillStyle = 'red';
canvas2d.fillRect(dots[i].x, dots[i].y, 10, 10);
}
}- В gameLoop вызывайте spawnDots() и drawDots():
function gameLoop() {
moveSnake();
drawSnake();
drawDots();
spawnDots();
checkCollision();
if (!gameEnded) setTimeout(gameLoop, 100);
}Как сделать, чтобы змейка росла
При пересечении головы и еды увеличиваем длину и удаляем точку из массива.
function checkCollision() {
// проверка столкновения с едой
for (var i = 0; i < dots.length; i++) {
if (snakeX === dots[i].x && snakeY === dots[i].y) {
snakeLength++;
dots.splice(i, 1);
i--; // корректируем индекс после удаления
}
}
// позже здесь добавим проверку стен и хвоста
}Откройте страницу и двигайтесь так, чтобы голова проходила по координатам точек — змейка будет расти.
Как завершить игру
Игра завершается при столкновении с границами поля или при пересечении головы с любым сегментом хвоста.
- Функция окончания игры с простым уведомлением:
function gameOver() {
setTimeout(function() {
alert('Game over!');
}, 500);
gameEnded = true;
}- Проверка выхода за пределы canvas (с запасом 0 px или по вашему шагу):
if (snakeX < 0 || snakeY < 0 || snakeX >= canvas.width || snakeY >= canvas.height) {
gameOver();
}- Проверка пересечения головы с хвостом:
for (var i = 1; i < snakeSegments.length; i++) {
if (snakeX === snakeSegments[i].x && snakeY === snakeSegments[i].y) {
gameOver();
}
}Пример alert при окончании игры.
Улучшения и варианты реализации
Ниже — идеи для дальнейшего развития и альтернативные подходы.
- Альтернативы движку
- Использовать requestAnimationFrame для плавности и адаптации к частоте кадра.
- Ввести масштабируемую сетку и относительные единицы вместо фиксированных 10px.
- Графика
- Заменить квадраты на изображения или градиенты.
- Добавить эффекты при поедании еды.
- Управление
- Поддержка WASD и сенсорных свайпов для мобильных устройств.
- ИИ
- Добавить бота, который играет автоматически, для демонстрации алгоритмов поиска пути.
- Сохранение
- Локальное сохранение рекордов в LocalStorage.
Когда подход из статьи может не подойти
- Если требуется высокая производительность и сложная физика — canvas 2D прост, но для сложной графики лучше WebGL.
- Для сетевой многопользовательской игры нужен сервер и синхронизация состояния.
- Если игра должна быть доступна на малых экранах, надо переработать управление и размеры сетки.
Мини-методология: шаги разработки
- Скелет: HTML + canvas + базовый CSS.
- Отрисовка и базовый игровой цикл без логики столкновений.
- Управление клавишами и движение.
- Еда и рост.
- Логика завершения игры и обработка состояний.
- Рефакторинг: вынести константы, улучшить масштабирование, написать тесты.
Чек-листы по ролям
Разработчик
- Создать структуру файлов.
- Реализовать игровой цикл и отрисовку.
- Обработать ввод и столкновения.
- Убедиться в отсутствии утечек памяти (массивы не растут бесконечно).
QA
- Проверить движение во всех направлениях.
- Проверить поедание еды и правильное увеличение длины.
- Тест на столкновение с границами и хвостом.
- Граничные сценарии: быстрая смена направления, многократное нажатие клавиш.
Дизайнер
- Проверить читаемость на разных размерах экрана.
- Задать цвета и контраст для доступности.
Критерии приёмки
- Игра запускается в современных браузерах.
- Змейка корректно движется, управляется стрелками и/или WASD.
- Еда появляется и исчезает при поедании, длина увеличивается.
- Игра заканчивается при столкновении со стеной или хвостом.
- Нет очевидных багов при быстрой смене направлений.
Тестовые сценарии (кратко)
- Старт игры: canvas видим, один сегмент в позиции (0,0).
- Движение вправо: после N шагов X увеличивается на N*10px.
- Поедание еды: при совпадении координат длина увеличивается на 1.
- Столкновение со стеной: alert показывается, цикл останавливается.
- Столкновение с хвостом: alert показывается, игра останавливается.
Подсказки и частые ошибки
- Не забудьте выравнивать координаты еды по шагу движения (например, 10px), иначе совпадение будет редким.
- Обрабатывайте нажатие противоположной стрелки: если двигались вправо, нельзя сразу принять направление влево — это мгновенно завершит игру.
- При генерации случайных точек проверяйте, чтобы точка не появилась внутри змейки.
Шпаргалка — часто используемые фрагменты
- Получить canvas и 2D-контекст:
var canvas = document.getElementById('snake');
var canvas2d = canvas.getContext('2d');- Очистить холст:
canvas2d.clearRect(0, 0, canvas.width, canvas.height);- Рисовать квадрат 10px:
canvas2d.fillRect(x, y, 10, 10);Короткий словарь
- canvas — HTML-элемент для рисования растровой графики.
- сегмент — один квадратный фрагмент змейки.
- step/шаг — размер перемещения за кадр, в примерах 10px.
Итог
Создание «Змейки» — отличный проект для закрепления основ DOM, canvas и базовой логики игр. Начните с минимальной версии, затем постепенно добавляйте улучшения: плавность, анимацию, мобильное управление и сохранение рекордов.
Краткие выводы и чек-лист есть выше — используйте их как план релиза минимально жизнеспособного продукта.
Похожие материалы
RDP: полный гид по настройке и безопасности
Android как клавиатура и трекпад для Windows
Советы и приёмы для работы с PDF
Calibration в Lightroom Classic: как и когда использовать
Отключить Siri Suggestions на iPhone