const gameArea = document.getElementById('gameArea'); const paddleLeft = document.getElementById('paddleLeft'); const paddleRight = document.getElementById('paddleRight'); const ball = document.getElementById('ball'); const playerScoreDisplay = document.getElementById('playerScore'); const botScoreDisplay = document.getElementById('botScore'); let paddleLeftY = window.innerHeight / 2 - 40; let paddleRightY = window.innerHeight / 2 - 40; let ballX = window.innerWidth / 2; let ballY = window.innerHeight / 2; let ballSpeedX = 3; let ballSpeedY = 0; let playerScore = 0; let botScore = 0; let isGamePaused = true; const paddleHeight = 80; const paddleWidth = 10; const ballSize = 15; const maxAngle = 45; gameArea.addEventListener('touchmove', (e) => { e.preventDefault(); let touchY = e.touches[0].clientY; if (e.touches[0].clientX < window.innerWidth / 2) { paddleLeftY = touchY - paddleHeight / 2; paddleLeftY = Math.max(0, Math.min(paddleLeftY, window.innerHeight - paddleHeight)); if (isGamePaused) { isGamePaused = false; } } }); function handlePaddleCollision(paddleY, paddleX, isLeftPaddle) { const ballRadius = ballSize / 2; const paddleRight = paddleX + paddleWidth; const paddleTop = paddleY; const paddleBottom = paddleY + paddleHeight; // Closest point on paddle to ball's center let closestX = ballX; if (isLeftPaddle) { closestX = paddleRight; } else { closestX = paddleX; } let closestY = ballY; if (ballY < paddleTop) { closestY = paddleTop; } else if (ballY > paddleBottom) { closestY = paddleBottom; } // Vector from ball to closest point const dx = ballX - closestX; const dy = ballY - closestY; const distance = Math.sqrt(dx * dx + dy * dy); if (distance <= ballRadius) { // Collision occurs // Calculate normal vector const normalX = isLeftPaddle ? 1 : -1; const normalY = 0; // Current velocity const velocity = { x: ballSpeedX, y: ballSpeedY }; // Reflect velocity over the normal const dot = velocity.x * normalX + velocity.y * normalY; const reflected = { x: velocity.x - 2 * dot * normalX, y: velocity.y - 2 * dot * normalY }; // Adjust Y velocity based on hit position const hitPosition = (closestY - (paddleTop + paddleHeight / 2)) / (paddleHeight / 2); const angle = hitPosition * maxAngle * (Math.PI / 180); reflected.y = Math.sin(angle) * Math.abs(reflected.x); // Set new velocity ballSpeedX = reflected.x; ballSpeedY = reflected.y; // Reposition ball outside the paddle const overlap = ballRadius - distance; ballX += normalX * overlap; } } function update() { if (!isGamePaused) { let previousBallX = ballX; ballX += ballSpeedX; ballY += ballSpeedY; // Ball collision with top and bottom walls if (ballY <= 0 || ballY >= window.innerHeight - ballSize) { ballSpeedY = -ballSpeedY; } // Check collision with left paddle using swept collision detection handlePaddleCollision(paddleLeftY, paddleWidth, true); // Check collision with right paddle using swept collision detection handlePaddleCollision(paddleRightY, window.innerWidth - paddleWidth - 25, false); // Ball out of bounds if (ballX <= 0) { botScore++; botScoreDisplay.textContent = botScore; resetBall('right'); } else if (ballX >= window.innerWidth) { playerScore++; playerScoreDisplay.textContent = playerScore; resetBall('left'); } } // Bot AI movement if (ballSpeedX > 0) { if (paddleRightY + paddleHeight / 2 < ballY) { paddleRightY += 3; } else if (paddleRightY + paddleHeight / 2 > ballY) { paddleRightY -= 3; } } paddleRightY = Math.max(0, Math.min(paddleRightY, window.innerHeight - paddleHeight)); ball.style.left = ballX + 'px'; ball.style.top = ballY + 'px'; paddleLeft.style.top = paddleLeftY + 'px'; paddleRight.style.top = paddleRightY + 'px'; requestAnimationFrame(update); } function resetBall(side) { isGamePaused = true; if (side === 'left') { ballX = paddleWidth + 10; ballY = paddleLeftY + paddleHeight / 2; ballSpeedX = Math.abs(ballSpeedX); } else { ballX = window.innerWidth - paddleWidth - 25; ballY = paddleRightY + paddleHeight / 2; ballSpeedX = -Math.abs(ballSpeedX); } ballSpeedY = 0; } // Start the game loop update();