AI 编程实践:用 Trae 快速开发 HTML 打砖块小游戏

在 AI 编程助手逐渐融入日常的今天,像"打砖块"这种交互小游戏,过去也许要半天才能从零实现;现在借助 Trae 生成基础骨架,再做少量调整与打磨,就能很快产出一个手感顺畅、兼顾键鼠与触控的网页游戏。

本篇文章会带你从 零到可玩,在 HTML 页面里一步步构建游戏,代码和解释紧密穿插,读完就能直接运行。


1. 游戏功能与技术选型

1.1 功能需求

  • 画布自适配高清屏(DPR 适配)
  • 球拍、弹球、五行九列彩色砖块
  • 碰撞检测:墙壁 / 球拍 / 砖块
  • 计分与生命值,速度等级随进度提升
  • 键盘、鼠标与移动端触控三种操控
  • 开始 / 暂停 / 重置控制与提示面板

1.2 技术栈

  • HTML5 Canvas:绘制与像素级碰撞
  • CSS3:现代化面板、按钮、响应式布局
  • JavaScript:游戏循环、状态管理、事件处理
  • Trae:生成页面框架、核心对象与事件绑定雏形

2. 页面与样式骨架

Trae 可以快速帮我们生成 HTML 结构和 CSS 样式,我们再在此基础上补充游戏逻辑。

html 复制代码
<!DOCTYPE html>
<html lang="zh-CN">
<head>
<meta charset="UTF-8">
<title>Trae 打砖块</title>
<style>
  :root {
    --primary-color: #4CAF50;
    --secondary-color: #ff9800;
    --bg-color: #222;
    --text-color: #fff;
  }
  body {
    background: var(--bg-color);
    color: var(--text-color);
    font-family: Arial, sans-serif;
    text-align: center;
    margin: 0;
    padding: 0;
  }
  #gameInfo {
    display: flex;
    justify-content: space-around;
    padding: 10px;
    background: var(--primary-color);
  }
  #gameCanvas {
    background: #000;
    display: block;
    margin: 0 auto;
    border: 2px solid var(--secondary-color);
  }
  .btn {
    background: var(--secondary-color);
    border: none;
    padding: 10px 20px;
    margin: 5px;
    color: #fff;
    cursor: pointer;
    border-radius: 5px;
  }
</style>
</head>
<body>
  <div id="gameInfo">
    <span id="score">分数: 0</span>
    <span id="lives">生命: 3</span>
    <span id="speed">速度等级: 1</span>
  </div>
  <canvas id="gameCanvas" width="800" height="600"></canvas>
  <div>
    <button id="startBtn" class="btn">开始</button>
    <button id="pauseBtn" class="btn">暂停</button>
    <button id="resetBtn" class="btn">重置</button>
  </div>
<script src="game.js"></script>
</body>
</html>

💡 说明

  • 顶部 #gameInfo 展示实时分数、生命和速度等级。
  • <canvas> 作为游戏绘制区域,初始 800×600,稍后用 JavaScript 动态缩放以适配 DPR。
  • 三个按钮用来控制游戏状态。

3. JavaScript:初始化与 DPR 适配

为了在高清屏幕上保持清晰,我们会获取 window.devicePixelRatio 进行缩放。

js 复制代码
const canvas = document.getElementById('gameCanvas');
const ctx = canvas.getContext('2d');
let dpr = window.devicePixelRatio || 1;

// 逻辑尺寸
const WIDTH = 800;
const HEIGHT = 600;

// DPR 适配
canvas.width = WIDTH * dpr;
canvas.height = HEIGHT * dpr;
canvas.style.width = WIDTH + 'px';
canvas.style.height = HEIGHT + 'px';
ctx.scale(dpr, dpr);

📌 关键点

  1. canvas.width / canvas.height实际像素尺寸 ,要乘上 dpr
  2. canvas.style.width / style.height 是 CSS 尺寸,保持逻辑大小。
  3. ctx.scale(dpr, dpr) 让绘制坐标依然用逻辑尺寸计算。

4. 游戏对象与状态

我们先定义球拍、球、砖块数组等核心对象。

js 复制代码
let score = 0;
let lives = 3;
let speedLevel = 1;

const paddle = {
  width: 100,
  height: 15,
  x: WIDTH / 2 - 50,
  y: HEIGHT - 30,
  dx: 7
};

