游戏说明
+-
+
- 使用方向键 ↑ ↓ ← → 控制蛇的移动 +
- 吃到食物得分,蛇身变长 +
- 撞墙或撞到自己,游戏结束 +
- 随时可以暂停或重置游戏 +
diff --git a/README.md b/README.md index 563e595..ea9ef0c 100644 --- a/README.md +++ b/README.md @@ -1,3 +1,98 @@ -# snake-game +# 🐍 贪吃蛇游戏 -网页版贪吃蛇游戏 - 完整开发流程演示项目 \ No newline at end of file +一个经典的网页版贪吃蛇游戏,使用纯 HTML/CSS/JavaScript 实现。 + +## ✨ 特性 + +- 🎮 经典贪吃蛇玩法 +- 🎯 三种难度级别(简单/中等/困难) +- ⏸️ 支持暂停和重置 +- 📊 实时分数统计 +- 🎨 精美的渐变 UI 设计 +- ⌨️ 键盘方向键控制 +- 📱 响应式设计 + +## 🎮 游戏玩法 + +1. 点击"开始游戏"按钮开始 +2. 使用方向键 ↑ ↓ ← → 控制蛇的移动 +3. 吃到食物(红色圆点)获得 10 分 +4. 避免撞墙或撞到自己的身体 +5. 随时可以暂停或重置游戏 + +## 🚀 快速开始 + +### 方式 1:直接打开 + +直接用浏览器打开 `index.html` 文件即可开始游戏。 + +### 方式 2:本地服务器 + +```bash +# 使用 Python +python3 -m http.server 8000 + +# 使用 Node.js (需要安装 http-server) +npx http-server + +# 然后访问 http://localhost:8000 +``` + +### 方式 3:部署到 Web 服务器 + +将所有文件部署到任何 Web 服务器(Nginx、Apache 等)的根目录即可。 + +## 📦 项目结构 + +``` +snake-game/ +├── index.html # 主页面 +├── style.css # 样式文件 +├── game.js # 游戏逻辑 +├── README.md # 项目说明 +└── LICENSE # MIT 许可证 +``` + +## 🛠️ 技术栈 + +- **HTML5** - 页面结构 +- **CSS3** - 样式和动画 +- **JavaScript (ES6+)** - 游戏逻辑 +- **Canvas API** - 游戏渲染 + +## 🎯 游戏配置 + +可以在 `game.js` 中修改 `CONFIG` 对象来自定义游戏参数: + +```javascript +const CONFIG = { + gridSize: 20, // 网格大小 + canvasSize: 400, // 画布大小 + easySpeed: 150, // 简单模式速度 + mediumSpeed: 100, // 中等模式速度 + hardSpeed: 50 // 困难模式速度 +}; +``` + +## 📝 开发路线图 + +本项目通过分阶段开发完成,展示完整的软件开发流程: + +- ✅ [阶段1:项目初始化](https://github.com/user/repo/issues/1) +- ✅ [阶段2:游戏核心逻辑](https://github.com/user/repo/issues/2) +- ✅ [阶段3:游戏 UI 界面](https://github.com/user/repo/issues/3) +- ✅ [阶段4:交互和优化](https://github.com/user/repo/issues/4) +- 🚧 [阶段5:CI/CD 配置](https://github.com/user/repo/issues/5) +- 🚧 [阶段6:部署上线](https://github.com/user/repo/issues/6) + +## 📄 许可证 + +本项目采用 MIT 许可证。详见 [LICENSE](LICENSE) 文件。 + +## 🤝 贡献 + +欢迎提交 Issue 和 Pull Request! + +--- + +**游戏愉快!🎮** diff --git a/game.js b/game.js new file mode 100644 index 0000000..dc15500 --- /dev/null +++ b/game.js @@ -0,0 +1,278 @@ +// 游戏配置 +const CONFIG = { + gridSize: 20, // 网格大小 + canvasSize: 400, // 画布大小 + easySpeed: 150, // 简单模式速度 + mediumSpeed: 100, // 中等模式速度 + hardSpeed: 50 // 困难模式速度 +}; + +// 游戏状态 +let snake = []; +let food = {}; +let direction = 'right'; +let nextDirection = 'right'; +let score = 0; +let gameLoop = null; +let isPaused = false; +let isGameRunning = false; + +// DOM 元素 +const canvas = document.getElementById('gameCanvas'); +const ctx = canvas.getContext('2d'); +const scoreElement = document.getElementById('score'); +const startBtn = document.getElementById('startBtn'); +const pauseBtn = document.getElementById('pauseBtn'); +const resetBtn = document.getElementById('resetBtn'); +const difficultySelect = document.getElementById('difficulty'); + +// 初始化游戏 +function initGame() { + snake = [ + { x: 5, y: 10 }, + { x: 4, y: 10 }, + { x: 3, y: 10 } + ]; + direction = 'right'; + nextDirection = 'right'; + score = 0; + isPaused = false; + isGameRunning = false; + updateScore(); + spawnFood(); + draw(); +} + +// 生成食物 +function spawnFood() { + const maxPos = CONFIG.canvasSize / CONFIG.gridSize; + let newFood; + + do { + newFood = { + x: Math.floor(Math.random() * maxPos), + y: Math.floor(Math.random() * maxPos) + }; + } while (snake.some(segment => segment.x === newFood.x && segment.y === newFood.y)); + + food = newFood; +} + +// 更新游戏状态 +function update() { + if (isPaused) return; + + direction = nextDirection; + + // 计算蛇头新位置 + const head = { ...snake[0] }; + + switch (direction) { + case 'up': + head.y -= 1; + break; + case 'down': + head.y += 1; + break; + case 'left': + head.x -= 1; + break; + case 'right': + head.x += 1; + break; + } + + // 检查碰撞 + if (checkCollision(head)) { + gameOver(); + return; + } + + // 添加蛇头 + snake.unshift(head); + + // 检查是否吃到食物 + if (head.x === food.x && head.y === food.y) { + score += 10; + updateScore(); + spawnFood(); + } else { + snake.pop(); + } + + draw(); +} + +// 检查碰撞 +function checkCollision(head) { + const maxPos = CONFIG.canvasSize / CONFIG.gridSize; + + // 撞墙检测 + if (head.x < 0 || head.x >= maxPos || head.y < 0 || head.y >= maxPos) { + return true; + } + + // 撞自己检测 + return snake.some(segment => segment.x === head.x && segment.y === head.y); +} + +// 绘制游戏 +function draw() { + // 清空画布 + ctx.fillStyle = '#2c3e50'; + ctx.fillRect(0, 0, CONFIG.canvasSize, CONFIG.canvasSize); + + // 绘制蛇 + snake.forEach((segment, index) => { + const gradient = ctx.createRadialGradient( + segment.x * CONFIG.gridSize + CONFIG.gridSize / 2, + segment.y * CONFIG.gridSize + CONFIG.gridSize / 2, + 0, + segment.x * CONFIG.gridSize + CONFIG.gridSize / 2, + segment.y * CONFIG.gridSize + CONFIG.gridSize / 2, + CONFIG.gridSize + ); + + if (index === 0) { + gradient.addColorStop(0, '#2ecc71'); + gradient.addColorStop(1, '#27ae60'); + } else { + gradient.addColorStop(0, '#82e0aa'); + gradient.addColorStop(1, '#58d68d'); + } + + ctx.fillStyle = gradient; + ctx.fillRect( + segment.x * CONFIG.gridSize + 1, + segment.y * CONFIG.gridSize + 1, + CONFIG.gridSize - 2, + CONFIG.gridSize - 2 + ); + }); + + // 绘制食物 + const foodGradient = ctx.createRadialGradient( + food.x * CONFIG.gridSize + CONFIG.gridSize / 2, + food.y * CONFIG.gridSize + CONFIG.gridSize / 2, + 0, + food.x * CONFIG.gridSize + CONFIG.gridSize / 2, + food.y * CONFIG.gridSize + CONFIG.gridSize / 2, + CONFIG.gridSize + ); + foodGradient.addColorStop(0, '#e74c3c'); + foodGradient.addColorStop(1, '#c0392b'); + + ctx.fillStyle = foodGradient; + ctx.beginPath(); + ctx.arc( + food.x * CONFIG.gridSize + CONFIG.gridSize / 2, + food.y * CONFIG.gridSize + CONFIG.gridSize / 2, + CONFIG.gridSize / 2 - 2, + 0, + Math.PI * 2 + ); + ctx.fill(); +} + +// 更新分数 +function updateScore() { + scoreElement.textContent = score; +} + +// 游戏结束 +function gameOver() { + stopGame(); + alert(`游戏结束!得分:${score}\n点击"重置"重新开始`); +} + +// 开始游戏 +function startGame() { + if (isGameRunning) return; + + initGame(); + isGameRunning = true; + startBtn.disabled = true; + pauseBtn.disabled = false; + difficultySelect.disabled = true; + + const speed = getGameSpeed(); + gameLoop = setInterval(update, speed); +} + +// 停止游戏 +function stopGame() { + if (gameLoop) { + clearInterval(gameLoop); + gameLoop = null; + } + isGameRunning = false; + startBtn.disabled = false; + pauseBtn.disabled = true; + pauseBtn.textContent = '暂停'; + difficultySelect.disabled = false; +} + +// 暂停/继续游戏 +function togglePause() { + if (!isGameRunning) return; + + isPaused = !isPaused; + + if (isPaused) { + pauseBtn.textContent = '继续'; + } else { + pauseBtn.textContent = '暂停'; + } +} + +// 重置游戏 +function resetGame() { + stopGame(); + initGame(); +} + +// 获取游戏速度 +function getGameSpeed() { + const difficulty = difficultySelect.value; + + switch (difficulty) { + case 'easy': + return CONFIG.easySpeed; + case 'medium': + return CONFIG.mediumSpeed; + case 'hard': + return CONFIG.hardSpeed; + default: + return CONFIG.mediumSpeed; + } +} + +// 键盘控制 +document.addEventListener('keydown', (e) => { + const key = e.key; + + if (key === 'ArrowUp' && direction !== 'down') { + nextDirection = 'up'; + e.preventDefault(); + } else if (key === 'ArrowDown' && direction !== 'up') { + nextDirection = 'down'; + e.preventDefault(); + } else if (key === 'ArrowLeft' && direction !== 'right') { + nextDirection = 'left'; + e.preventDefault(); + } else if (key === 'ArrowRight' && direction !== 'left') { + nextDirection = 'right'; + e.preventDefault(); + } else if (key === ' ' && isGameRunning) { + togglePause(); + e.preventDefault(); + } +}); + +// 按钮事件 +startBtn.addEventListener('click', startGame); +pauseBtn.addEventListener('click', togglePause); +resetBtn.addEventListener('click', resetGame); + +// 初始化 +initGame(); diff --git a/index.html b/index.html new file mode 100644 index 0000000..bedc912 --- /dev/null +++ b/index.html @@ -0,0 +1,50 @@ + + +
+ + +