我:使用 web 技术栈生成一个贪吃蛇游戏

Roo Code会给出一个Todo List updated:

  • 创建项目结构
  • 实现游戏逻辑
  • 添加样式
  • 测试游戏
  • 完善游戏功能

然后按照这个顺序依次创建文件,直至测试游戏。

第一次使用通义千问3-Coder-Plus生成贪食蛇游戏的学思笔记.png

大家觉得以下代码质量如何?

入口文件index.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>
    <link rel="stylesheet" href="style.css">
</head>
<body>
    <h1>贪吃蛇游戏</h1>
    <div class="game-container">
        <canvas id="gameCanvas" width="400" height="400"></canvas>
    </div>
    <script src="script.js"></script>
</body>
</html>

样式文件style.css

body {
    font-family: Arial, sans-serif;
    text-align: center;
    background-color: #f0f0f0;
    margin: 0;
    padding: 0;
}

h1 {
    background-color: #333;
    color: white;
    padding: 1em 0;
    margin: 0;
}

.game-container {
    margin: 20px auto;
    width: 400px;
    height: 400px;
    background-color: #fff;
    border: 2px solid #333;
    display: flex;
    justify-content: center;
    align-items: center;
}

canvas {
    border: 1px solid #000;
}

游戏逻辑文件script.js

// 游戏配置
const config = {
    canvasSize: 400,
    gridSize: 20,
    initialSpeed: 150,
    foodColor: '#ff0000',
    snakeColor: '#00ff00',
    snakeBorderColor: '#006600'
};

// 获取画布上下文
const canvas = document.getElementById('gameCanvas');
const ctx = canvas.getContext('2d');

// 蛇的表示
let snake = [{
    x: 100,
    y: 100
}];

// 食物的位置
let food = {
    x: 200,
    y: 200
};

// 游戏状态
let score = 0;
let dx = config.gridSize;
let dy = 0;
let speed = config.initialSpeed;
let gameInterval;

// 键盘控制
document.addEventListener('keydown', changeDirection);

// 开始游戏
function startGame() {
    gameInterval = setInterval(gameLoop, speed);
}

// 游戏循环
function gameLoop() {
    // 移动蛇
    const head = {
        x: snake[0].x + dx,
        y: snake[0].y + dy
    };
    
    // 插入新头到蛇的前面
    snake.unshift(head);
    
    // 检查是否吃到食物
    if (head.x === food.x && head.y === food.y) {
        // 增加分数
        score++;
        
        // 生成新食物
        placeFood();
    } else {
        // 移除蛇尾
        snake.pop();
    }
    
    // 检查碰撞
    if (checkCollision()) {
        // 结束游戏
        endGame();
        return;
    }
    
    // 重绘游戏
    drawGame();
}

// 绘制游戏
function drawGame() {
    // 清除画布
    ctx.fillStyle = '#fff';
    ctx.fillRect(0, 0, canvas.width, canvas.height);
    
    // 绘制分数
    ctx.fillStyle = '#000';
    ctx.font = '16px Arial';
    ctx.fillText('分数: ' + score, 10, 20);
    
    // 绘制蛇
    for (let i = 0; i < snake.length; i++) {
        ctx.fillStyle = config.snakeColor;
        ctx.strokeStyle = config.snakeBorderColor;
        ctx.fillRect(snake[i].x, snake[i].y, config.gridSize, config.gridSize);
        ctx.strokeRect(snake[i].x, snake[i].y, config.gridSize, config.gridSize);
    }
    
    // 绘制食物
    ctx.fillStyle = config.foodColor;
    ctx.fillRect(food.x, food.y, config.gridSize, config.gridSize);
}

// 放置食物
function placeFood() {
    // 计算随机位置
    const newX = Math.floor(Math.random() * (config.canvasSize / config.gridSize)) * config.gridSize;
    const newY = Math.floor(Math.random() * (config.canvasSize / config.gridSize)) * config.gridSize;
    
    // 检查是否与蛇重叠
    for (let i = 0; i < snake.length; i++) {
        if (newX === snake[i].x && newY === snake[i].y) {
            // 重试
            placeFood();
            return;
        }
    }
    
    // 设置食物位置
    food.x = newX;
    food.y = newY;
}

// 改变方向
function changeDirection(event) {
    const key = event.key;
    
    // 避免反向移动
    if (key === 'ArrowLeft' && dx === 0) {
        dx = -config.gridSize;
        dy = 0;
    } else if (key === 'ArrowRight' && dx === 0) {
        dx = config.gridSize;
        dy = 0;
    } else if (key === 'ArrowUp' && dy === 0) {
        dx = 0;
        dy = -config.gridSize;
    } else if (key === 'ArrowDown' && dy === 0) {
        dx = 0;
        dy = config.gridSize;
    }
}

// 检查碰撞
function checkCollision() {
    // 检查墙碰撞
    if (snake[0].x < 0 || snake[0].x >= config.canvasSize || 
        snake[0].y < 0 || snake[0].y >= config.canvasSize) {
        return true;
    }
    
    // 检查自身碰撞
    for (let i = 1; i < snake.length; i++) {
        if (snake[0].x === snake[i].x && snake[0].y === snake[i].y) {
            return true;
        }
    }
    
    return false;
}

// 结束游戏
function endGame() {
    // 停止游戏循环
    clearInterval(gameInterval);
    
    // 显示游戏结束
    ctx.fillStyle = 'rgba(0, 0, 0, 0.5)';
    ctx.fillRect(0, 0, canvas.width, canvas.height, 50);
    
    ctx.fillStyle = '#fff';
    ctx.font = '30px Arial';
    ctx.fillText('游戏结束', 90, 200);
    ctx.font = '20px Arial';
    ctx.fillText('分数: ' + score, 155, 240);
    ctx.font = '16px Arial';
    ctx.fillText('按空格键重新开始', 130, 270);
    
    // 重置游戏
    document.addEventListener('keydown', restartGame);
}

// 重新开始游戏
function restartGame(event) {
    if (event.key === ' ') {
        // 重置蛇
        snake = [{
            x: 100,
            y: 100
        }];
        
        // 重置食物
        food = {
            x: 200,
            y: 200
        };
        
        // 重置分数
        score = 0;
        
        // 重置方向
        dx = config.gridSize;
        dy = 0;
        
        // 移除事件监听器
        document.removeEventListener('keydown', restartGame);
        
        // 开始新游戏
        startGame();
    }
}

// 初始化游戏
startGame();