const ball = {
  x: WIDTH / 2,
  y: HEIGHT - 50,
  radius: 10,
  speed: 4,
  dx: 4,
  dy: -4
};

const BRICK_ROWS = 5;
const BRICK_COLS = 9;
const BRICK_WIDTH = 70;
const BRICK_HEIGHT = 20;
const BRICK_PADDING = 10;
const BRICK_OFFSET_TOP = 50;
const BRICK_OFFSET_LEFT = 35;
let bricks = [];

function createBricks() {
  bricks = [];
  for (let c = 0; c < BRICK_COLS; c++) {
    bricks[c] = [];
    for (let r = 0; r < BRICK_ROWS; r++) {
      const brickX = c * (BRICK_WIDTH + BRICK_PADDING) + BRICK_OFFSET_LEFT;
      const brickY = r * (BRICK_HEIGHT + BRICK_PADDING) + BRICK_OFFSET_TOP;
      bricks[c][r] = { x: brickX, y: brickY, status: 1 };
    }
  }
}

createBricks();

💡 解释

  • 球拍位置固定在底部,宽 100,高 15,可左右移动。
  • 球初始在球拍上方,速度 dx / dy 控制方向。
  • 砖块是一个二维数组 [列][行],每个砖块存储 x / y 坐标和 status(1 = 存活,0 = 已打掉)。

5. 绘制函数

js 复制代码
function drawPaddle() {
  ctx.fillStyle = '#0095DD';
  ctx.fillRect(paddle.x, paddle.y, paddle.width, paddle.height);
}

function drawBall() {
  ctx.beginPath();
  ctx.arc(ball.x, ball.y, ball.radius, 0, Math.PI * 2);
  ctx.fillStyle = '#ff5722';
  ctx.fill();
  ctx.closePath();
}

function drawBricks() {
  for (let c = 0; c < BRICK_COLS; c++) {
    for (let r = 0; r < BRICK_ROWS; r++) {
      if (bricks[c][r].status === 1) {
        ctx.fillStyle = `hsl(${r * 50}, 70%, 50%)`;
        ctx.fillRect(bricks[c][r].x, bricks[c][r].y, BRICK_WIDTH, BRICK_HEIGHT);
      }
    }
  }
}

📌 小技巧

  • hsl() 根据行号生成不同颜色,让砖块更有层次感。
  • 绘制顺序:砖块 → 球拍 → 球,保证视觉正确。

6. 碰撞检测

js 复制代码
function collisionDetection() {
  for (let c = 0; c < BRICK_COLS; c++) {
    for (let r = 0; r < BRICK_ROWS; r++) {
      const b = bricks[c][r];
      if (b.status === 1) {
        if (
          ball.x > b.x && ball.x < b.x + BRICK_WIDTH &&
          ball.y > b.y && ball.y < b.y + BRICK_HEIGHT
        ) {
          ball.dy = -ball.dy;
          b.status = 0;
          score += 10;
          if (score % 100 === 0) {
            speedLevel++;
            ball.speed += 0.5;
          }
        }
      }
    }
  }
}

💡 原理

  • 简单的包围盒检测(AABB),判断球心是否进入砖块矩形区域。
  • 命中后反转 dy,加分,定期提速。

7. 游戏循环

js 复制代码
function draw() {
  ctx.clearRect(0, 0, WIDTH, HEIGHT);
  drawBricks();
  drawPaddle();
  drawBall();
  collisionDetection();

  // 移动球
  ball.x += ball.dx;
  ball.y += ball.dy;

  // 左右墙
  if (ball.x + ball.radius > WIDTH || ball.x - ball.radius < 0) {
    ball.dx = -ball.dx;
  }
  // 上墙
  if (ball.y - ball.radius < 0) {
    ball.dy = -ball.dy;
  }
  // 下边界
  if (ball.y + ball.radius > HEIGHT) {
    lives--;
    if (!lives) {
      alert('游戏结束');
      document.location.reload();
    } else {
      resetBall();
    }
  }

  // 球拍碰撞
  if (
    ball.x > paddle.x &&
    ball.x < paddle.x + paddle.width &&
    ball.y + ball.radius > paddle.y
  ) {
    ball.dy = -ball.dy;
  }

  requestAnimationFrame(draw);
}

function resetBall() {
  ball.x = WIDTH / 2;
  ball.y = HEIGHT - 50;
  ball.dx = 4;
  ball.dy = -4;
}

