|
| 1 | +let lastTime = 0; |
| 2 | +let delta = 1; |
| 3 | +let playerScore = 0; |
| 4 | +let computerScore = 0; |
| 5 | +let gameRunning = false; |
| 6 | +const playerOneScoreElement = document.querySelector("#player-score"); |
| 7 | +const playerTwoScoreElement = document.querySelector("#computer-score"); |
| 8 | +const canvas = document.getElementById("board"); |
| 9 | +const startGameButton = document.getElementById("start-game"); |
| 10 | +const resetGameButton = document.getElementById("reset"); |
| 11 | +const resetModal = document.getElementById("reset-modal"); |
| 12 | +const confirmResetButton = document.getElementById("confirm-reset"); |
| 13 | +const cancelResetButton = document.getElementById("cancel-reset"); |
| 14 | +const ctx = canvas.getContext("2d"); |
| 15 | + |
| 16 | +// Paddle Variables |
| 17 | +const paddleWidth = 10; |
| 18 | +const paddleHeight = 100; |
| 19 | + |
| 20 | +let paddle1Y = canvas?.height / 2 - paddleHeight / 2; |
| 21 | + |
| 22 | +let paddle2Y = canvas?.height / 2 - paddleHeight / 2; |
| 23 | + |
| 24 | +// Ball Variables |
| 25 | +const ballSize = 10; |
| 26 | + |
| 27 | +let ballX = canvas?.width / 2; |
| 28 | + |
| 29 | +let ballY = canvas?.height / 2; |
| 30 | +let ballSpeedX = 3.5; |
| 31 | +let ballSpeedY = 3.5; |
| 32 | + |
| 33 | +function startGame() { |
| 34 | + const gameRules = document.querySelector(".game-rules"); |
| 35 | + const game = document.querySelector("#game"); |
| 36 | + gameRules?.classList.add("hidden"); |
| 37 | + game?.classList.remove("hidden"); |
| 38 | + |
| 39 | + playerOneScoreElement.textContent = "Player: " + playerScore; |
| 40 | + |
| 41 | + playerTwoScoreElement.textContent = "Computer: " + computerScore; |
| 42 | + gameRunning = true; |
| 43 | + gameLoop(); |
| 44 | +} |
| 45 | + |
| 46 | +function resetGame() { |
| 47 | + gameRunning = false; |
| 48 | + resetModal.showModal(); |
| 49 | +} |
| 50 | + |
| 51 | +function confirmReset() { |
| 52 | + playerScore = 0; |
| 53 | + computerScore = 0; |
| 54 | + |
| 55 | + playerOneScoreElement.textContent = "Player: " + playerScore; |
| 56 | + playerTwoScoreElement.textContent = "Computer: " + computerScore; |
| 57 | + |
| 58 | + resetModal.close(); |
| 59 | + gameRunning = true; |
| 60 | +} |
| 61 | + |
| 62 | +function cancelReset() { |
| 63 | + resetModal.close(); |
| 64 | + gameRunning = true; |
| 65 | +} // Draw Functions |
| 66 | +/** |
| 67 | + * @param {number} x |
| 68 | + * @param {number} y |
| 69 | + * @param {number} width |
| 70 | + * @param {number} height |
| 71 | + * @param {string} color |
| 72 | + */ |
| 73 | +function drawRect(x, y, width, height, color) { |
| 74 | + ctx.fillStyle = color; |
| 75 | + ctx.fillRect(x, y, width, height); |
| 76 | +} |
| 77 | + |
| 78 | +/** |
| 79 | + * @param {number} x |
| 80 | + * @param {number} y |
| 81 | + * @param {number} radius |
| 82 | + * @param {string} color |
| 83 | + */ |
| 84 | +function drawCircle(x, y, radius, color) { |
| 85 | + ctx.fillStyle = color; |
| 86 | + ctx.beginPath(); |
| 87 | + ctx.arc(x, y, radius, 0, Math.PI * 2, false); |
| 88 | + ctx.fill(); |
| 89 | +} |
| 90 | + |
| 91 | +function drawSeparator() { |
| 92 | + ctx.beginPath(); |
| 93 | + ctx.setLineDash([5, 15]); |
| 94 | + |
| 95 | + ctx.moveTo(canvas?.width / 2, 0); |
| 96 | + |
| 97 | + ctx.lineTo(canvas?.height, canvas?.width / 2); |
| 98 | + ctx.strokeStyle = "#8dff41"; |
| 99 | + |
| 100 | + ctx.stroke(); |
| 101 | +} |
| 102 | + |
| 103 | +function draw() { |
| 104 | + // Clear Canvas |
| 105 | + drawRect(0, 0, canvas?.width, canvas?.height, "#005430"); |
| 106 | + |
| 107 | + drawSeparator(); |
| 108 | + |
| 109 | + // Draw Paddles |
| 110 | + drawRect(0, paddle1Y, paddleWidth, paddleHeight, "#00df86"); |
| 111 | + drawRect( |
| 112 | + canvas?.width - paddleWidth, |
| 113 | + paddle2Y, |
| 114 | + paddleWidth, |
| 115 | + paddleHeight, |
| 116 | + "#00df86" |
| 117 | + ); |
| 118 | + |
| 119 | + // Draw Ball |
| 120 | + drawCircle(ballX, ballY, ballSize, "#92e84c"); |
| 121 | +} |
| 122 | + |
| 123 | +function increasePlayerScore() { |
| 124 | + playerScore++; |
| 125 | + playerOneScoreElement.textContent = "Player: " + playerScore.toString(); |
| 126 | +} |
| 127 | + |
| 128 | +function increaseComputerScore() { |
| 129 | + computerScore++; |
| 130 | + playerTwoScoreElement.textContent = "Computer: " + computerScore.toString(); |
| 131 | +} |
| 132 | + |
| 133 | +function update() { |
| 134 | + if (!gameRunning) return; |
| 135 | + |
| 136 | + // Move Ball |
| 137 | + ballX += ballSpeedX; |
| 138 | + ballY += ballSpeedY; |
| 139 | + |
| 140 | + // Ball Collision with Top and Bottom Walls |
| 141 | + if (ballY - ballSize < 0 || ballY + ballSize > canvas?.height) { |
| 142 | + ballSpeedY = -ballSpeedY; |
| 143 | + } |
| 144 | + |
| 145 | + // Left paddle collision |
| 146 | + if ( |
| 147 | + ballX - ballSize <= paddleWidth && |
| 148 | + ballX - ballSize > 0 && |
| 149 | + ballY >= paddle1Y && |
| 150 | + ballY <= paddle1Y + paddleHeight && |
| 151 | + ballSpeedX < 0 |
| 152 | + ) { |
| 153 | + ballSpeedX = -ballSpeedX; |
| 154 | + ballX = paddleWidth + ballSize; |
| 155 | + } |
| 156 | + |
| 157 | + // Right paddle collision |
| 158 | + if ( |
| 159 | + ballX + ballSize >= canvas?.width - paddleWidth && |
| 160 | + ballX + ballSize < canvas?.width && |
| 161 | + ballY >= paddle2Y && |
| 162 | + ballY <= paddle2Y + paddleHeight && |
| 163 | + ballSpeedX > 0 |
| 164 | + ) { |
| 165 | + ballSpeedX = -ballSpeedX; |
| 166 | + ballX = canvas?.width - paddleWidth - ballSize; |
| 167 | + } |
| 168 | + |
| 169 | + const paddle2Center = paddleHeight / 2 + paddle2Y; |
| 170 | + if (paddle2Center < ballY - 35) { |
| 171 | + paddle2Y += 6; |
| 172 | + } else if (paddle2Center > ballY + 35) { |
| 173 | + paddle2Y -= 6; |
| 174 | + } |
| 175 | + |
| 176 | + // Ball Out of Bounds |
| 177 | + if (ballX - ballSize < 0) { |
| 178 | + increaseComputerScore(); |
| 179 | + resetBall(); |
| 180 | + } else if (ballX + ballSize > canvas?.width) { |
| 181 | + increasePlayerScore(); |
| 182 | + resetBall(); |
| 183 | + } |
| 184 | +} |
| 185 | + |
| 186 | +function resetBall() { |
| 187 | + ballX = canvas?.width / 2; |
| 188 | + ballY = canvas?.height / 2; |
| 189 | + ballSpeedX = -ballSpeedX; |
| 190 | +} |
| 191 | + |
| 192 | +function calculateMousePos(evt) { |
| 193 | + const rect = canvas.getBoundingClientRect(); |
| 194 | + const root = document.documentElement; |
| 195 | + const mouseX = evt.clientX - rect.left - root.scrollLeft; |
| 196 | + const mouseY = evt.clientY - rect.top - root.scrollTop; |
| 197 | + return { |
| 198 | + x: mouseX, |
| 199 | + y: mouseY, |
| 200 | + }; |
| 201 | +} |
| 202 | + |
| 203 | +canvas.addEventListener("mousemove", function (evt) { |
| 204 | + const mousePos = calculateMousePos(evt); |
| 205 | + paddle1Y = mousePos.y - paddleHeight / 2; |
| 206 | + paddle1Y = Math.min(Math.max(0, paddle1Y), canvas?.height - paddleHeight); |
| 207 | +}); |
| 208 | + |
| 209 | +// Game Loop |
| 210 | +/** |
| 211 | + * @param {number | undefined} [currentTime] |
| 212 | + */ |
| 213 | +function gameLoop(currentTime) { |
| 214 | + if (lastTime !== undefined && currentTime !== undefined) { |
| 215 | + delta = Math.round(currentTime - lastTime); |
| 216 | + lastTime = currentTime; |
| 217 | + } |
| 218 | + draw(); |
| 219 | + update(); |
| 220 | + requestAnimationFrame(gameLoop); |
| 221 | +} |
| 222 | + |
| 223 | +startGameButton?.addEventListener("click", startGame); |
| 224 | +resetGameButton?.addEventListener("click", resetGame); |
| 225 | +confirmResetButton?.addEventListener("click", confirmReset); |
| 226 | +cancelResetButton?.addEventListener("click", cancelReset); |
| 227 | + |
| 228 | +// Close modal when clicking outside of it |
| 229 | +resetModal?.addEventListener("click", (e) => { |
| 230 | + if (e.target === resetModal) { |
| 231 | + resetModal.close(); |
| 232 | + gameRunning = true; |
| 233 | + } |
| 234 | +}); |
0 commit comments