打砖块 · 经典彩砖
by PrismDolphin13352 lines11.3 KB
<!DOCTYPE html>
<html lang="zh">
<head>
<meta charset="UTF-8">
<meta name="viewport" content="width=device-width, initial-scale=1.0">
<title>打砖块 · 经典彩砖</title>
<style>
* { margin: 0; padding: 0; box-sizing: border-box; }
body {
background: #1a1a2e;
display: flex;
justify-content: center;
align-items: center;
height: 100vh;
font-family: 'Segoe UI', sans-serif;
}
.game-wrap {
background: #16213e;
padding: 20px 30px 30px 30px;
border-radius: 20px;
box-shadow: 0 10px 30px rgba(0, 0, 0, 0.6);
text-align: center;
}
canvas {
display: block;
margin: 0 auto;
border-radius: 12px;
background: #0f0f23;
box-shadow: 0 0 20px rgba(0, 255, 255, 0.15);
cursor: none;
}
.info {
display: flex;
justify-content: space-between;
color: #a0f0e0;
font-size: 18px;
font-weight: bold;
margin-top: 12px;
padding: 0 6px;
}
.info span {
background: #1f2a48;
padding: 4px 18px;
border-radius: 30px;
color: #ffd700;
}
.info .lives {
color: #ff6b8a;
}
.info .score {
color: #7af0ff;
}
.status {
margin-top: 10px;
color: #ffaa66;
font-size: 16px;
min-height: 28px;
}
button {
background: #ff6b6b;
border: none;
padding: 8px 28px;
border-radius: 30px;
font-size: 16px;
font-weight: bold;
color: #fff;
cursor: pointer;
transition: 0.2s;
box-shadow: 0 4px 0 #a04545;
}
button:hover {
transform: translateY(-2px);
box-shadow: 0 6px 0 #a04545;
}
button:active {
transform: translateY(2px);
box-shadow: 0 2px 0 #a04545;
}
</style>
</head>
<body>
<div class="game-wrap">
<canvas id="gameCanvas" width="600" height="400"></canvas>
<div class="info">
<span class="score">🏆 <span id="scoreDisplay">0</span></span>
<span class="lives">❤️ × <span id="livesDisplay">3</span></span>
</div>
<div class="status" id="statusMsg">🎯 鼠标移动挡板 · 打掉所有砖块</div>
<button id="restartBtn">🔄 重新开始</button>
</div>
<script>
const canvas = document.getElementById('gameCanvas');
const ctx = canvas.getContext('2d');
const scoreSpan = document.getElementById('scoreDisplay');
const livesSpan = document.getElementById('livesDisplay');
const statusMsg = document.getElementById('statusMsg');
// --- 游戏参数 ---
const W = 600, H = 400;
const PADDLE_W = 100, PADDLE_H = 14, PADDLE_Y = 370;
const BALL_RADIUS = 8;
const BRICK_ROWS = 5, BRICK_COLS = 8;
const BRICK_W = 64, BRICK_H = 20, BRICK_GAP = 4;
const BRICK_TOP = 40;
// --- 游戏状态 ---
let paddle = { x: W/2 - PADDLE_W/2 };
let ball = { x: W/2, y: PADDLE_Y - BALL_RADIUS, dx: 3, dy: -4 };
let bricks = [];
let score = 0;
let lives = 3;
let gameRunning = true;
let animationId = null;
// --- 初始化砖块 ---
function initBricks() {
bricks = [];
const colors = ['#ff6b6b', '#ffd93d', '#6bcb77', '#4d96ff', '#9b59b6'];
for (let r = 0; r < BRICK_ROWS; r++) {
for (let c = 0; c < BRICK_COLS; c++) {
const x = c * (BRICK_W + BRICK_GAP) + BRICK_GAP/2 + 20;
const y = r * (BRICK_H + BRICK_GAP) + BRICK_TOP;
bricks.push({
x, y, w: BRICK_W, h: BRICK_H,
alive: true,
color: colors[r % colors.length]
});
}
}
}
// --- 重置游戏 ---
function resetGame() {
paddle.x = W/2 - PADDLE_W/2;
ball.x = W/2;
ball.y = PADDLE_Y - BALL_RADIUS - 2;
ball.dx = (Math.random() > 0.5 ? 1 : -1) * 3.5;
ball.dy = -4.5;
lives = 3;
score = 0;
gameRunning = true;
statusMsg.textContent = '🎯 鼠标移动挡板 · 打掉所有砖块';
updateDisplay();
initBricks();
}
// --- 更新界面数字 ---
function updateDisplay() {
scoreSpan.textContent = score;
livesSpan.textContent = lives;
}
// --- 碰撞检测 ---
function ballRectCollision(ball, rect) {
const cx = ball.x, cy = ball.y, r = BALL_RADIUS;
const rx = rect.x, ry = rect.y, rw = rect.w, rh = rect.h;
const nearestX = Math.max(rx, Math.min(cx, rx + rw));
const nearestY = Math.max(ry, Math.min(cy, ry + rh));
const dx = cx - nearestX;
const dy = cy - nearestY;
return (dx * dx + dy * dy) < (r * r);
}
// --- 更新逻辑 ---
function update() {
if (!gameRunning) return;
// 移动球
ball.x += ball.dx;
ball.y += ball.dy;
// 左右墙反弹
if (ball.x - BALL_RADIUS < 0 || ball.x + BALL_RADIUS > W) {
ball.dx = -ball.dx;
}
// 上墙反弹
if (ball.y - BALL_RADIUS < 0) {
ball.dy = -ball.dy;
}
// 下边界(丢球)
if (ball.y + BALL_RADIUS > H) {
lives--;
updateDisplay();
if (lives <= 0) {
gameRunning = false;
statusMsg.textContent = '💀 游戏结束!点击"重新开始"';
return;
} else {
// 重置球和挡板位置
ball.x = W/2;
ball.y = PADDLE_Y - BALL_RADIUS - 2;
ball.dx = (Math.random() > 0.5 ? 1 : -1) * 3.5;
ball.dy = -4.5;
paddle.x = W/2 - PADDLE_W/2;
statusMsg.textContent = `❤️ 剩余 ${lives} 条命`;
return;
}
}
// 挡板碰撞
if (ball.dy > 0 &&
ball.y + BALL_RADIUS >= PADDLE_Y &&
ball.y + BALL_RADIUS <= PADDLE_Y + PADDLE_H + 4 &&
ball.x >= paddle.x - BALL_RADIUS &&
ball.x <= paddle.x + PADDLE_W + BALL_RADIUS) {
// 根据击打位置改变角度
const hitPos = (ball.x - paddle.x) / PADDLE_W; // 0~1
const angle = (hitPos - 0.5) * 1.2; // -0.6 ~ 0.6 弧度
const speed = Math.sqrt(ball.dx*ball.dx + ball.dy*ball.dy);
ball.dx = speed * Math.sin(angle);
ball.dy = -speed * Math.cos(angle);
// 限制最小垂直速度
if (Math.abs(ball.dy) < 2) {
ball.dy = -2.5;
}
ball.y = PADDLE_Y - BALL_RADIUS - 1;
// 防止卡住
}
// 砖块碰撞
for (let i = 0; i < bricks.length; i++) {
const b = bricks[i];
if (!b.alive) continue;
if (ballRectCollision(ball, b)) {
b.alive = false;
score++;
updateDisplay();
// 反弹方向(根据碰撞方向)
const overlapX = (ball.x > b.x + b.w/2) ? (b.x + b.w - ball.x + BALL_RADIUS) : (ball.x - BALL_RADIUS - b.x);
const overlapY = (ball.y > b.y + b.h/2) ? (b.y + b.h - ball.y + BALL_RADIUS) : (ball.y - BALL_RADIUS - b.y);
if (overlapX < overlapY) {
ball.dx = -ball.dx;
} else {
ball.dy = -ball.dy;
}
// 检查是否全部打掉
if (bricks.every(brick => !brick.alive)) {
gameRunning = false;
statusMsg.textContent = '🎉 恭喜!你赢了!点击"重新开始"继续';
}
break;
}
}
}
// --- 绘制 ---
function draw() {
ctx.clearRect(0, 0, W, H);
// 绘制砖块
bricks.forEach(b => {
if (!b.alive) return;
ctx.shadowColor = b.color;
ctx.shadowBlur = 12;
ctx.fillStyle = b.color;
ctx.beginPath();
ctx.roundRect(b.x, b.y, b.w, b.h, 6);
ctx.fill();
// 高光
ctx.shadowBlur = 0;
ctx.fillStyle = 'rgba(255,255,255,0.2)';
ctx.beginPath();
ctx.roundRect(b.x+2, b.y+2, b.w-4, 6, 4);
ctx.fill();
});
ctx.shadowBlur = 0;
// 绘制挡板
ctx.shadowColor = '#00ccff';
ctx.shadowBlur = 20;
ctx.fillStyle = '#00bbff';
ctx.beginPath();
ctx.roundRect(paddle.x, PADDLE_Y, PADDLE_W, PADDLE_H, 8);
ctx.fill();
// 挡板高光
ctx.shadowBlur = 0;
ctx.fillStyle = 'rgba(255,255,255,0.3)';
ctx.beginPath();
ctx.roundRect(paddle.x+10, PADDLE_Y-2, PADDLE_W-20, 6, 4);
ctx.fill();
// 绘制球
ctx.shadowColor = '#ffaa44';
ctx.shadowBlur = 25;
ctx.fillStyle = '#ffaa44';
ctx.beginPath();
ctx.arc(ball.x, ball.y, BALL_RADIUS, 0, 2*Math.PI);
ctx.fill();
// 高光
ctx.shadowBlur = 0;
ctx.fillStyle = 'rgba(255,255,255,0.6)';
ctx.beginPath();
ctx.arc(ball.x-2, ball.y-3, 3, 0, 2*Math.PI);
ctx.fill();
ctx.shadowBlur = 0;
}
// 扩展 roundRect
CanvasRenderingContext2D.prototype.roundRect = function(x, y, w, h, r) {
if (w < 2*r) r = w/2;
if (h < 2*r) r = h/2;
this.moveTo(x+r, y);
this.lineTo(x+w-r, y);
this.quadraticCurveTo(x+w, y, x+w, y+r);
this.lineTo(x+w, y+h-r);
this.quadraticCurveTo(x+w, y+h, x+w-r, y+h);
this.lineTo(x+r, y+h);
this.quadraticCurveTo(x, y+h, x, y+h-r);
this.lineTo(x, y+r);
this.quadraticCurveTo(x, y, x+r, y);
this.closePath();
return this;
};
// --- 游戏循环 ---
function gameLoop() {
update();
draw();
animationId = requestAnimationFrame(gameLoop);
}
// --- 启动游戏 ---
function startGame() {
if (animationId) cancelAnimationFrame(animationId);
resetGame();
gameLoop();
}
// --- 鼠标控制挡板 ---
canvas.addEventListener('mousemove', (e) => {
if (!gameRunning) return;
const rect = canvas.getBoundingClientRect();
const scaleX = canvas.width / rect.width;
const mouseX = (e.clientX - rect.left) * scaleX;
let newX = mouseX - PADDLE_W/2;
if (newX < 0) newX = 0;
if (newX + PADDLE_W > W) newX = W - PADDLE_W;
paddle.x = newX;
});
// --- 重新开始按钮 ---
document.getElementById('restartBtn').addEventListener('click', () => {
startGame();
});
// --- 自动启动 ---
startGame();
</script>
</body>
</html>Game Source: 打砖块 · 经典彩砖
Creator: PrismDolphin13
Libraries: none
Complexity: complex (352 lines, 11.3 KB)
The full source code is displayed above on this page.
Remix Instructions
To remix this game, copy the source code above and modify it. Add a ARCADELAB header at the top with "remix_of: game-prismdolphin13" to link back to the original. Then publish at arcadelab.ai/publish.