draw();

📌 要点

  • requestAnimationFrame 保证帧同步与性能。
  • resetBall() 让球回到初始位置,游戏不中断。

8. 输入控制

js 复制代码
document.addEventListener('keydown', e => {
  if (e.key === 'ArrowLeft') paddle.x -= paddle.dx;
  if (e.key === 'ArrowRight') paddle.x += paddle.dx;
});

canvas.addEventListener('mousemove', e => {
  const rect = canvas.getBoundingClientRect();
  const mouseX = e.clientX - rect.left;
  paddle.x = mouseX - paddle.width / 2;
});

💡 说明

  • 键盘控制适合 PC,鼠标更直观,移动端可以用 touchstart/touchmove 做。

9. 可选优化

  1. 球拍反弹角度控制 用击中位置计算反弹方向,让玩家可控。
js 复制代码
const hit = (ball.x - paddle.x) / paddle.width;
const angle = hit * Math.PI - Math.PI / 2;
ball.dx = ball.speed * Math.cos(angle);
ball.dy = -ball.speed * Math.sin(angle);
  1. 防穿透 当速度过快时,可分多次小步移动并检测碰撞。

  2. 粒子特效 命中砖块时生成小粒子,让反馈更爽。


10. 总结

用 Trae 生成骨架,能让我们迅速进入手感调优环节,不用耗时间在 UI 布局和基础事件上。这种"AI 辅助 + 人工优化"的工作方式特别适合入门游戏开发的练习项目,也适合原型验证。

附:完整代码

html 复制代码
<!DOCTYPE html>
<html lang="zh-CN">
<head>
    <meta charset="UTF-8">
    <meta name="viewport" content="width=device-width, initial-scale=1.0">
    <title>打砖块小游戏</title>
    <style>
        :root {
            --primary-color: #3498db;
            --secondary-color: #2ecc71;
            --accent-color: #e74c3c;
            --text-color: #333;
            --bg-color: #f5f5f5;
            --panel-color: #fff;
            --border-radius: 8px;
            --box-shadow: 0 4px 6px rgba(0, 0, 0, 0.1);
        }

        * {
            margin: 0;
            padding: 0;
            box-sizing: border-box;
        }

        body {
            font-family: 'Segoe UI', Tahoma, Geneva, Verdana, sans-serif;
            background-color: var(--bg-color);
            color: var(--text-color);
            display: flex;
            flex-direction: column;
            align-items: center;
            min-height: 100vh;
            padding: 20px;
        }

        h1 {
            margin-bottom: 15px;
            color: var(--primary-color);
            text-align: center;
        }

        .game-container {
            position: relative;
            width: 100%;
            max-width: 800px;
            display: flex;
            flex-direction: column;
            align-items: center;
        }

        .game-info {
            display: flex;
            justify-content: space-between;
            width: 100%;
            background-color: var(--panel-color);
            padding: 15px 20px;
            border-radius: var(--border-radius) var(--border-radius) 0 0;
            box-shadow: var(--box-shadow);
            font-weight: bold;
            font-size: 1.1rem;
        }

        .canvas-container {
            position: relative;
            width: 100%;
            background-color: #000;
            box-shadow: var(--box-shadow);
            overflow: hidden;
        }

        canvas {
            display: block;
            width: 100%;
            height: auto;
        }

        .game-controls {
            display: flex;
            justify-content: center;
            gap: 15px;
            margin-top: 20px;
            width: 100%;
            flex-wrap: wrap;
        }

        .control-btn {
            padding: 10px 20px;
            background-color: var(--primary-color);
            color: white;
            border: none;
            border-radius: var(--border-radius);
            cursor: pointer;
            font-size: 1rem;
            transition: background-color 0.3s;
            flex: 1;
            min-width: 120px;
            max-width: 200px;
        }

        .control-btn:hover {
            background-color: #2980b9;
        }

        .control-btn.secondary {
            background-color: var(--secondary-color);
        }

        .control-btn.secondary:hover {
            background-color: #27ae60;
        }

        .control-btn.danger {
            background-color: var(--accent-color);
        }

        .control-btn.danger:hover {
            background-color: #c0392b;
        }

        .mobile-controls {
            display: none;
            width: 100%;
            margin-top: 20px;
        }

        .touch-area {
            display: flex;
            height: 80px;
        }

        .touch-left, .touch-right {
            flex: 1;
            background-color: rgba(52, 152, 219, 0.2);
            border-radius: var(--border-radius);
            display: flex;
            justify-content: center;
            align-items: center;
            font-size: 2rem;
            cursor: pointer;
            touch-action: none;
        }

        .touch-left {
            margin-right: 10px;
        }

        .game-message {
            position: absolute;
            top: 50%;
            left: 50%;
            transform: translate(-50%, -50%);
            background-color: rgba(0, 0, 0, 0.7);
            color: white;
            padding: 30px 50px;
            border-radius: var(--border-radius);
            text-align: center;
            font-size: 1.5rem;
            display: none;
        }

        .message-title {
            font-size: 2rem;
            margin-bottom: 15px;
            color: var(--accent-color);
        }

        @media (max-width: 768px) {
            .mobile-controls {
                display: block;
            }
        }
    </style>
    <link rel="stylesheet" href="https://cdnjs.cloudflare.com/ajax/libs/font-awesome/6.4.0/css/all.min.css">
