Вы когда-нибудь задумывались, как работают браузерные игры? Многие из них используют Canvas — элемент HTML5, который позволяет рисовать графику прямо на странице. В этой статье я покажу, как с помощью JavaScript и Canvas создать простую 2D-игру. Мы пройдем путь от настройки холста до управления персонажем и обработки столкновений. Вы удивитесь, насколько это просто и увлекательно.

Что такое Canvas и зачем он нужен?

Canvas — это, по сути, пустой холст в HTML. Вы берете его, берете кисть (контекст рисования) и рисуете всё, что угодно: линии, круги, изображения. В играх Canvas используется для отрисовки кадров. Каждый кадр — это новое изображение, которое полностью перерисовывается. Так создается анимация.

Преимущество Canvas перед другими технологиями — скорость. Он работает напрямую с графикой браузера, не требуя тяжелых библиотек. Для 2D-игр этого более чем достаточно.

Правило №1: Всегда очищайте холст перед отрисовкой нового кадра. Иначе старые объекты будут наслаиваться друг на друга, создавая „грязь“.

Настройка проекта и первый холст

Начнем с разметки. Создайте HTML-файл и добавьте в него элемент <canvas>. Вот минимальный шаблон:

<!DOCTYPE html>
<html>
<head>
    <title>Моя первая игра</title>
</head>
<body>
    <canvas id="gameCanvas" width="800" height="600"></canvas>
    <script src="game.js"></script>
</body>
</html>

Теперь создайте файл game.js. В нем мы будем писать весь код игры. Начнем с получения доступа к холсту и его контексту:

const canvas = document.getElementById('gameCanvas');
const ctx = canvas.getContext('2d');

Контекст 2d — это набор методов для рисования. С его помощью мы будем рисовать квадраты, круги и текст.

Основной цикл игры: как всё движется

Любая игра работает в цикле. Этот цикл повторяется десятки раз в секунду. Каждый повтор — это кадр. В JavaScript для этого используется функция requestAnimationFrame. Она запускает ваш код синхронно с обновлением экрана.

function gameLoop() {
    // Очищаем холст
    ctx.clearRect(0, 0, canvas.width, canvas.height);

    // Рисуем объекты
    draw();

    // Обновляем логику
    update();

    // Запускаем следующий кадр
    requestAnimationFrame(gameLoop);
}

// Запускаем цикл
gameLoop();

Функции draw и update мы напишем позже. Пока это просто заглушки. Обратите внимание: clearRect очищает весь холст. Без него старые изображения останутся.

Рисуем персонажа: квадрат, который двигается

Давайте нарисуем простого персонажа — красный квадрат. Он будет двигаться по нажатию стрелок. Сначала определим его свойства:

const player = {
    x: 50,
    y: 50,
    width: 40,
    height: 40,
    speed: 5
};

Теперь напишем функцию draw, которая рисует квадрат:

function draw() {
    ctx.fillStyle = 'red';
    ctx.fillRect(player.x, player.y, player.width, player.height);
}

Чтобы квадрат двигался, нужно обрабатывать нажатия клавиш. Добавим обработчик событий:

document.addEventListener('keydown', function(event) {
    switch(event.key) {
        case 'ArrowUp':
            player.y -= player.speed;
            break;
        case 'ArrowDown':
            player.y += player.speed;
            break;
        case 'ArrowLeft':
            player.x -= player.speed;
            break;
        case 'ArrowRight':
            player.x += player.speed;
            break;
    }
});

Теперь при нажатии стрелок квадрат будет двигаться. Но есть нюанс: он может уйти за границы холста. Исправим это в функции update.

function update() {
    // Не даём выйти за левый край
    if (player.x < 0) player.x = 0;
    // За правый край
    if (player.x + player.width > canvas.width) player.x = canvas.width - player.width;
    // За верхний край
    if (player.y < 0) player.y = 0;
    // За нижний край
    if (player.y + player.height > canvas.height) player.y = canvas.height - player.height;
}

Добавляем препятствия и столкновения

Что за игра без препятствий? Создадим массив из нескольких квадратов-врагов. Они будут стоять на месте, а игрок должен их избегать.

const enemies = [
    { x: 200, y: 150, width: 50, height: 50 },
    { x: 400, y: 300, width: 50, height: 50 },
    { x: 600, y: 100, width: 50, height: 50 }
];

function drawEnemies() {
    ctx.fillStyle = 'blue';
    enemies.forEach(function(enemy) {
        ctx.fillRect(enemy.x, enemy.y, enemy.width, enemy.height);
    });
}

Теперь добавим проверку столкновений. Если игрок касается врага, игра заканчивается. Напишем функцию checkCollision:

function checkCollision() {
    for (let i = 0; i < enemies.length; i++) {
        const enemy = enemies[i];
        if (player.x < enemy.x + enemy.width &&
            player.x + player.width > enemy.x &&
            player.y < enemy.y + enemy.height &&
            player.y + player.height > enemy.y) {
            // Столкновение!
            alert('Game Over!');
            // Сброс позиции игрока
            player.x = 50;
            player.y = 50;
        }
    }
}

Вызовите эту функцию внутри update. Теперь при касании синего квадрата появится сообщение, и игрок вернется в начало.

Лайфхак: Для более точного обнаружения столкновений используйте метод isPointInPath или проверяйте расстояние между центрами объектов. Но для прямоугольников простая проверка границ работает отлично.

Таблица: основные методы Canvas для игр

Чтобы вам было проще ориентироваться, вот таблица самых полезных методов и их назначение:

Метод Описание Пример
clearRect(x, y, w, h) Очищает прямоугольную область Очистка всего холста
fillRect(x, y, w, h) Рисует закрашенный прямоугольник Рисование игрока
fillStyle Устанавливает цвет заливки ctx.fillStyle = 'red'
beginPath() Начинает новый путь для фигуры Для кругов и линий
arc(x, y, r, startAngle, endAngle) Рисует дугу или круг ctx.arc(100, 100, 30, 0, Math.PI * 2)
requestAnimationFrame(callback) Запускает анимацию Основной цикл игры

Эти методы — основа. С их помощью можно создавать не только квадраты, но и круги, линии и даже текст.

Оптимизация: как сделать игру плавной

Если игра тормозит, проверьте несколько вещей. Во-первых, не рисуйте слишком много объектов. Для сотен объектов используйте спрайты (изображения) вместо рисования примитивов. Во-вторых, избегайте операций с DOM внутри цикла — это замедляет всё.

Вот несколько советов:

  • Используйте requestAnimationFrame, а не setInterval — он адаптируется под частоту кадров.
  • Не создавайте новые объекты в цикле — переиспользуйте существующие.
  • Для столкновений используйте простые прямоугольники, а не сложные полигоны.
Правило №2: Если игра тормозит, уменьшите количество рисуемых объектов или используйте двойную буферизацию (рисуйте в оффлайн-канвас, а потом копируйте на основной).

Что в итоге

Мы создали простую игру на JavaScript с Canvas. Вы научились настраивать холст, рисовать прямоугольники, обрабатывать клавиши и проверять столкновения. Это основа, на которой можно строить более сложные проекты: добавлять врагов, очки, уровни.

Главное — не бойтесь экспериментировать. Canvas даёт полную свободу. Попробуйте нарисовать круг вместо квадрата, добавьте случайное движение врагам или сделайте фон с облаками. Каждый новый элемент приближает вас к настоящей игре.

Если что-то пошло не так, проверьте консоль браузера — там чаще всего пишутся ошибки. И помните: даже самые крутые игры начинали с простого квадрата.

Студия WNDER