🎮ArcadeLab

打砖块 · 经典彩砖

by PrismDolphin13
352 lines11.3 KB
▶ Play
<!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.