game.html. Я объясняю, куда именно вставлять и что каждая строчка делает.
Управление: стрелки влево/вправо или мышь
Создай новый файл game.html и скопируй в него этот код:
<!DOCTYPE html>
<html>
<head>
<meta charset="utf-8">
<title>Арканоид — моя игра</title>
<style>
* { padding: 0; margin: 0; box-sizing: border-box; }
canvas {
background: #eee;
display: block;
margin: 20px auto;
box-shadow: 0 0 10px rgba(0,0,0,0.3);
}
body {
background: linear-gradient(135deg, #1a1a2e, #16213e);
min-height: 100vh;
display: flex;
flex-direction: column;
justify-content: center;
align-items: center;
}
.controls { text-align: center; margin-top: 10px; }
button {
background: linear-gradient(135deg, #ff6b6b, #feca57);
color: #1a1a2e;
padding: 10px 24px;
border-radius: 30px;
font-size: 16px;
font-weight: bold;
border: none;
cursor: pointer;
margin: 5px;
}
button:hover { transform: scale(1.05); }
.status {
text-align: center;
margin-top: 10px;
color: #feca57;
font-size: 14px;
}
</style>
</head>
<body>
<canvas id="gameCanvas" width="480" height="320"></canvas>
<div class="controls">
<button id="startBtn">🎮 Начать игру</button>
<button id="restartBtn">🔄 Начать заново</button>
</div>
<div id="gameStatus" class="status">⚡ Нажми «Начать игру»</div>
<script>
// Весь код игры будем писать здесь
</script>
</body>
</html>
<canvas> — белое поле 480x320, где будет рисоваться игра.<div id="gameStatus"> — место для сообщений (победа, поражение).<script>.Вставь этот код внутрь тега <script> (после комментария "// Весь код игры будем писать здесь"):
// ========== 1. Получаем доступ к холсту ==========
const canvas = document.getElementById('gameCanvas');
const ctx = canvas.getContext('2d');
// ========== 2. Переменные для управления игрой ==========
let gameRunning = false; // false — игра стоит на паузе, true — мяч движется
// ========== 3. Параметры мяча ==========
let x = canvas.width / 2; // x-координата центра мяча (по горизонтали) — ставим по середине
let y = canvas.height - 30; // y-координата (по вертикали) — 30 пикселей от низа
let dx = 2; // скорость по горизонтали (2 пикселя за кадр, положительное — вправо)
let dy = -2; // скорость по вертикали (отрицательная — вверх)
const ballRadius = 8; // радиус мяча (8 пикселей)
// ========== 4. Функция рисования мяча ==========
function drawBall() {
ctx.beginPath(); // начинаем новую фигуру
ctx.arc(x, y, ballRadius, 0, Math.PI * 2); // рисуем круг (центр x,y, радиус, угол от 0 до 360°)
ctx.fillStyle = "#0095DD"; // цвет заливки — синий
ctx.fill(); // заливаем
ctx.closePath(); // завершаем фигуру
}
// ========== 5. Главная функция анимации ==========
function draw() {
ctx.clearRect(0, 0, canvas.width, canvas.height); // очищаем весь холст
drawBall(); // рисуем мяч
if (gameRunning) {
// Отскок от левой и правой стены
if (x + dx > canvas.width - ballRadius || x + dx < ballRadius) {
dx = -dx; // разворачиваем горизонтальную скорость
}
// Отскок от верхней стены
if (y + dy < ballRadius) {
dy = -dy; // разворачиваем вертикальную скорость
}
// Обновляем позицию мяча
x += dx;
y += dy;
}
requestAnimationFrame(draw); // просим браузер вызвать эту функцию снова (60 раз в секунду)
}
draw(); // запускаем анимацию (мяч пока не движется, так как gameRunning = false)
// ========== 6. Кнопка "Начать игру" ==========
document.getElementById('startBtn').addEventListener('click', () => {
gameRunning = true;
});
canvas.getContext('2d') — получаем инструмент для рисования.requestAnimationFrame(draw) — создаёт бесконечный цикл перерисовки (анимацию).gameRunning — флаг, который разрешает движение мяча. По нажатию кнопки «Старт» он становится true.Проверь: Открой файл в браузере. Мяч должен стоять на месте. Нажми «Начать игру» — мяч полетит и будет отскакивать от левой, правой и верхней стен. От нижней стены он пока не отскакивает — это сделаем на следующем шаге.
Добавь этот код в конец скрипта (перед закрывающим тегом </script>):
// ========== 7. Параметры ракетки ==========
let paddleHeight = 10; // высота ракетки
let paddleWidth = 75; // ширина ракетки
let paddleX = (canvas.width - paddleWidth) / 2; // начальное положение — по центру
let rightPressed = false; // флаг "нажата стрелка вправо"
let leftPressed = false; // флаг "нажата стрелка влево"
// ========== 8. Управление с клавиатуры ==========
document.addEventListener('keydown', (e) => {
if (e.key === 'ArrowRight') rightPressed = true;
if (e.key === 'ArrowLeft') leftPressed = true;
});
document.addEventListener('keyup', (e) => {
if (e.key === 'ArrowRight') rightPressed = false;
if (e.key === 'ArrowLeft') leftPressed = false;
});
// ========== 9. Управление мышью ==========
canvas.addEventListener('mousemove', (e) => {
if (!gameRunning) return; // двигать ракетку можно только когда игра идёт
const mouseX = e.clientX - canvas.offsetLeft; // координата мыши внутри canvas
if (mouseX > 0 && mouseX < canvas.width) {
paddleX = mouseX - paddleWidth / 2; // ставим ракетку под курсор
// Не даём ракетке вылезти за левый край
if (paddleX < 0) paddleX = 0;
// Не даём вылезти за правый край
if (paddleX > canvas.width - paddleWidth) paddleX = canvas.width - paddleWidth;
}
});
// ========== 10. Рисуем ракетку ==========
function drawPaddle() {
ctx.beginPath();
ctx.rect(paddleX, canvas.height - paddleHeight, paddleWidth, paddleHeight);
ctx.fillStyle = "#0095DD";
ctx.fill();
ctx.closePath();
}
// ========== 11. Добавляем движение ракетки и отскок от неё в функцию draw() ==========
// Нужно изменить функцию draw(). Ниже показан полный код draw() с новыми вставками.
// Замени свою старую функцию draw() на эту:
function draw() {
ctx.clearRect(0, 0, canvas.width, canvas.height);
drawBall();
drawPaddle(); // ← рисуем ракетку
if (gameRunning) {
// Движение ракетки по клавишам
if (rightPressed && paddleX < canvas.width - paddleWidth) paddleX += 7;
if (leftPressed && paddleX > 0) paddleX -= 7;
// Отскок от левой/правой стены
if (x + dx > canvas.width - ballRadius || x + dx < ballRadius) dx = -dx;
// Отскок от верхней стены
if (y + dy < ballRadius) dy = -dy;
// ОТСКОК ОТ РАКЕТКИ
if (y + dy > canvas.height - ballRadius - paddleHeight &&
y + dy < canvas.height - ballRadius &&
x > paddleX && x < paddleX + paddleWidth) {
dy = -dy;
}
// Если мяч упал ниже ракетки (проигрыш) — возвращаем в центр и уменьшаем жизнь (позже)
if (y + dy > canvas.height - ballRadius) {
// Пока просто возвращаем мяч в центр (без потери жизни)
x = canvas.width / 2;
y = canvas.height - 30;
dx = 2;
dy = -2;
}
x += dx;
y += dy;
}
requestAnimationFrame(draw);
}
// Перезапускаем draw (новая версия уже работает)
paddleX — x-координата левого верхнего угла ракетки.rightPressed/leftPressed — запоминают, нажата ли стрелка.draw() добавляем drawPaddle() и движение ракетки.Проверь: Теперь ракетка двигается за мышью и стрелками, мяч отскакивает от неё. Если пропустишь мяч — он вернётся в центр.
Добавь этот код после объявления переменных мяча (например, после строки "let ballRadius = 8;"):
// ========== 12. Параметры кирпичей ==========
const brickRowCount = 3; // количество строк кирпичей
const brickColumnCount = 5; // количество столбцов
const brickWidth = 75; // ширина одного кирпича
const brickHeight = 15; // высота
const brickPadding = 10; // отступ между кирпичами
const brickOffsetTop = 30; // отступ от верхнего края
const brickOffsetLeft = 30; // отступ от левого края
let bricks = []; // массив, где будем хранить кирпичи
// ========== 13. Функция инициализации кирпичей (создаёт массив) ==========
function initBricks() {
bricks = [];
for (let c = 0; c < brickColumnCount; c++) {
bricks[c] = [];
for (let r = 0; r < brickRowCount; r++) {
bricks[c][r] = { x: 0, y: 0, status: 1 }; // status: 1 = целый, 0 = разбит
}
}
}
// ========== 14. Рисуем кирпичи ==========
function drawBricks() {
for (let c = 0; c < brickColumnCount; c++) {
for (let r = 0; r < brickRowCount; r++) {
if (bricks[c][r].status === 1) { // рисуем только целые
const brickX = c * (brickWidth + brickPadding) + brickOffsetLeft;
const brickY = r * (brickHeight + brickPadding) + brickOffsetTop;
bricks[c][r].x = brickX;
bricks[c][r].y = brickY;
ctx.beginPath();
ctx.rect(brickX, brickY, brickWidth, brickHeight);
ctx.fillStyle = "#0095DD";
ctx.fill();
ctx.closePath();
}
}
}
}
// Вызови initBricks() один раз при загрузке (добавь после объявления функций)
initBricks();
status хранит, разбит кирпич (0) или нет (1).initBricks() создаёт новый массив со всеми целыми кирпичами.drawBricks() внутрь функции draw() (сделаем на следующем шаге).Добавь эту функцию после функции drawBricks():
// ========== 15. Проверка попадания в кирпичи ==========
function collisionDetection() {
for (let c = 0; c < brickColumnCount; c++) {
for (let r = 0; r < brickRowCount; r++) {
const b = bricks[c][r];
if (b.status === 1) {
// Если мяч внутри прямоугольника кирпича
if (x > b.x && x < b.x + brickWidth && y > b.y && y < b.y + brickHeight) {
dy = -dy; // отскакивает
b.status = 0; // кирпич разбит
score++; // увеличиваем счёт (переменную объявим позже)
// Если разбили все кирпичи — победа
if (score === brickRowCount * brickColumnCount) {
gameRunning = false;
updateStatusMessage("🎉 ПОБЕДА! 🎉");
}
}
}
}
}
}
Теперь нужно обновить функцию draw(): добавить в неё отрисовку кирпичей и вызов collisionDetection(). Также добавим переменную score и функцию для вывода счёта.
Добавь эти переменные в начало скрипта (рядом с переменными мяча):
let score = 0; // очки
Добавь функцию для рисования счёта:
function drawScore() {
ctx.font = "16px Arial";
ctx.fillStyle = "#0095DD";
ctx.fillText("Score: " + score, 8, 20);
}
Добавь функцию для сообщений (обновляет текст в #gameStatus):
function updateStatusMessage(msg) {
const statusDiv = document.getElementById('gameStatus');
if (statusDiv) statusDiv.textContent = msg;
}
Теперь замени функцию draw() на эту (полностью):
function draw() {
ctx.clearRect(0, 0, canvas.width, canvas.height);
drawBricks(); // рисуем кирпичи
drawBall();
drawPaddle();
drawScore(); // рисуем счёт
if (gameRunning) {
collisionDetection(); // проверяем столкновения с кирпичами
if (rightPressed && paddleX < canvas.width - paddleWidth) paddleX += 7;
if (leftPressed && paddleX > 0) paddleX -= 7;
if (x + dx > canvas.width - ballRadius || x + dx < ballRadius) dx = -dx;
if (y + dy < ballRadius) dy = -dy;
if (y + dy > canvas.height - ballRadius - paddleHeight &&
y + dy < canvas.height - ballRadius &&
x > paddleX && x < paddleX + paddleWidth) {
dy = -dy;
}
if (y + dy > canvas.height - ballRadius) {
// Потеря жизни — добавим позже, пока просто сбрасываем позицию
x = canvas.width / 2;
y = canvas.height - 30;
dx = 2;
dy = -2;
}
x += dx;
y += dy;
}
requestAnimationFrame(draw);
}
Проверь: Теперь на экране появились кирпичи, и мяч их разбивает. Счёт увеличивается. При разбитии всех кирпичей игра останавливается и выводится "ПОБЕДА".
Добавь переменную lives (жизни) рядом с другими переменными:
let lives = 3; // начальное количество жизней
Функция для отображения жизней:
function drawLives() {
ctx.font = "16px Arial";
ctx.fillStyle = "#0095DD";
ctx.fillText("Lives: " + lives, canvas.width - 65, 20);
}
Добавь вызов drawLives() в функцию draw() (после drawScore()).
Теперь изменим обработку потери жизни в функции draw(). Найди место, где было if (y + dy > canvas.height - ballRadius) и замени его на:
if (y + dy > canvas.height - ballRadius) {
lives--;
if (lives === 0) {
gameRunning = false;
updateStatusMessage("💀 GAME OVER 💀 Очки: " + score);
// остановить анимацию (можно оставить, но игра не двигается)
} else {
// Сброс позиции мяча и ракетки
x = canvas.width / 2;
y = canvas.height - 30;
dx = 2;
dy = -2;
paddleX = (canvas.width - paddleWidth) / 2;
updateStatusMessage("❤️ Жизнь потеряна! Осталось: " + lives);
}
}
Наконец, кнопка "Начать заново" — добавь этот код в конец скрипта:
function restartGame() {
gameRunning = true;
lives = 3;
score = 0;
initBricks(); // восстанавливаем все кирпичи
x = canvas.width / 2;
y = canvas.height - 30;
dx = 2;
dy = -2;
paddleX = (canvas.width - paddleWidth) / 2;
updateStatusMessage("🎮 Игра перезапущена! Удачи!");
}
document.getElementById('restartBtn').addEventListener('click', () => {
restartGame();
});
Если в процессе что-то пошло не так, вот полный работающий код. Сохрани его как game.html и открой в браузере.
<!DOCTYPE html>
<html>
<head>
<meta charset="utf-8">
<title>Арканоид — моя игра</title>
<style>
* { padding: 0; margin: 0; box-sizing: border-box; }
canvas { background: #eee; display: block; margin: 20px auto; box-shadow: 0 0 10px rgba(0,0,0,0.3); }
body { background: linear-gradient(135deg, #1a1a2e, #16213e); min-height: 100vh; display: flex; flex-direction: column; justify-content: center; align-items: center; }
.controls { text-align: center; margin-top: 10px; }
button { background: linear-gradient(135deg, #ff6b6b, #feca57); color: #1a1a2e; padding: 10px 24px; border-radius: 30px; font-size: 16px; font-weight: bold; border: none; cursor: pointer; margin: 5px; }
button:hover { transform: scale(1.05); }
.status { text-align: center; margin-top: 10px; color: #feca57; font-size: 14px; }
</style>
</head>
<body>
<canvas id="gameCanvas" width="480" height="320"></canvas>
<div class="controls">
<button id="startBtn">🎮 Начать игру</button>
<button id="restartBtn">🔄 Начать заново</button>
</div>
<div id="gameStatus" class="status">⚡ Нажми «Начать игру»</div>
<script>
const canvas = document.getElementById('gameCanvas');
const ctx = canvas.getContext('2d');
let gameRunning = false;
// Мяч
let x = canvas.width / 2;
let y = canvas.height - 30;
let dx = 2;
let dy = -2;
const ballRadius = 8;
// Ракетка
let paddleHeight = 10;
let paddleWidth = 75;
let paddleX = (canvas.width - paddleWidth) / 2;
let rightPressed = false;
let leftPressed = false;
// Кирпичи
const brickRowCount = 3;
const brickColumnCount = 5;
const brickWidth = 75;
const brickHeight = 15;
const brickPadding = 10;
const brickOffsetTop = 30;
const brickOffsetLeft = 30;
let bricks = [];
function initBricks() {
bricks = [];
for (let c = 0; c < brickColumnCount; c++) {
bricks[c] = [];
for (let r = 0; r < brickRowCount; r++) {
bricks[c][r] = { x: 0, y: 0, status: 1 };
}
}
}
initBricks();
let score = 0;
let lives = 3;
function drawBall() {
ctx.beginPath();
ctx.arc(x, y, ballRadius, 0, Math.PI * 2);
ctx.fillStyle = "#0095DD";
ctx.fill();
ctx.closePath();
}
function drawPaddle() {
ctx.beginPath();
ctx.rect(paddleX, canvas.height - paddleHeight, paddleWidth, paddleHeight);
ctx.fillStyle = "#0095DD";
ctx.fill();
ctx.closePath();
}
function drawBricks() {
for (let c = 0; c < brickColumnCount; c++) {
for (let r = 0; r < brickRowCount; r++) {
if (bricks[c][r].status === 1) {
const brickX = c * (brickWidth + brickPadding) + brickOffsetLeft;
const brickY = r * (brickHeight + brickPadding) + brickOffsetTop;
bricks[c][r].x = brickX;
bricks[c][r].y = brickY;
ctx.beginPath();
ctx.rect(brickX, brickY, brickWidth, brickHeight);
ctx.fillStyle = "#0095DD";
ctx.fill();
ctx.closePath();
}
}
}
}
function drawScore() {
ctx.font = "16px Arial";
ctx.fillStyle = "#0095DD";
ctx.fillText("Score: " + score, 8, 20);
}
function drawLives() {
ctx.font = "16px Arial";
ctx.fillStyle = "#0095DD";
ctx.fillText("Lives: " + lives, canvas.width - 65, 20);
}
function updateStatusMessage(msg) {
const statusDiv = document.getElementById('gameStatus');
if (statusDiv) statusDiv.textContent = msg;
}
function collisionDetection() {
for (let c = 0; c < brickColumnCount; c++) {
for (let r = 0; r < brickRowCount; r++) {
const b = bricks[c][r];
if (b.status === 1) {
if (x > b.x && x < b.x + brickWidth && y > b.y && y < b.y + brickHeight) {
dy = -dy;
b.status = 0;
score++;
if (score === brickRowCount * brickColumnCount) {
gameRunning = false;
updateStatusMessage("🎉 ПОБЕДА! 🎉");
}
}
}
}
}
}
document.addEventListener('keydown', (e) => {
if (e.key === 'ArrowRight') rightPressed = true;
if (e.key === 'ArrowLeft') leftPressed = true;
});
document.addEventListener('keyup', (e) => {
if (e.key === 'ArrowRight') rightPressed = false;
if (e.key === 'ArrowLeft') leftPressed = false;
});
canvas.addEventListener('mousemove', (e) => {
if (!gameRunning) return;
const mouseX = e.clientX - canvas.offsetLeft;
if (mouseX > 0 && mouseX < canvas.width) {
paddleX = mouseX - paddleWidth / 2;
if (paddleX < 0) paddleX = 0;
if (paddleX > canvas.width - paddleWidth) paddleX = canvas.width - paddleWidth;
}
});
function draw() {
ctx.clearRect(0, 0, canvas.width, canvas.height);
drawBricks();
drawBall();
drawPaddle();
drawScore();
drawLives();
if (gameRunning) {
collisionDetection();
if (rightPressed && paddleX < canvas.width - paddleWidth) paddleX += 7;
if (leftPressed && paddleX > 0) paddleX -= 7;
if (x + dx > canvas.width - ballRadius || x + dx < ballRadius) dx = -dx;
if (y + dy < ballRadius) dy = -dy;
if (y + dy > canvas.height - ballRadius - paddleHeight &&
y + dy < canvas.height - ballRadius &&
x > paddleX && x < paddleX + paddleWidth) {
dy = -dy;
}
if (y + dy > canvas.height - ballRadius) {
lives--;
if (lives === 0) {
gameRunning = false;
updateStatusMessage("💀 GAME OVER 💀 Очки: " + score);
} else {
x = canvas.width / 2;
y = canvas.height - 30;
dx = 2;
dy = -2;
paddleX = (canvas.width - paddleWidth) / 2;
updateStatusMessage("❤️ Жизнь потеряна! Осталось: " + lives);
}
}
x += dx;
y += dy;
}
requestAnimationFrame(draw);
}
draw();
document.getElementById('startBtn').addEventListener('click', () => {
gameRunning = true;
});
function restartGame() {
gameRunning = true;
lives = 3;
score = 0;
initBricks();
x = canvas.width / 2;
y = canvas.height - 30;
dx = 2;
dy = -2;
paddleX = (canvas.width - paddleWidth) / 2;
updateStatusMessage("🎮 Игра перезапущена! Удачи!");
}
document.getElementById('restartBtn').addEventListener('click', () => {
restartGame();
});
</script>
</body>
</html>