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

Введение
Игра «Змейка» — классическая учебная задача для практики программирования и решения задач. Проект подходит для знакомства с DOM, canvas‑рендерингом, обработкой событий и простыми игровыми циклами. В этой статье все шаги приведены так, чтобы вы могли сразу запустить игру локально и постепенно улучшать её.
Краткая терминология:
- Canvas — элемент HTML для 2D‑рисования.
- Сегмент — один «квадратик» змейки.
- Дот (dot) — кусочек еды на поле.
Что потребуется
- Любой текстовый редактор (VS Code, Atom, Sublime и т. п.).
- Современный браузер (Chrome, Firefox, Edge).
- Папка проекта с файлами index.html, styles.css, script.js.
Важно: все примеры кода можно вставлять в файлы с указанными именами и открывать index.html в браузере.
Основная идея реализации — кратко
- Создаём canvas 400×400 px и стилизуем страницу.
- В script.js инициализируем переменные: положение змейки, массив сегментов, длину, направление и массив еды.
- В цикле игры (gameLoop) обновляем позицию, рисуем сцену, генерируем еду и проверяем столкновения.
- На нажатия клавиш реагируем сменой направления движения.
Факты: canvas 400×400 px (рекомендация), размер сегмента 10 пикселей, интервал игрового цикла — 100 мс, максимум одновременно 10 точек еды (в примере).
Создание пользовательского интерфейса для canvas
Для отображения игрового поля используйте обычный HTML и CSS. Создайте базовую структуру и подключите стили.
- Создайте файл index.html.
- Откройте его в редакторе и вставьте базовую структуру:
Snake Game
- Внутри body добавьте заголовок и canvas:
Snake Game
- В той же папке создайте файл styles.css. Пример простых стилей:
#game {
width:400px;
height:400px;
margin:0 auto;
background-color:#eee;
}
h2 {
text-align:center;
font-family:Arial;
font-size:36px;
}- Подключите стили в head:
- Откройте index.html в браузере, чтобы увидеть пустой canvas.
Важно: в CSS и JavaScript размеры поля задаются в пикселях (px). При желании можно сделать адаптивный canvas, но в учебном примере фиксированный размер упрощает расчёты.
Отрисовка змейки
Змейка состоит из нескольких квадратных сегментов. На старте длина — один сегмент. Ниже — базовые шаги по отрисовке.
- В index.html подключите JavaScript внизу body:
- Создайте script.js и получите элемент canvas:
var canvas = document.getElementById("snake");- Установите 2D‑контекст для рисования:
var canvas2d = canvas.getContext("2d");- Инициализируйте переменные:
var gameEnded = false;
canvas.width = 400;
canvas.height = 400;- Объявите массив сегментов и длину змейки:
var snakeSegments = [];
var snakeLength = 1;- Начальные координаты головы змейки:
var snakeX = 0;
var snakeY = 0;- Функция moveSnake добавляет текущие координаты в начало массива сегментов:
function moveSnake() {
snakeSegments.unshift({ x: snakeX, y: snakeY });
}- Функция drawSnake устанавливает цвет и рисует сегменты:
function drawSnake() {
canvas2d.fillStyle = "black";
}- Для каждого сегмента отрисуйте прямоугольник 10×10 px:
for (var i = 0; i < snakeSegments.length; i++) {
canvas2d.fillRect(snakeSegments[i].x, snakeSegments[i].y, 10, 10);
}- Создайте игровой цикл, выполняющийся каждые 100 мс:
function gameLoop() {
moveSnake();
drawSnake();
// далее — генерация еды, проверка столкновений и повторы цикла
}- Откройте index.html — вы должны увидеть змейку в начальной позиции.
Примечание: пока нет очистки холста и удаления лишних сегментов — змейка будет оставлять след.
Движение змейки
Добавьте логику смены направления при нажатии клавиш и обновления координат.
- В начале файла объявите направление (например, движение вправо):
var directionX = 10;
var directionY = 0;- Добавьте обработчик нажатий клавиш:
document.onkeydown = function(event) {
};- Внутри обработчика обновляйте направление в зависимости от кода клавиши:
switch (event.keyCode) {
case 37: // Left arrow
directionX = -10;
directionY = 0;
break;
case 38: // Up arrow
directionX = 0;
directionY = -10;
break;
case 39: // Right arrow
directionX = 10;
directionY = 0;
break;
case 40: // Down arrow
directionX = 0;
directionY = 10;
break;
}- В функции moveSnake после unshift примените направление к координатам:
function moveSnake() {
snakeSegments.unshift({ x: snakeX, y: snakeY });
snakeX += directionX;
snakeY += directionY;
}- Убедитесь, что в drawSnake в начале очищается холст:
canvas2d.clearRect(0, 0, canvas.width, canvas.height);- В moveSnake удаляйте лишние сегменты, чтобы длина массива соответствовала текущей длине змейки:
while (snakeSegments.length > snakeLength) {
snakeSegments.pop();
}После этого при нажатии стрелок сегмент будет перемещаться по полю, как одиночная «голова». Чтобы увидеть это в действии, откройте index.html и используйте стрелки.
Совет: добавьте простую защиту от мгновенного разворота на 180° (например, блокируйте смену направления на противоположную), чтобы игрок не врезался сам в себя из‑за резкого ввода.
Добавление еды на поле
Еда — это точки, которые генерируются в случайных позициях. При съедании змейка увеличивает длину.
- Объявите массив dots для точек еды:
var dots = [];- Создайте функцию spawnDots, которая добавляет случайные точки, поддерживая до 10 штук:
function spawnDots() {
if(dots.length < 10) {
var dotX = Math.floor(Math.random() * canvas.width);
var dotY = Math.floor(Math.random() * canvas.height);
dots.push({ x: dotX, y: dotY });
}
}- Отрисуйте точки красным цветом:
for (var i = 0; i < dots.length; i++) {
canvas2d.fillStyle = "red";
canvas2d.fillRect(dots[i].x, dots[i].y, 10, 10);
}- Вызывайте spawnDots() в gameLoop, чтобы в кадрах появлялась еда:
function gameLoop() {
moveSnake();
drawSnake();
spawnDots();
if(!gameEnded) {
setTimeout(gameLoop, 100);
}
}- Откройте страницу и убедитесь, что на поле появляются красные квадратики — еда.
Замечание: в текущей реализации координаты еды могут быть любыми пикселями, не обязательно кратными 10 — это влияет на точность столкновений. Ниже рассмотрим вариант выравнивания сетки.
Сделать генерацию еды и движения сеточной (опционально)
Лучше выравнивать координаты еды и стартовую позицию по сетке размера сегмента (10 px), тогда столкновения будут точными и предсказуемыми.
Пример генерации точки, выровненной по сетке:
var gridSize = 10; // размер сегмента в пикселях
var cols = canvas.width / gridSize;
var rows = canvas.height / gridSize;
function spawnDotsGrid() {
if (dots.length < 10) {
var dotX = Math.floor(Math.random() * cols) * gridSize;
var dotY = Math.floor(Math.random() * rows) * gridSize;
dots.push({ x: dotX, y: dotY });
}
}Используйте gridSize при вычислении направлений и проверках столкновений.
Как заставить змейку расти
При столкновении головы со «едой» увеличиваем snakeLength и удаляем точку из dots.
- Создаём функцию checkCollision:
function checkCollision() {
for (var i = 0; i < dots.length; i++) {
}
}- Если координаты головы пересекаются с координатами точки (AABB проверка), увеличиваем длину и удаляем точку:
if (snakeX < dots[i].x + 10 &&
snakeX + 10 > dots[i].x &&
snakeY < dots[i].y + 10 &&
snakeY + 10 > dots[i].y) {
snakeLength++;
dots.splice(i, 1);
}- Вызывайте checkCollision() в gameLoop:
function gameLoop() {
moveSnake();
drawSnake();
spawnDots();
checkCollision();
if(!gameEnded) {
setTimeout(gameLoop, 100);
}
}После съедания еды длина змейки увеличится, так как в каждом кадре добавляется новая «голова», а удаление хвоста контролируется переменной snakeLength.
Окончание игры
Игра должна завершаться, когда голова сталкивается со стеной или с собственным хвостом.
- Функция gameOver показывает уведомление и ставит флаг завершения игры:
function gameOver() {
setTimeout(function() {
alert("Game over!");
}, 500);
gameEnded = true
}- В checkCollision проверьте выход за границы canvas:
if (snakeX < -10 ||
snakeY < -10 ||
snakeX > canvas.width+10 ||
snakeY > canvas.height+10) {
gameOver();
}- Для проверки столкновения головы с хвостом пройдитесь по массиву сегментов (начиная с 1, пропуская голову на индексе 0):
for (var i = 1; i < snakeSegments.length; i++) {
}
if (snakeX === snakeSegments[i].x && snakeY === snakeSegments[i].y) {
gameOver();
}- Запустите игру и проверьте: при столкновении с стеной или хвостом появится alert «Game over!».
Совет: вместо alert можно отрисовывать сообщение на canvas и предлагать кнопку “Перезапустить”.
Улучшения и варианты развития
Ниже — список улучшений и альтернативных подходов, которыми можно воспользоваться после базовой реализации.
- Выравнивание по сетке (gridSize) для улучшенной предсказуемости столкновений.
- Плавное движение: интерполяция между клетками для анимации при меньшем шаге в пикселях.
- Работа с touch и мобильными жестами: добавить виртуальные кнопки управления или свайпы.
- Сложность: увеличивать скорость (уменьшать интервал gameLoop) с ростом длины или по таймеру.
- Использование requestAnimationFrame вместо setTimeout для более плавной и эффективной отрисовки.
- Сохранение рекорда в localStorage, табло лидеров.
- Добавление препятствий на поле, разных видов еды с бонусами/вредом.
- Перехват фокуса и пауза при потере окна (visibilitychange).
Важно: requestAnimationFrame даёт частоту отрисовки, но тогда нужно вычислять движение на базе прошедшего времени (deltaTime).
Частые ошибки и способы их устранения
- Змейка «застревает» или дергается при быстром смене клавиш: блокируйте смену направления до следующего кадра.
- Столкновения не срабатывают из‑за дробных координат: выровняйте координаты по сетке.
- Еда появляется частично вне области видимости: генерируйте позиции валидно в диапазоне [0, canvas.width-gridSize].
- Накладывающаяся еда на змейку: проверяйте при генерации, чтобы точка не появлялась на текущих сегментах.
Чек‑лист перед публикацией (роль‑ориентированный)
Разработчик:
- Код не содержит глобальных конфликтующих переменных.
- Обработчики клавиш не создают утечек памяти.
- Реализация использует gridSize для предсказуемости.
Тестировщик:
- Проверить движение по всем направлениям.
- Проверить съедение еды и рост змейки.
- Проверить завершение при столкновениях и выход за границы.
- Проверить поведение при смене вкладки / потере фокуса.
Дизайнер:
- Удобные размеры кнопок на мобильных устройствах.
- Контраст цветов хорошо читается при цветовой слепоте.
Критерии приёмки
- Игра запускается в современном браузере, canvas отображается корректно.
- Змейка перемещается по нажатию стрелок (или виртуальных кнопок) и растёт при поедании еды.
- Игра завершается при столкновении с границей или собственным хвостом.
- После окончания игры игрок может перезапустить с начальных настроек.
Тестовые сценарии и критерии качества
- Начальная загрузка: canvas 400×400, одна «голова» в позиции (0,0).
- Движение: при последовательности нажатий змейка проходит путь без самоперекрытия (если не направлять сознательно).
- Поедание: при пересечении головы с клеткой еды длина увеличивается на 1.
- Столкновение с телом: при движении в себя — игра завершается.
- Столкновение со стеной: выход за границы завершает игру.
- Перезагрузка: стартовая кнопка или перезагрузка страницы возвращает стартовое состояние.
Шпаргалка по ключевым функциям (cheat sheet)
- moveSnake() — смещает голову и управляет массивом сегментов.
- drawSnake() — очищает холст и отрисовывает сегменты.
- spawnDots() — создаёт еду.
- checkCollision() — проверяет столкновения с едой, стенами и хвостом.
- gameOver() — завершает игру и показывает сообщение.
Mermaid‑диаграмма — простое дерево принятия решения при коллизии:
flowchart TD
A[Имеется ли столкновение?] -->|Нет| B[Продолжать игру]
A -->|Со стеной| C[gameOver]
A -->|С едой| D[Увеличить длину и удалить еду]
A -->|С хвостом| CБезопасность и конфиденциальность
Игра работает полностью в браузере; персональные данные не требуются. Если вы добавляете регистрацию или таблицу лидеров, храните минимально необходимую информацию и учитывайте требования локального законодательства о данных.
Сравнение подходов: canvas vs DOM
- Canvas: эффективен при большом количестве объектов и частой перерисовке, но требует ручной отрисовки и управления состоянием.
- DOM (div‑сетки): проще реализовать вначале (каждый сегмент — элемент), но при большом количестве элементов производительность падает.
Выбор зависит от целей: для учебного проекта canvas даёт полезный опыт с 2D‑графикой.
Ресурсы и дальнейшие шаги
- Перевести координаты в систему gridSize для стабильности.
- Добавить уровни, препятствия, дополнительные типы еды.
- Поддержка мобильных жестов и touchcontrols.
- Сохранение результатов в localStorage и создание табло лидеров.
Итог
Вы создали базовую игру «Змейка», покрыв ключевые моменты: интерфейс, отрисовка, движение, генерация еды, рост и завершение игры. Дальше можно улучшать UX, производительность и добавлять новые механики.
Важно: экспериментируйте с gridSize, скоростью и правилами — это отличная площадка для изучения JavaScript и игровых паттернов.
Краткое резюме
- Canvas + JS — быстрый путь к интерактивной игре в браузере.
- Grid‑подход упрощает логику столкновений.
- Улучшайте игру постепенно: сначала корректность, затем UX и оптимизация.
Похожие материалы
Как скачать полноразмерный аватар Discord
CSV в Node.js: fs и fast-csv — чтение и запись
Как увеличить экран на ПК — все способы
Как найти человека в WhatsApp — быстро и просто
Подсказки формул в Excel — руководство