ご要望いただきありがとうございます。以下が実装例になります。
```
<!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>
```