Все мы любим игры. Но когда дело доходит до их создания, многие думают, что это удел избранных — тех, кто знает C++ или Unity наизусть. А что если я скажу, что можно сделать полноценную аркаду или головоломку прямо в браузере, используя обычный JavaScript и один элемент <canvas>? Звучит как магия, но это чистая правда. В этой статье я расскажу, как сделать первые шаги в геймдеве без боли и страха, используя только то, что уже есть в твоём браузере.
Почему Canvas, а не что-то серьёзное?
Когда я начинал, мне казалось, что для игр нужен какой-то мощный движок. Но Canvas — это как чистый лист бумаги. Ты сам рисуешь каждый кадр, каждую фигуру. Это даёт полный контроль и понимание того, как работают игры под капотом. Плюс, Canvas работает везде — на компьютере, планшете, телефоне. Не нужно ничего устанавливать, просто открыл браузер и пишешь код.
Конечно, для огромных 3D-миров Canvas не подойдёт. Но для 2D-платформеров, змейки, тетриса, арканоида — это идеальный инструмент. И знаешь, что самое приятное? Ты можешь сделать игру, которая будет работать даже на старом ноутбуке, не нагружая его.
Давай сразу к делу. Создадим простую игру — поймай падающий шарик. Это классика, на которой удобно учиться.
Настройка сцены: создаём Canvas
Canvas — это HTML-элемент, который выглядит как прямоугольная область. Вся магия происходит через JavaScript: мы получаем контекст рисования и начинаем творить. Представь, что это холст художника, а кисти — это код.
Сначала создадим HTML-страницу с элементом <canvas>. Не забудь указать ширину и высоту — иначе он будет размером 300 на 150 пикселей по умолчанию, что маловато для игры.
<!DOCTYPE html>
<html lang="ru">
<head>
<meta charset="UTF-8">
<title>Поймай шарик</title>
<style>
canvas {
border: 2px solid #333;
display: block;
margin: 0 auto;
}
</style>
</head>
<body>
<canvas id="game" width="600" height="400"></canvas>
<script src="game.js"></script>
</body>
</html>
Теперь в файле game.js мы получим наш холст и контекст. Контекст — это объект, через который мы рисуем. Есть два типа: 2d и webgl. Для нас пока нужен только 2D.
// Получаем элемент canvas по его ID
const canvas = document.getElementById('game');
// Получаем 2D-контекст
const ctx = canvas.getContext('2d');
// Проверяем, что всё получилось
console.log(ctx); // Должен быть объект, а не null
Если ты видишь в консоли объект — всё работает. Если null, значит что-то не так с ID или браузер слишком старый. Но в современных браузерах проблем не будет.
Игровой цикл: сердце любой игры
Любая игра — это бесконечный цикл: отрисовали кадр, обновили логику, отрисовали следующий. Это называется игровой цикл. В JavaScript для этого есть специальная функция requestAnimationFrame. Она вызывает наш код перед каждой перерисовкой экрана, обычно 60 раз в секунду. Это идеально для плавной анимации.
Вот как выглядит базовый цикл:
function update() {
// Здесь будем обновлять логику игры
// Например, двигать шарик
}
function draw() {
// Здесь будем рисовать всё на экране
}
function gameLoop() {
update();
draw();
requestAnimationFrame(gameLoop);
}
// Запускаем цикл
gameLoop();
Обрати внимание: мы не используем setInterval. requestAnimationFrame сам подстраивается под частоту экрана и не тратит ресурсы, когда вкладка неактивна. Это экономно и правильно.
requestAnimationFrame — это метка времени в миллисекундах. Двигай объекты на скорость * (дельта / 16.7), чтобы нормализовать скорость.
Рисуем персонажа и врага
Теперь давай нарисуем простые фигуры. Пусть у нас будет игрок — прямоугольная ракетка внизу, и враг — падающий шарик сверху. Для начала просто нарисуем их в статике.
// Игрок: ракетка
const player = {
x: 250, // позиция по X
y: 350, // позиция по Y (низ экрана)
width: 80,
height: 15,
speed: 5
};
// Враг: шарик
const ball = {
x: 300, // случайная позиция
y: 0,
radius: 10,
speedY: 2, // скорость падения
speedX: 1 // скорость по горизонтали (для разнообразия)
};
function draw() {
// Очищаем экран
ctx.clearRect(0, 0, canvas.width, canvas.height);
// Рисуем игрока (прямоугольник)
ctx.fillStyle = '#4CAF50'; // зелёный
ctx.fillRect(player.x, player.y, player.width, player.height);
// Рисуем врага (круг)
ctx.beginPath();
ctx.arc(ball.x, ball.y, ball.radius, 0, Math.PI * 2);
ctx.fillStyle = '#F44336'; // красный
ctx.fill();
ctx.closePath();
}
Что здесь происходит? fillRect рисует прямоугольник, а arc — круг. beginPath и closePath нужны, чтобы не смешивать фигуры. Цвет задаётся через fillStyle — это может быть любой CSS-цвет.
Запусти и посмотри: зелёная полоска внизу и красный шарик сверху. Это уже почти игра, но шарик не падает. Добавим движение.
Движение и управление
Чтобы шарик падал, нужно в функции update изменять его координату y. А игроком будем управлять с клавиатуры — стрелками влево и вправо. Для этого слушаем события нажатия клавиш.
let keys = {};
// Запоминаем, какие клавиши нажаты
document.addEventListener('keydown', (e) => {
keys[e.code] = true;
});
document.addEventListener('keyup', (e) => {
keys[e.code] = false;
});
function update() {
// Управление игроком
if (keys['ArrowLeft'] && player.x > 0) {
player.x -= player.speed;
}
if (keys['ArrowRight'] && player.x + player.width < canvas.width) {
player.x += player.speed;
}
// Движение шарика
ball.y += ball.speedY;
ball.x += ball.speedX;
// Отскок от стенок по X
if (ball.x + ball.radius > canvas.width || ball.x - ball.radius < 0) {
ball.speedX = -ball.speedX;
}
// Если шарик упал вниз — вернуть наверх
if (ball.y > canvas.height) {
ball.y = 0;
ball.x = Math.random() * canvas.width;
// Можно увеличить скорость для сложности
}
// Проверка столкновения с игроком
if (ball.y + ball.radius > player.y &&
ball.y - ball.radius < player.y + player.height &&
ball.x > player.x && ball.x < player.x + player.width) {
// Отскок вверх
ball.speedY = -ball.speedY;
// Немного меняем направление по X для интереса
ball.speedX = (Math.random() - 0.5) * 2;
}
}
Здесь мы используем объект keys для хранения состояния клавиш. Это простой и надёжный способ. Шарик при столкновении с ракеткой отскакивает вверх, а если упал вниз — появляется сверху.
Попробуй запустить код. Теперь ты можешь ловить шарик ракеткой. Это уже игра! Конечно, не хватает счёта и проигрыша, но это легко добавить.
Обработка столкновений и счёт
Столкновения — это ключевая механика. В нашем случае мы проверяем пересечение прямоугольника и круга. Но есть более простой способ — использовать окружность вокруг шарика и прямоугольник ракетки. Мы уже сделали это приблизительно, но можно улучшить.
Добавим счёт. Пусть за каждое отбивание мы получаем очко. А если шарик упал — игра заканчивается. Для этого заведём переменную score и gameOver.
let score = 0;
let gameOver = false;
function update() {
// ... предыдущий код ...
// Проверка столкновения
if (ball.y + ball.radius > player.y &&
ball.y - ball.radius < player.y + player.height &&
ball.x > player.x && ball.x < player.x + player.width) {
ball.speedY = -Math.abs(ball.speedY); // всегда вверх
score++;
// Можно увеличить скорость
ball.speedY += 0.1;
}
// Если упал вниз
if (ball.y - ball.radius > canvas.height) {
gameOver = true;
}
}
function draw() {
// Очищаем экран
ctx.clearRect(0, 0, canvas.width, canvas.height);
// Рисуем игрока и шарик
// ...
// Рисуем счёт
ctx.fillStyle = '#000';
ctx.font = '20px Arial';
ctx.fillText('Счёт: ' + score, 10, 30);
// Если игра окончена
if (gameOver) {
ctx.fillStyle = 'rgba(0,0,0,0.5)';
ctx.fillRect(0, 0, canvas.width, canvas.height);
ctx.fillStyle = '#fff';
ctx.font = '40px Arial';
ctx.fillText('Игра окончена!', canvas.width/2 - 150, canvas.height/2);
ctx.font = '20px Arial';
ctx.fillText('Нажми R для рестарта', canvas.width/2 - 100, canvas.height/2 + 40);
}
}
// Рестарт по клавише R
document.addEventListener('keydown', (e) => {
if (e.code === 'KeyR' && gameOver) {
// Сбросить всё
score = 0;
gameOver = false;
ball.y = 0;
ball.x = Math.random() * canvas.width;
ball.speedY = 2;
}
});
Теперь у нас есть полноценная игра со счётом, условием проигрыша и рестартом. Это уже можно показать друзьям.
Полезные советы для новичков
Создание игр — это итеративный процесс. Не пытайся сразу сделать идеально. Вот несколько правил, которые я вывел за годы практики:
- Начинай с малого. Сделай змейку, а не RPG с открытым миром. Маленький успех лучше большого провала.
- Используй константы. Выноси скорость, размеры в отдельные переменные. Так проще настраивать баланс.
- Не бойся ошибок. Ошибки — это часть процесса. Читай сообщения в консоли, они часто подсказывают решение.
- Разделяй код. Логика, отрисовка, обработка ввода — лучше держать в разных функциях.
Что дальше?
Теперь ты знаешь основы. Можно добавить звуки через Web Audio API, спрайты вместо кружочков, уровни сложности, анимацию частиц. Canvas позволяет многое. Если захочешь углубиться — изучи физику, игровые паттерны, или переходи на библиотеки типа Phaser, но уже с пониманием, как всё работает.
В итоге, создание игр на Canvas — это увлекательный способ прокачать навыки программирования. Ты учишься думать алгоритмически, работать с анимацией, обрабатывать события. И всё это с мгновенным результатом — ты видишь, как твоя игра оживает.
Так что открывай редактор, пиши код и не бойся экспериментировать. Удачи!