</head>
<body>
    <h1>打砖块小游戏</h1>

    <div class="game-container">
        <div class="game-info">
            <div>分数: <span id="score">0</span></div>
            <div>生命: <span id="lives">3</span></div>
            <div>速度: <span id="speed-level">1</span></div>
        </div>

        <div class="canvas-container">
            <canvas id="gameCanvas"></canvas>
            <div class="game-message" id="gameMessage">
                <div class="message-title" id="messageTitle">游戏开始</div>
                <div id="messageContent">按空格键或点击开始按钮开始游戏</div>
            </div>
        </div>

        <div class="game-controls">
            <button class="control-btn" id="startBtn"><i class="fas fa-play"></i> 开始</button>
            <button class="control-btn secondary" id="pauseBtn"><i class="fas fa-pause"></i> 暂停</button>
            <button class="control-btn danger" id="resetBtn"><i class="fas fa-sync-alt"></i> 重置</button>
        </div>

        <div class="mobile-controls">
            <div class="touch-area">
                <div class="touch-left" id="touchLeft"><i class="fas fa-arrow-left"></i></div>
                <div class="touch-right" id="touchRight"><i class="fas fa-arrow-right"></i></div>
            </div>
        </div>
    </div>

    <script>
        // 获取DOM元素
        const canvas = document.getElementById('gameCanvas');
        const ctx = canvas.getContext('2d');
        const scoreElement = document.getElementById('score');
        const livesElement = document.getElementById('lives');
        const speedLevelElement = document.getElementById('speed-level');
        const startBtn = document.getElementById('startBtn');
        const pauseBtn = document.getElementById('pauseBtn');
        const resetBtn = document.getElementById('resetBtn');
        const gameMessage = document.getElementById('gameMessage');
        const messageTitle = document.getElementById('messageTitle');
        const messageContent = document.getElementById('messageContent');
        const touchLeft = document.getElementById('touchLeft');
        const touchRight = document.getElementById('touchRight');

        // 游戏状态
        let gameState = 'ready'; // ready, playing, paused, gameOver
        let score = 0;
        let lives = 3;
        let speedLevel = 1;

        // 游戏常量
        const PADDLE_HEIGHT = 15;
        const PADDLE_WIDTH = 100;
        const BALL_RADIUS = 10;
        const BRICK_ROWS = 5;
        const BRICK_COLUMNS = 9;
        const BRICK_WIDTH = 75;
        const BRICK_HEIGHT = 25;
        const BRICK_PADDING = 10;
        const BRICK_OFFSET_TOP = 70;
        const BRICK_OFFSET_LEFT = 30;
        const INITIAL_BALL_SPEED = 5;
        const BALL_SPEED_INCREASE = 0.2;

        // 游戏对象
        const paddle = {
            x: 0,
            y: 0,
            width: PADDLE_WIDTH,
            height: PADDLE_HEIGHT,
            dx: 8
        };

        const ball = {
            x: 0,
            y: 0,
            radius: BALL_RADIUS,
            dx: 0,
            dy: 0,
            speed: INITIAL_BALL_SPEED
        };

        // 砖块数组
        let bricks = [];

        // 键盘状态
        const keys = {
            left: false,
            right: false
        };

        // 鼠标状态
        let mouseX = 0;

        // 触摸状态
        let isTouchingLeft = false;
        let isTouchingRight = false;

        // 初始化游戏
        function initGame() {
            // 适配高清屏幕
            const dpr = window.devicePixelRatio || 1;
            canvas.width = 800 * dpr;
            canvas.height = 600 * dpr;
            canvas.style.width = '800px';
            canvas.style.height = '600px';
            ctx.scale(dpr, dpr);

            // 重置游戏对象位置
            resetPaddleAndBall();

            // 创建砖块
            createBricks();

            // 重置游戏状态
            score = 0;
            lives = 3;
            speedLevel = 1;
            gameState = 'ready';

            // 更新UI
            updateScore();
            updateLives();
            updateSpeedLevel();
            showMessage('游戏开始', '按空格键或点击开始按钮开始游戏');
        }

        // 重置球拍和球的位置
        function resetPaddleAndBall() {
            paddle.x = (canvas.width / window.devicePixelRatio - paddle.width) / 2;
            paddle.y = canvas.height / window.devicePixelRatio - paddle.height - 10;

            ball.x = canvas.width / window.devicePixelRatio / 2;
            ball.y = paddle.y - ball.radius;
            ball.dx = 0;
            ball.dy = 0;
            ball.speed = INITIAL_BALL_SPEED * (1 + (speedLevel - 1) * BALL_SPEED_INCREASE);
        }

        // 创建砖块
        function createBricks() {
            bricks = [];
            for (let c = 0; c < BRICK_COLUMNS; c++) {
                bricks[c] = [];
                for (let r = 0; r < BRICK_ROWS; r++) {
                    const brickX = c * (BRICK_WIDTH + BRICK_PADDING) + BRICK_OFFSET_LEFT;
                    const brickY = r * (BRICK_HEIGHT + BRICK_PADDING) + BRICK_OFFSET_TOP;
                    bricks[c][r] = {
                        x: brickX,
                        y: brickY,
                        status: 1, // 1表示存在,0表示被击碎
                        color: getBrickColor(r)
                    };
                }
            }
        }

        // 获取砖块颜色
        function getBrickColor(row) {
            const colors = ['#e74c3c', '#f39c12', '#f1c40f', '#2ecc71', '#3498db'];
            return colors[row % colors.length];
        }

        // 绘制游戏
        function drawGame() {
            // 清空画布
            ctx.clearRect(0, 0, canvas.width / window.devicePixelRatio, canvas.height / window.devicePixelRatio);

            // 绘制砖块
            drawBricks();

            // 绘制球
            drawBall();

            // 绘制球拍
            drawPaddle();

            // 检测碰撞
            if (gameState === 'playing') {
                detectCollisions();
                updateBallPosition();
                updatePaddlePosition();
            }

            // 请求下一帧
            requestAnimationFrame(drawGame);
        }

        // 绘制砖块
        function drawBricks() {
            for (let c = 0; c < BRICK_COLUMNS; c++) {
                for (let r = 0; r < BRICK_ROWS; r++) {
                    const brick = bricks[c][r];
                    if (brick.status === 1) {
                        ctx.beginPath();
                        ctx.rect(brick.x, brick.y, BRICK_WIDTH, BRICK_HEIGHT);
                        ctx.fillStyle = brick.color;
                        ctx.fill();
                        ctx.closePath();
                    }
                }
            }
        }

        // 绘制球
        function drawBall() {
            ctx.beginPath();
            ctx.arc(ball.x, ball.y, ball.radius, 0, Math.PI * 2);
            ctx.fillStyle = '#fff';
            ctx.fill();
            ctx.closePath();
        }

        // 绘制球拍
        function drawPaddle() {
            ctx.beginPath();
            ctx.rect(paddle.x, paddle.y, paddle.width, paddle.height);
            ctx.fillStyle = '#3498db';
            ctx.fill();
            ctx.closePath();
        }

        // 更新球的位置
        function updateBallPosition() {
            // 检测墙壁碰撞
            if (ball.x + ball.dx > canvas.width / window.devicePixelRatio - ball.radius || ball.x + ball.dx < ball.radius) {
                ball.dx = -ball.dx;
            }

            if (ball.y + ball.dy < ball.radius) {
                ball.dy = -ball.dy;
            } else if (ball.y + ball.dy > canvas.height / window.devicePixelRatio - ball.radius - paddle.height) {
                // 检测球拍碰撞
                if (ball.x > paddle.x && ball.x < paddle.x + paddle.width) {
                    // 根据碰撞位置调整反弹角度
                    const hitPosition = (ball.x - paddle.x) / paddle.width;
                    const angle = hitPosition * Math.PI - Math.PI / 2;
                    ball.dx = ball.speed * Math.cos(angle);
                    ball.dy = -ball.speed * Math.sin(angle);
                } else if (ball.y + ball.dy > canvas.height / window.devicePixelRatio - ball.radius) {
                    // 球掉落,减少生命
                    lives--;
                    updateLives();

                    if (lives <= 0) {
                        gameOver();
                    } else {
                        resetPaddleAndBall();
                        gameState = 'paused';
                        showMessage('球掉落', '按空格键或点击开始按钮继续');
                    }
                }
            }

            ball.x += ball.dx;
            ball.y += ball.dy;
        }

        // 更新球拍位置
        function updatePaddlePosition() {
            // 键盘控制
            if (keys.left && paddle.x > 0) {
                paddle.x -= paddle.dx;
            } else if (keys.right && paddle.x < canvas.width / window.devicePixelRatio - paddle.width) {
                paddle.x += paddle.dx;
            }

            // 鼠标控制
            if (mouseX !== 0) {
                const relativeX = mouseX * (canvas.width / window.devicePixelRatio) / canvas.clientWidth;
                if (relativeX > paddle.width / 2 && relativeX < canvas.width / window.devicePixelRatio - paddle.width / 2) {
                    paddle.x = relativeX - paddle.width / 2;
                }
            }

            // 触摸控制
            if (isTouchingLeft && paddle.x > 0) {
                paddle.x -= paddle.dx;
            } else if (isTouchingRight && paddle.x < canvas.width / window.devicePixelRatio - paddle.width) {
                paddle.x += paddle.dx;
            }
        }

        // 检测碰撞
        function detectCollisions() {
            let bricksInRow = 0;
            let allBricksGone = true;

            for (let c = 0; c < BRICK_COLUMNS; c++) {
                for (let r = 0; r < BRICK_ROWS; r++) {
                    const brick = bricks[c][r];
                    if (brick.status === 1) {
                        allBricksGone = false;
                        bricksInRow++;

                        // 检测球与砖块的碰撞
                        if (
                            ball.x + ball.radius > brick.x &&
                            ball.x - ball.radius < brick.x + BRICK_WIDTH &&
                            ball.y + ball.radius > brick.y &&
                            ball.y - ball.radius < brick.y + BRICK_HEIGHT
                        ) {
                            // 根据碰撞方向调整球的速度
                            const overlapLeft = ball.x + ball.radius - brick.x;
                            const overlapRight = brick.x + BRICK_WIDTH - (ball.x - ball.radius);
                            const overlapTop = ball.y + ball.radius - brick.y;
                            const overlapBottom = brick.y + BRICK_HEIGHT - (ball.y - ball.radius);

                            const minOverlap = Math.min(overlapLeft, overlapRight, overlapTop, overlapBottom);

                            if (minOverlap === overlapLeft || minOverlap === overlapRight) {
                                ball.dx = -ball.dx;
                            } else {
                                ball.dy = -ball.dy;
                            }

                            // 击碎砖块
                            brick.status = 0;
                            score += 10;
                            updateScore();

                            // 检查是否需要增加速度
                            checkSpeedIncrease();
                        }
                    }
                }

                // 如果一行砖块全部被击碎,增加速度
                if (bricksInRow === 0 && r === BRICK_ROWS - 1) {
                    increaseSpeed();
                }
                bricksInRow = 0;
            }

            // 如果所有砖块都被击碎,重新创建砖块并增加速度
            if (allBricksGone) {
                createBricks();
                increaseSpeed();
            }
        }

        // 检查是否需要增加速度
        function checkSpeedIncrease() {
            // 每得到100分增加一次速度
            if (score % 100 === 0 && score !== 0) {
                increaseSpeed();
            }
        }

        // 增加速度
        function increaseSpeed() {
            speedLevel++;
            ball.speed = INITIAL_BALL_SPEED * (1 + (speedLevel - 1) * BALL_SPEED_INCREASE);
            updateSpeedLevel();
        }

        // 更新分数
        function updateScore() {
            scoreElement.textContent = score;
        }

        // 更新生命
        function updateLives() {
            livesElement.textContent = lives;
        }

        // 更新速度等级
        function updateSpeedLevel() {
            speedLevelElement.textContent = speedLevel;
        }

        // 显示消息
        function showMessage(title, content) {
            messageTitle.textContent = title;
            messageContent.textContent = content;
            gameMessage.style.display = 'block';
        }

        // 隐藏消息
        function hideMessage() {
            gameMessage.style.display = 'none';
        }

        // 开始游戏
        function startGame() {
            if (gameState === 'ready' || gameState === 'paused') {
                gameState = 'playing';
                hideMessage();

                // 如果是刚准备好的游戏,设置球的初始速度
                if (ball.dx === 0 && ball.dy === 0) {
                    ball.dx = ball.speed * 0.5;
                    ball.dy = -ball.speed;
                }
            }
        }

        // 暂停游戏
        function pauseGame() {
            if (gameState === 'playing') {
                gameState = 'paused';
                showMessage('游戏暂停', '按空格键或点击开始按钮继续');
            }
        }

        // 重置游戏
        function resetGame() {
            initGame();
        }

        // 游戏结束
        function gameOver() {
            gameState = 'gameOver';
            showMessage('游戏结束', `最终得分: ${score}\n按空格键或点击重置按钮重新开始`);
        }

        // 事件监听
        function setupEventListeners() {
            // 键盘控制
            document.addEventListener('keydown', (e) => {
                if (e.key === 'ArrowLeft') {
                    keys.left = true;
                } else if (e.key === 'ArrowRight') {
                    keys.right = true;
                } else if (e.key === ' ') {
                    if (gameState === 'ready' || gameState === 'paused') {
                        startGame();
                    } else if (gameState === 'playing') {
                        pauseGame();
                    } else if (gameState === 'gameOver') {
                        resetGame();
                    }
                }
            });

            document.addEventListener('keyup', (e) => {
                if (e.key === 'ArrowLeft') {
                    keys.left = false;
                } else if (e.key === 'ArrowRight') {
                    keys.right = false;
                }
            });

            // 鼠标控制
            canvas.addEventListener('mousemove', (e) => {
                mouseX = e.clientX - canvas.getBoundingClientRect().left;
            });

            // 按钮控制
            startBtn.addEventListener('click', startGame);
            pauseBtn.addEventListener('click', pauseGame);
            resetBtn.addEventListener('click', resetGame);

            // 触摸控制
            touchLeft.addEventListener('touchstart', (e) => {
                e.preventDefault();
                isTouchingLeft = true;
            });

            touchLeft.addEventListener('touchend', (e) => {
                e.preventDefault();
                isTouchingLeft = false;
            });

            touchRight.addEventListener('touchstart', (e) => {
                e.preventDefault();
                isTouchingRight = true;
            });

            touchRight.addEventListener('touchend', (e) => {
                e.preventDefault();
                isTouchingRight = false;
            });

            // 窗口大小变化时重新调整画布
            window.addEventListener('resize', () => {
                // 重新初始化游戏以适应新窗口大小
                const currentState = gameState;
                initGame();
                gameState = currentState;
                if (gameState !== 'ready' && gameState !== 'gameOver') {
                    hideMessage();
                }
            });
        }

        // 启动游戏
        function start() {
            initGame();
            setupEventListeners();
            drawGame();
        }

        // 开始游戏
        start();
    </script>
</body>
</html>
相关推荐
前端的日常13 小时前
还不会写抽奖转盘?快来让Trae写吧
trae
你不会困13 小时前
让 NestJS 冷启动时间从20s提升到3s,Trae只改了这些
trae
你不会困13 小时前
不想接口联调,不想写代码,那就交给Trae
trae
bug菌14 小时前
还在为编程效率发愁?字节跳动Trae如何让你秒变“代码大师“!
后端·ai编程·trae
数字扫地僧14 小时前
Trae模型保存/加载:Checkpoint机制详解
trae
数字扫地僧14 小时前
Trae混合精度训练指南:FP16加速技巧
trae
数字扫地僧14 小时前
Trae可视化工具:实时监控训练过程
trae
数字扫地僧14 小时前
Trae调试技巧:常见错误与异常处理
trae
数字扫地僧14 小时前
数据加载优化:Trae高效数据管道实现
trae
数字扫地僧15 小时前
Trae张量操作大全:从基础运算到广播机制
trae