Загрузка данных
const canvas = document.getElementById("pongCanvas");
const ctx = canvas.getContext("2d");
// Ракетка игрока (слева)
const player = {
x: 10,
y: canvas.height / 2 - 50,
width: 10,
height: 100,
score: 0,
speed: 8
};
// Ракетка бота (справа)
const ai = {
x: canvas.width - 20,
y: canvas.height / 2 - 50,
width: 10,
height: 100,
score: 0,
speed: 4.5 // Сделай больше (например 6), если бот слишком тупой
};
// Мяч
const ball = {
x: canvas.width / 2,
y: canvas.height / 2,
radius: 7,
speedX: 5,
speedY: 5,
baseSpeed: 5
};
// Отслеживание нажатых клавиш
const keys = {};
window.addEventListener("keydown", e => keys[e.key] = true);
window.addEventListener("keyup", e => keys[e.key] = false);
// Сброс мяча в центр после гола
function resetBall() {
ball.x = canvas.width / 2;
ball.y = canvas.height / 2;
// Меняем направление полета и сбрасываем скорость к начальной
ball.speedX = -ball.speedX > 0 ? ball.baseSpeed : -ball.baseSpeed;
ball.speedY = (Math.random() > 0.5 ? 1 : -1) * ball.baseSpeed;
}
// Проверка столкновения мяча с ракеткой
function collision(b, p) {
return b.x + b.radius > p.x &&
b.x - b.radius < p.x + p.width &&
b.y + b.radius > p.y &&
b.y - b.radius < p.y + p.height;
}
// Обновление физики и позиций объектов
function update() {
// 1. Управление игроком (Стрелки Вверх / Вниз или W / S)
if ((keys["ArrowUp"] || keys["w"] || keys["W"]) && player.y > 0) {
player.y -= player.speed;
}
if ((keys["ArrowDown"] || keys["s"] || keys["S"]) && player.y < canvas.height - player.height) {
player.y += player.speed;
}
// 2. Простой ИИ для бота (он плавно двигается за мячом)
let aiTargetY = ball.y - ai.height / 2;
ai.y += (aiTargetY - ai.y) * 0.12; // Коэффициент плавной погони
// Ограничиваем бота рамками экрана
if (ai.y < 0) ai.y = 0;
if (ai.y > canvas.height - ai.height) ai.y = canvas.height - ai.height;
// 3. Движение мяча
ball.x += ball.speedX;
ball.y += ball.speedY;
// Отскок от верхней и нижней стен
if (ball.y - ball.radius < 0 || ball.y + ball.radius > canvas.height) {
ball.speedY = -ball.speedY;
}
// Определяем, с чьей стороны мяч, чтобы проверить столкновение
let paddle = (ball.x < canvas.width / 2) ? player : ai;
if (collision(ball, paddle)) {
// Меняем направление по X
ball.speedX = -ball.speedX;
// Немного ускоряем мяч при каждом ударе для азарта
ball.speedX += ball.speedX > 0 ? 0.3 : -0.3;
ball.speedY += ball.speedY > 0 ? 0.3 : -0.3;
}
// 4. Подсчет очков (выход за левую/правую границу)
if (ball.x - ball.radius < 0) {
ai.score++;
resetBall();
} else if (ball.x + ball.radius > canvas.width) {
player.score++;
resetBall();
}
}
// Отрисовка графики
function draw() {
// Чистим экран черным цветом
ctx.fillStyle = "#000";
ctx.fillRect(0, 0, canvas.width, canvas.height);
// Рисуем сетку посередине поля
ctx.strokeStyle = "rgba(255, 255, 255, 0.2)";
ctx.setLineDash([10, 10]);
ctx.beginPath();
ctx.moveTo(canvas.width / 2, 0);
ctx.lineTo(canvas.width / 2, canvas.height);
ctx.stroke();
ctx.setLineDash([]); // сброс пунктира
// Рисуем счет игрока и бота
ctx.fillStyle = "#fff";
ctx.font = "45px Arial";
ctx.fillText(player.score, canvas.width / 4, 60);
ctx.fillText(ai.score, 3 * canvas.width / 4, 60);
// Рисуем ракетки
ctx.fillRect(player.x, player.y, player.width, player.height);
ctx.fillRect(ai.x, ai.y, ai.width, ai.height);
// Рисуем мяч кругом
ctx.beginPath();
ctx.arc(ball.x, ball.y, ball.radius, 0, Math.PI * 2);
ctx.fill();
}
// Главный игровой цикл
function gameLoop() {
update();
draw();
requestAnimationFrame(gameLoop);
}
// Запуск
gameLoop();