我:使用 web 技术栈生成一个贪吃蛇游戏
Roo Code会给出一个Todo List updated:
- 创建项目结构
- 实现游戏逻辑
- 添加样式
- 测试游戏
- 完善游戏功能
然后按照这个顺序依次创建文件,直至测试游戏。
大家觉得以下代码质量如何?
入口文件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();