ご要望いただきありがとうございます。以下が実装例になります。 ``` <!DOCTYPE html> <html> <head> <meta charset="UTF-8"> <title>ブロック崩し</title> <style> body { margin: 0; padding: 0; } canvas { background-color: black; display: block; margin: 0 auto; padding: 0; position: absolute; top: 0; bottom: 0; left: 0; right: 0; } </style> </head> <body> <canvas id="canvas"></canvas> <script> const canvas = document.getElementById("canvas"); const ctx = canvas.getContext("2d"); const ROW_COUNT = 5; const COLUMN_COUNT = 8; const BLOCK_WIDTH = canvas.width / (COLUMN_COUNT + 1); const BLOCK_HEIGHT = 20; let blocks = []; for(let i = 0; i < COLUMN_COUNT; i++) { blocks[i] = []; for(let j = 0; j < ROW_COUNT; j++) { let emoji; switch(j) { case 0: emoji = "🐣"; break; case 1: emoji = "🐔"; break; default: emoji = "🥚"; break; } blocks[i][j] = { x: (i + 1) * BLOCK_WIDTH, y: (j + 1) * BLOCK_HEIGHT, width: BLOCK_WIDTH, height: BLOCK_HEIGHT, emoji: emoji, alive: true }; } } const BALL_COUNT = 5; let balls = []; for(let i = 0; i < BALL_COUNT; i++) { balls.push({ x: canvas.width / 2, y: canvas.height - 30, radius: 10, speedX: Math.random() * 6 - 3, speedY: -5, alive: true }); } const BAR_WIDTH = 30; const BAR_HEIGHT = 10; const BAR_SPEED = 10; let bar = { x: canvas.width / 2 - BAR_WIDTH / 2, y: canvas.height - BAR_HEIGHT * 2, width: BAR_WIDTH, height: BAR_HEIGHT }; let score = 0; let life = BALL_COUNT; let stage = 1; let item = null; let itemX; let itemY; canvas.addEventListener("mousemove", function(event) { bar.x = event.clientX - canvas.offsetLeft - BAR_WIDTH / 2; }); function drawBall() { let aliveBallCount = 0; for(let i = 0; i < balls.length; i++) { if(balls[i].alive) { ctx.beginPath(); ctx.arc(balls[i].x, balls[i].y, balls[i].radius, 0, Math.PI * 2); ctx.fillStyle = "white"; ctx.fill(); ctx.closePath(); aliveBallCount++; } } if(aliveBallCount == 0) { life--; if(life > 0) { resetBall(); } } if(life <= 0) { alert("Game Over"); showToast("再読み込みしてください"); } } function drawBar() { ctx.beginPath(); ctx.rect(bar.x, bar.y, bar.width, bar.height); ctx.fillStyle = "white"; ctx.fill(); ctx.closePath(); } function drawBlocks() { let aliveBlockCount = 0; for(let i = 0; i < blocks.length; i++) { for(let j = 0; j < blocks[i].length; j++) { if(blocks[i][j].alive) { ctx.beginPath(); ctx.rect(blocks[i][j].x, blocks[i][j].y, blocks[i][j].width, blocks[i][j].height); ctx.fillStyle = "white"; ctx.fill(); ctx.font = "14px sans-serif"; ctx.fillText(blocks[i][j].emoji, blocks[i][j].x + blocks[i][j].width / 3, blocks[i][j].y + blocks[i][j].height / 2); ctx.closePath(); aliveBlockCount++; } } } if(aliveBlockCount == 0) { stage++; if(stage <= 5) { resetStage(); } else { alert(`Stage Clear! Your score is ${score}.`); showToast("再読み込みしてください"); } } } function drawItem() { if(item) { ctx.beginPath(); ctx.arc(itemX, itemY, 10, 0, Math.PI * 2); ctx.fillStyle = "#FFD700"; ctx.fill(); ctx.closePath(); itemY += 3; if(itemY > canvas.height) { item = null; } if(checkCollision(itemX, itemY, 10, bar.x, bar.y, bar.width, bar.height)) { switch(item.type) { case "score": score += 10; break; case "life": life++; break; case "barWidth": bar.width += 5; break; } item = null; } } else { if(Math.random() < 0.002) { let itemType = Math.floor(Math.random() * 3); let itemX = Math.random() * canvas.width; item = { type: itemType == 0 ? "score" : itemType == 1 ? "life" : "barWidth" }; switch(item.type) { case "score": item.emoji = "💯"; break; case "life": item.emoji = "❤️"; break; case "barWidth": item.emoji = "📏"; break; } this.itemX = itemX; this.itemY = 0; } } } function resetBall() { let ballIndex = balls.findIndex(ball => !ball.alive); if(ballIndex != -1) { balls[ballIndex].x = canvas.width / 2; balls[ballIndex].y = canvas.height - 30; balls[ballIndex].speedX = Math.random() * 6 - 3; balls[ballIndex].speedY = -5; balls[ballIndex].alive = true; } } function resetStage() { for(let i = 0; i < BLOCK_WIDTH; i++) { for(let j = 0; j < BLOCK_HEIGHT; j++) { blocks[i][j].alive = true; } } resetBall(); } function checkCollision(x1, y1, r1, x2, y2, w2, h2) { if(x1 + r1 < x2 || x1 - r1 > x2 + w2 || y1 + r1 < y2 || y1 - r1 > y2 + h2) { return false; } else { return true; } } function update() { for(let i = 0; i < balls.length; i++) { if(!balls[i].alive) { continue; } let ball = balls[i]; let nextX = ball.x + ball.speedX; let nextY = ball.y + ball.speedY; if(nextX < ball.radius || nextX > canvas.width - ball.radius) { ball.speedX = -ball.speedX; } if(nextY < ball.radius) { ball.speedY = -ball.speedY; } else if(nextY > canvas.height - ball.radius) { ball.alive = false; } else if(checkCollision(nextX, nextY, ball.radius, bar.x, bar.y, bar.width, bar.height)) { ball.speedY = -ball.speedY; ball.speedX = (ball.x - bar.x - bar.width / 2) / BAR_WIDTH * 10; } else { for(let j = 0; j < blocks.length; j++) { for(let k = 0; k < blocks[j].length; k++) { if(blocks[j][k].alive && checkCollision(nextX, nextY, ball.radius, blocks[j][k].x, blocks[j][k].y, blocks[j][k].width, blocks[j][k].height)) { blocks[j][k].alive = false; if(Math.random() < 0.1) { item = { type: "score" }; itemX = blocks[j][k].x + blocks[j][k].width / 2; itemY = blocks[j][k].y + blocks[j][k].height / 2; } score += (ROW_COUNT - k) * 10; if(nextX < blocks[j][k].x || nextX > blocks[j][k].x + blocks[j][k].width) { ball.speedX = -ball.speedX; } else { ball.speedY = -ball.speedY; } break; } } } } ball.x += ball.speedX; ball.y += ball.speedY; } } function draw() { ctx.clearRect(0, 0, canvas.width, canvas.height); drawBall(); drawBar(); drawBlocks(); drawItem(); update(); ctx.font = "18px sans-serif"; ctx.fillStyle = "white"; ctx.fillText(`Score: ${score} Life: ${life} Stage: ${stage}`, 10, canvas.height - 10); requestAnimationFrame(draw); } draw(); </script> </body> </html> ```