diff --git a/index.html b/index.html new file mode 100644 index 0000000..dff4b40 --- /dev/null +++ b/index.html @@ -0,0 +1,28 @@ + + + + + + Ping Pong Game + + + +

Ping Pong

+ +
+
Player: 0
+
Opponent: 0
+
+ + + +
+

Instructions

+

Use your mouse to move your paddle (the left one) up and down.
+ The first player to reach 5 points wins!
+ If the game ends, refresh the page to play again.

+
+ + + + diff --git a/script.js b/script.js new file mode 100644 index 0000000..5964865 --- /dev/null +++ b/script.js @@ -0,0 +1,242 @@ +// 1. Canvas and Context +const canvas = document.getElementById('gameCanvas'); +const context = canvas.getContext('2d'); +const canvasWidth = canvas.width; +const canvasHeight = canvas.height; + +// 2. Game Elements - Properties +// Paddles +const paddleWidth = 10; +const paddleHeight = 100; + +const player = { + x: 0, + y: canvasHeight / 2 - paddleHeight / 2, + width: paddleWidth, + height: paddleHeight, + color: 'blue', + dy: 0 // vertical speed +}; + +const opponent = { + x: canvasWidth - paddleWidth, + y: canvasHeight / 2 - paddleHeight / 2, + width: paddleWidth, + height: paddleHeight, + color: 'red', + dy: 0 // vertical speed +}; + +// Ball +const ballRadius = 7; +const ball = { + x: canvasWidth / 2, + y: canvasHeight / 2, + radius: ballRadius, + speedX: 5, // initial horizontal speed + speedY: 5, // initial vertical speed + color: 'white' +}; + +// Score +let playerScore = 0; +let opponentScore = 0; +const WINNING_SCORE = 5; +let gameOver = false; + +// Net (Optional, for visual) +const net = { + x: canvasWidth / 2 - 1, // center of the canvas, adjusted for width + y: 0, + width: 2, + height: 10, // height of each segment + color: 'grey' +}; + +// 3. Drawing Functions +function drawRect(x, y, width, height, color) { + context.fillStyle = color; + context.fillRect(x, y, width, height); +} + +function drawCircle(x, y, radius, color) { + context.fillStyle = color; + context.beginPath(); + context.arc(x, y, radius, 0, Math.PI * 2, false); + context.closePath(); + context.fill(); +} + +function drawNet() { + for (let i = 0; i <= canvasHeight; i += 15) { // Draw dashed line + drawRect(net.x, net.y + i, net.width, net.height, net.color); + } +} + +function drawText(text, x, y, color, font = '16px Arial') { + context.fillStyle = color; + context.font = font; + context.fillText(text, x, y); +} + +// 4. Initial Rendering Function +function renderInitialState() { + // Clear the canvas (though gameLoop will also do this) + context.clearRect(0, 0, canvasWidth, canvasHeight); // Clear the entire canvas + + // Set a background color for the canvas (optional, if not set in CSS or if CSS is overridden) + drawRect(0, 0, canvasWidth, canvasHeight, 'black'); + + + // Draw elements + drawNet(); + drawRect(player.x, player.y, player.width, player.height, player.color); + drawRect(opponent.x, opponent.y, opponent.width, opponent.height, opponent.color); + drawCircle(ball.x, ball.y, ball.radius, ball.color); + + // Example of drawing scores (will be updated dynamically later) + // drawText('Player: 0', 50, 30, 'white'); + // drawText('Opponent: 0', canvasWidth - 150, 30, 'white'); +} + + +// 5. Game Loop Structure +function gameLoop() { + // Clear the canvas + // context.clearRect(0, 0, canvasWidth, canvasHeight); // Already done in renderInitialState for now + // and will be the first step in actual game updates + drawRect(0, 0, canvasWidth, canvasHeight, 'black'); // Draw background + + // Draw game elements + drawNet(); + drawRect(player.x, player.y, player.width, player.height, player.color); + drawRect(opponent.x, opponent.y, opponent.width, opponent.height, opponent.color); + drawCircle(ball.x, ball.y, ball.radius, ball.color); + + // (Movement, collision detection, score updates will go here in later steps) + + // Call gameLoop again for the next frame + requestAnimationFrame(gameLoop); +} + +// 6. Player Paddle Movement +canvas.addEventListener('mousemove', movePlayerPaddle); + +function movePlayerPaddle(event) { + let rect = canvas.getBoundingClientRect(); + player.y = event.clientY - rect.top - player.height / 2; + + // Prevent paddle from going off-screen + if (player.y < 0) { + player.y = 0; + } else if (player.y + player.height > canvasHeight) { + player.y = canvasHeight - player.height; + } +} + +// Collision Detection function +function collision(b, p) { // b for ball, p for paddle + return b.x + b.radius > p.x && // ball right edge vs paddle left edge + b.x - b.radius < p.x + p.width && // ball left edge vs paddle right edge + b.y + b.radius > p.y && // ball bottom edge vs paddle top edge + b.y - b.radius < p.y + p.height; // ball top edge vs paddle bottom edge +} + +// Reset Ball function +function resetBall() { + ball.x = canvasWidth / 2; + ball.y = canvasHeight / 2; + ball.speedX = -ball.speedX; // Change direction to the player who conceded + ball.speedY = (Math.random() > 0.5 ? 1 : -1) * (Math.random() * 3 + 2); // Random Y speed and direction +} + +// Update function (called in gameLoop) +function update() { + if (gameOver) return; + + // Ball Movement + ball.x += ball.speedX; + ball.y += ball.speedY; + + // Ball Collision - Top and Bottom Walls + if (ball.y - ball.radius < 0 || ball.y + ball.radius > canvasHeight) { + ball.speedY = -ball.speedY; + } + + // Opponent Paddle (Simple AI) + opponent.y += (ball.y - (opponent.y + opponent.height / 2)) * 0.09; // Slightly adjusted AI speed + + // Prevent opponent paddle from going off-screen + if (opponent.y < 0) { + opponent.y = 0; + } else if (opponent.y + opponent.height > canvasHeight) { + opponent.y = canvasHeight - opponent.height; + } + + // Ball Collision - Paddles + let targetPaddle = ball.x < canvasWidth / 2 ? player : opponent; + if (collision(ball, targetPaddle)) { + ball.speedX = -ball.speedX; + + // Optional: Adjust Y speed based on hit location (simple version) + let deltaY = ball.y - (targetPaddle.y + targetPaddle.height / 2); + ball.speedY = deltaY * 0.25; // The further from center, the more angle + + // Optional: Increase ball speed + // ball.speedX *= 1.05; + // ball.speedY *= 1.05; + } + + // Scoring System + if (ball.x - ball.radius < 0) { // Opponent scores + opponentScore++; + resetBall(); + } else if (ball.x + ball.radius > canvasWidth) { // Player scores + playerScore++; + resetBall(); + } + + // Check for Game Over + if (playerScore === WINNING_SCORE || opponentScore === WINNING_SCORE) { + gameOver = true; + } +} + +// Draw function (called in gameLoop) +function drawGameElements() { + // Clear the canvas + drawRect(0, 0, canvasWidth, canvasHeight, 'black'); // Draw background + + // Draw game elements + drawNet(); + drawRect(player.x, player.y, player.width, player.height, player.color); + drawRect(opponent.x, opponent.y, opponent.width, opponent.height, opponent.color); + drawCircle(ball.x, ball.y, ball.radius, ball.color); + + // Display Scores + drawText(playerScore.toString(), canvasWidth / 4, canvasHeight / 5, 'white', '30px Arial'); + drawText(opponentScore.toString(), 3 * canvasWidth / 4, canvasHeight / 5, 'white', '30px Arial'); + + // Display Game Over Message + if (gameOver) { + let message = playerScore === WINNING_SCORE ? "Player Wins!" : "Opponent Wins!"; + drawText(message, canvasWidth / 2 - 100, canvasHeight / 2, 'yellow', '40px Arial'); + drawText("Refresh to Play Again", canvasWidth / 2 - 120, canvasHeight / 2 + 40, 'white', '20px Arial'); + } +} + +// 5. Game Loop Structure +function gameLoop() { + update(); // Update game state + drawGameElements(); // Draw the game + + // Call gameLoop again for the next frame if game is not over + if (!gameOver) { + requestAnimationFrame(gameLoop); + } +} + +// 7. Initial Call & Ball Serve +resetBall(); // Serve the ball initially +ball.speedX = 5; // Ensure initial serve goes towards player or opponent consistently +requestAnimationFrame(gameLoop); // Start the game loop diff --git a/style.css b/style.css new file mode 100644 index 0000000..0ac7400 --- /dev/null +++ b/style.css @@ -0,0 +1,53 @@ +body { + background-color: #f0f0f0; /* Light grey background */ + font-family: Arial, sans-serif; + text-align: center; + margin: 0; + padding: 20px; +} + +h1 { + color: #333; +} + +#gameCanvas { + border: 1px solid black; + display: block; + margin: 20px auto; /* Centering the canvas */ + background-color: #fff; /* White background for the canvas */ +} + +#score { + display: flex; + justify-content: space-around; + width: 800px; /* Same width as canvas for alignment */ + margin: 10px auto; + font-size: 1.2em; +} + +#player-score, #opponent-score { + color: #333; + padding: 10px; + background-color: #e7e7e7; + border-radius: 5px; +} + +#instructions { + width: 800px; + margin: 20px auto; + padding: 15px; + background-color: #fff; + border: 1px solid #ddd; + border-radius: 5px; + text-align: left; +} + +#instructions h2 { + text-align: center; + color: #333; +} + +#instructions p { + color: #555; + line-height: 1.6; +}