From 4bcce60d326d990017f17660798603ebf294a615 Mon Sep 17 00:00:00 2001 From: traetest Date: Mon, 13 Apr 2026 01:56:45 +0800 Subject: [PATCH] =?UTF-8?q?Add=20=E7=BB=8F=E5=85=B8=E8=B4=AA=E5=90=83?= =?UTF-8?q?=E8=9B=87=E6=B8=B8=E6=88=8F=20-=20Pygame=20=E5=AE=9E=E7=8E=B0?= =?UTF-8?q?=E7=9A=84=E7=B2=BE=E7=BE=8E=E8=B4=AA=E5=90=83=E8=9B=87=E5=B0=8F?= =?UTF-8?q?=E6=B8=B8=E6=88=8F?= MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit --- GAME_README.md | 225 +++++++++++++++++++++++++ game_requirements.txt | 1 + play_game.py | 9 + snake_game.py | 382 ++++++++++++++++++++++++++++++++++++++++++ 4 files changed, 617 insertions(+) create mode 100644 GAME_README.md create mode 100644 game_requirements.txt create mode 100644 play_game.py create mode 100644 snake_game.py diff --git a/GAME_README.md b/GAME_README.md new file mode 100644 index 0000000..cc5eca0 --- /dev/null +++ b/GAME_README.md @@ -0,0 +1,225 @@ +# 🐍 Python 经典贪吃蛇游戏 + +一个使用 Python 和 Pygame 开发的经典贪吃蛇游戏,画面精美,操作简单有趣! + +## ✨ 游戏特性 + +### 🎮 核心玩法 +- **经典规则** - 控制蛇移动,吃到食物变长 +- **渐进难度** - 每吃 5 个食物速度加快 +- **精美画面** - 现代化 UI 设计,蛇有眼睛动画 +- **流畅操作** - 支持方向键和 WASD 双控制方案 + +### 🎨 视觉效果 +- 深蓝色背景 + 网格线 +- 绿色蛇身,带眼睛动画 +- 红色圆形食物,带高光和阴影 +- 半透明游戏结束/暂停界面 +- 实时分数和速度显示 + +### 🎯 游戏功能 +- 暂停/继续功能(P 键) +- 游戏结束后快速重开(空格键) +- 优雅退出(ESC 键) +- 碰撞检测(墙壁和自己) +- 速度系统(10-25 FPS) + +## 📦 安装依赖 + +```bash +pip install -r game_requirements.txt +``` + +或者单独安装: +```bash +pip install pygame>=2.5.0 +``` + +## 🚀 快速开始 + +### 方法 1:运行启动文件 +```bash +python play_game.py +``` + +### 方法 2:直接运行游戏 +```bash +python snake_game.py +``` + +## 🎮 游戏操作 + +| 按键 | 功能 | +|------|------| +| ↑ / W | 向上移动 | +| ↓ / S | 向下移动 | +| ← / A | 向左移动 | +| → / D | 向右移动 | +| P | 暂停/继续游戏 | +| 空格 | 游戏结束后重新开始 | +| ESC | 退出游戏 | + +## 📖 游戏规则 + +1. **目标** - 控制蛇吃到尽可能多的食物 +2. **得分** - 每个食物 = 10 分 +3. **加速** - 每吃 5 个食物(50 分),速度提升 2 档 +4. **失败条件**: + - 蛇头撞到墙壁 + - 蛇头撞到自己的身体 +5. **最大速度** - 25 FPS(初始速度 10 FPS) + +## 🏆 技巧提示 + +### 新手技巧 +- 开始时速度较慢,熟悉操作 +- 提前规划路线,不要急转弯 +- 尽量保持在屏幕中央活动 + +### 进阶技巧 +- 利用墙壁反弹改变方向 +- 预留足够的转弯空间 +- 高速时小幅调整方向 +- 观察蛇头眼睛判断方向 + +### 高分策略 +- 采用"之"字形路线清理食物 +- 避免把自己逼入死角 +- 保持蛇身紧凑,留出空间 +- 后期以生存为主,不要贪心 + +## 🎨 游戏画面 + +``` +┌────────────────────────────────────────┐ +│ Score: 120 Speed: 14 │ +├────────────────────────────────────────┤ +│ │ +│ ●●●●● (蛇) │ +│ ● │ +│ ● │ +│ ●●●● │ +│ ● │ +│ ● 🍎 (食物) │ +│ │ +│ │ +└────────────────────────────────────────┘ +``` + +## 🔧 自定义配置 + +### 修改窗口大小 +编辑 `snake_game.py` 中的初始化参数: +```python +game = SnakeGame(width=1000, height=800, cell_size=25) +``` + +### 修改颜色 +编辑 `snake_game.py` 中的颜色字典: +```python +self.colors = { + 'background': (15, 23, 42), # 背景色 + 'snake_head': (34, 197, 94), # 蛇头颜色 + 'snake_body': (22, 163, 74), # 蛇身颜色 + 'food': (239, 68, 68), # 食物颜色 + # ... 更多颜色 +} +``` + +### 修改初始速度 +```python +self.speed = 15 # 改为 15 FPS +``` + +## 🐛 常见问题 + +### Q: 游戏窗口打不开? +A: 确保已安装 Pygame:`pip install pygame` + +### Q: 按键没反应? +A: 确保游戏窗口处于焦点状态,点击窗口后再操作 + +### Q: 游戏太慢/太快? +A: 修改 `self.speed = 10` 调整初始速度 + +### Q: 想要更大/更小的蛇? +A: 修改 `cell_size = 20` 调整格子大小 + +## 🎯 扩展计划 + +- [ ] 添加音效和背景音乐 +- [ ] 多种难度选择 +- [ ] 排行榜系统 +- [ ] 多种蛇皮肤 +- [ ] 双人模式 +- [ ] 特殊道具(加速、减速、穿墙) +- [ ] 地图编辑器 +- [ ] 录像回放功能 + +## 💻 技术实现 + +### 核心技术 +- **Pygame** - 游戏引擎和渲染 +- **网格系统** - 基于网格的移动和碰撞检测 +- **队列数据结构** - 蛇身管理 +- **事件驱动** - 键盘输入处理 + +### 性能优化 +- 只重绘变化的部分 +- 使用矩形碰撞检测 +- 帧率控制保证流畅性 + +### 代码结构 +``` +SnakeGame +├── __init__() # 初始化 +├── reset_game() # 重置游戏 +├── _generate_food() # 生成食物 +├── _handle_events() # 处理事件 +├── _update() # 更新状态 +├── _check_collision() # 碰撞检测 +├── _draw_grid() # 绘制网格 +├── _draw_snake() # 绘制蛇 +├── _draw_food() # 绘制食物 +├── _draw_score() # 绘制分数 +├── _draw_game_over() # 游戏结束画面 +├── _draw_paused() # 暂停画面 +├── draw() # 总绘制 +└── run() # 主循环 +``` + +## 📝 版本历史 + +### v1.0.0 +- ✅ 基础游戏功能 +- ✅ 精美 UI 界面 +- ✅ 蛇眼睛动画 +- ✅ 暂停/继续功能 +- ✅ 速度系统 +- ✅ 碰撞检测 + +## 🎓 学习价值 + +通过这个游戏可以学习: +- Pygame 基础使用 +- 游戏循环设计 +- 碰撞检测算法 +- 事件处理机制 +- 面向对象编程 +- 动画原理 + +## 📄 许可证 + +MIT License + +## 🎉 开始游戏 + +```bash +# 安装依赖 +pip install -r game_requirements.txt + +# 运行游戏 +python play_game.py +``` + +祝你玩得开心!🐍🎮 diff --git a/game_requirements.txt b/game_requirements.txt new file mode 100644 index 0000000..183ccce --- /dev/null +++ b/game_requirements.txt @@ -0,0 +1 @@ +pygame>=2.5.0 diff --git a/play_game.py b/play_game.py new file mode 100644 index 0000000..e0a2bb7 --- /dev/null +++ b/play_game.py @@ -0,0 +1,9 @@ +""" +Python 小游戏合集 +贪吃蛇游戏启动文件 +""" + +from snake_game import main + +if __name__ == "__main__": + main() diff --git a/snake_game.py b/snake_game.py new file mode 100644 index 0000000..1c69555 --- /dev/null +++ b/snake_game.py @@ -0,0 +1,382 @@ +""" +经典贪吃蛇游戏 +使用方向键控制蛇的移动,吃到食物后蛇会变长 +撞墙或撞到自己则游戏结束 +""" + +import pygame +import random +import sys +from enum import Enum +from typing import Tuple, List + + +class Direction(Enum): + """方向枚举""" + UP = (0, -1) + DOWN = (0, 1) + LEFT = (-1, 0) + RIGHT = (1, 0) + + +class SnakeGame: + """贪吃蛇游戏类""" + + def __init__(self, width: int = 800, height: int = 600, cell_size: int = 20): + """ + 初始化游戏 + + Args: + width: 游戏窗口宽度 + height: 游戏窗口高度 + cell_size: 每个格子的大小 + """ + self.width = width + self.height = height + self.cell_size = cell_size + + # 计算网格数量 + self.grid_width = width // cell_size + self.grid_height = height // cell_size + + # 初始化 Pygame + pygame.init() + self.screen = pygame.display.set_mode((width, height)) + pygame.display.set_caption("🐍 经典贪吃蛇") + self.clock = pygame.time.Clock() + + # 字体 + self.font_large = pygame.font.Font(None, 74) + self.font_medium = pygame.font.Font(None, 48) + self.font_small = pygame.font.Font(None, 36) + + # 颜色定义 + self.colors = { + 'background': (15, 23, 42), # 深蓝色背景 + 'snake_head': (34, 197, 94), # 绿色蛇头 + 'snake_body': (22, 163, 74), # 深绿色蛇身 + 'food': (239, 68, 68), # 红色食物 + 'grid': (30, 41, 59), # 网格线 + 'text': (255, 255, 255), # 白色文字 + 'score_bg': (51, 65, 85) # 分数背景 + } + + # 游戏状态 + self.reset_game() + + def reset_game(self): + """重置游戏状态""" + # 蛇的初始位置(中心) + start_x = self.grid_width // 2 + start_y = self.grid_height // 2 + + self.snake: List[Tuple[int, int]] = [ + (start_x, start_y), + (start_x - 1, start_y), + (start_x - 2, start_y) + ] + + # 初始方向(向右) + self.direction = Direction.RIGHT + self.next_direction = Direction.RIGHT + + # 生成第一个食物 + self.food = self._generate_food() + + # 游戏状态 + self.score = 0 + self.game_over = False + self.paused = False + self.speed = 10 # 初始速度(FPS) + + def _generate_food(self) -> Tuple[int, int]: + """ + 生成食物位置 + + Returns: + 食物的坐标 (x, y) + """ + while True: + food_pos = ( + random.randint(0, self.grid_width - 1), + random.randint(0, self.grid_height - 1) + ) + # 确保食物不会生成在蛇身上 + if food_pos not in self.snake: + return food_pos + + def _handle_events(self): + """处理事件""" + for event in pygame.event.get(): + if event.type == pygame.QUIT: + return False + + if event.type == pygame.KEYDOWN: + if self.game_over: + if event.key == pygame.K_SPACE or event.key == pygame.K_RETURN: + self.reset_game() + elif event.key == pygame.K_ESCAPE: + return False + else: + # 暂停功能 + if event.key == pygame.K_p: + self.paused = not self.paused + + # 方向控制 + if event.key in [pygame.K_UP, pygame.K_w]: + if self.direction != Direction.DOWN: + self.next_direction = Direction.UP + elif event.key in [pygame.K_DOWN, pygame.K_s]: + if self.direction != Direction.UP: + self.next_direction = Direction.DOWN + elif event.key in [pygame.K_LEFT, pygame.K_a]: + if self.direction != Direction.RIGHT: + self.next_direction = Direction.LEFT + elif event.key in [pygame.K_RIGHT, pygame.K_d]: + if self.direction != Direction.LEFT: + self.next_direction = Direction.RIGHT + elif event.key == pygame.K_ESCAPE: + return False + + return True + + def _update(self): + """更新游戏状态""" + if self.game_over or self.paused: + return + + # 更新方向 + self.direction = self.next_direction + + # 计算新的头部位置 + head_x, head_y = self.snake[0] + dx, dy = self.direction.value + new_head = (head_x + dx, head_y + dy) + + # 检查碰撞 + if self._check_collision(new_head): + self.game_over = True + return + + # 移动蛇 + self.snake.insert(0, new_head) + + # 检查是否吃到食物 + if new_head == self.food: + self.score += 10 + self.food = self._generate_food() + + # 每吃 5 个食物加速一次 + if self.score % 50 == 0: + self.speed = min(self.speed + 2, 25) # 最大速度 25 + else: + # 没吃到食物,移除尾部 + self.snake.pop() + + def _check_collision(self, position: Tuple[int, int]) -> bool: + """ + 检查碰撞 + + Args: + position: 要检查的位置 + + Returns: + 是否发生碰撞 + """ + x, y = position + + # 撞墙 + if x < 0 or x >= self.grid_width or y < 0 or y >= self.grid_height: + return True + + # 撞到自己 + if position in self.snake: + return True + + return False + + def _draw_grid(self): + """绘制网格背景""" + for x in range(0, self.width, self.cell_size): + pygame.draw.line(self.screen, self.colors['grid'], + (x, 0), (x, self.height)) + for y in range(0, self.height, self.cell_size): + pygame.draw.line(self.screen, self.colors['grid'], + (0, y), (self.width, y)) + + def _draw_snake(self): + """绘制蛇""" + for i, (x, y) in enumerate(self.snake): + rect = pygame.Rect( + x * self.cell_size, + y * self.cell_size, + self.cell_size - 2, + self.cell_size - 2 + ) + + # 蛇头颜色不同 + color = self.colors['snake_head'] if i == 0 else self.colors['snake_body'] + pygame.draw.rect(self.screen, color, rect, border_radius=5) + + # 画蛇眼睛 + if i == 0: + eye_size = 4 + if self.direction == Direction.UP: + eye1_pos = (x * self.cell_size + 5, y * self.cell_size + 5) + eye2_pos = (x * self.cell_size + self.cell_size - 9, y * self.cell_size + 5) + elif self.direction == Direction.DOWN: + eye1_pos = (x * self.cell_size + 5, y * self.cell_size + self.cell_size - 9) + eye2_pos = (x * self.cell_size + self.cell_size - 9, y * self.cell_size + self.cell_size - 9) + elif self.direction == Direction.LEFT: + eye1_pos = (x * self.cell_size + 5, y * self.cell_size + 5) + eye2_pos = (x * self.cell_size + 5, y * self.cell_size + self.cell_size - 9) + else: # RIGHT + eye1_pos = (x * self.cell_size + self.cell_size - 9, y * self.cell_size + 5) + eye2_pos = (x * self.cell_size + self.cell_size - 9, y * self.cell_size + self.cell_size - 9) + + pygame.draw.circle(self.screen, (255, 255, 255), eye1_pos, eye_size) + pygame.draw.circle(self.screen, (255, 255, 255), eye2_pos, eye_size) + + def _draw_food(self): + """绘制食物""" + x, y = self.food + center_x = x * self.cell_size + self.cell_size // 2 + center_y = y * self.cell_size + self.cell_size // 2 + + # 画圆形食物 + pygame.draw.circle(self.screen, self.colors['food'], + (center_x, center_y), self.cell_size // 2 - 2) + + # 画高光 + highlight_pos = (center_x - 3, center_y - 3) + pygame.draw.circle(self.screen, (255, 100, 100), highlight_pos, 3) + + def _draw_score(self): + """绘制分数""" + # 分数背景 + score_rect = pygame.Rect(10, 10, 200, 50) + pygame.draw.rect(self.screen, self.colors['score_bg'], score_rect, border_radius=10) + + # 分数文字 + score_text = self.font_small.render(f"Score: {self.score}", True, self.colors['text']) + self.screen.blit(score_text, (20, 25)) + + # 速度显示 + speed_text = self.font_small.render(f"Speed: {self.speed}", True, self.colors['text']) + self.screen.blit(speed_text, (120, 25)) + + def _draw_game_over(self): + """绘制游戏结束画面""" + # 半透明遮罩 + overlay = pygame.Surface((self.width, self.height)) + overlay.set_alpha(128) + overlay.fill((0, 0, 0)) + self.screen.blit(overlay, (0, 0)) + + # 游戏结束文字 + game_over_text = self.font_large.render("GAME OVER", True, self.colors['food']) + text_rect = game_over_text.get_rect(center=(self.width // 2, self.height // 2 - 50)) + self.screen.blit(game_over_text, text_rect) + + # 分数 + score_text = self.font_medium.render(f"Final Score: {self.score}", True, self.colors['text']) + score_rect = score_text.get_rect(center=(self.width // 2, self.height // 2 + 20)) + self.screen.blit(score_text, score_rect) + + # 提示文字 + hint_text = self.font_small.render("Press SPACE to Restart", True, self.colors['text']) + hint_rect = hint_text.get_rect(center=(self.width // 2, self.height // 2 + 80)) + self.screen.blit(hint_text, hint_rect) + + hint_text2 = self.font_small.render("Press ESC to Quit", True, self.colors['text']) + hint_rect2 = hint_text2.get_rect(center=(self.width // 2, self.height // 2 + 120)) + self.screen.blit(hint_text2, hint_rect2) + + def _draw_paused(self): + """绘制暂停画面""" + # 半透明遮罩 + overlay = pygame.Surface((self.width, self.height)) + overlay.set_alpha(128) + overlay.fill((0, 0, 0)) + self.screen.blit(overlay, (0, 0)) + + # 暂停文字 + pause_text = self.font_large.render("PAUSED", True, self.colors['text']) + text_rect = pause_text.get_rect(center=(self.width // 2, self.height // 2)) + self.screen.blit(pause_text, text_rect) + + hint_text = self.font_small.render("Press P to Resume", True, self.colors['text']) + hint_rect = hint_text.get_rect(center=(self.width // 2, self.height // 2 + 60)) + self.screen.blit(hint_text, hint_rect) + + def draw(self): + """绘制游戏画面""" + # 清空屏幕 + self.screen.fill(self.colors['background']) + + # 绘制网格 + self._draw_grid() + + # 绘制食物 + self._draw_food() + + # 绘制蛇 + self._draw_snake() + + # 绘制分数 + self._draw_score() + + # 游戏结束画面 + if self.game_over: + self._draw_game_over() + + # 暂停画面 + if self.paused: + self._draw_paused() + + # 更新显示 + pygame.display.flip() + + def run(self): + """运行游戏主循环""" + running = True + + while running: + # 处理事件 + running = self._handle_events() + + # 更新游戏状态 + self._update() + + # 绘制画面 + self.draw() + + # 控制帧率 + self.clock.tick(self.speed) + + pygame.quit() + sys.exit() + + +def main(): + """主函数""" + print("=" * 60) + print("🐍 经典贪吃蛇游戏") + print("=" * 60) + print("\n游戏说明:") + print(" - 使用方向键 ↑↓←→ 或 WASD 控制蛇的移动") + print(" - 吃到红色食物可以得分并变长") + print(" - 撞墙或撞到自己则游戏结束") + print(" - 按 P 键暂停游戏") + print(" - 按 ESC 键退出游戏") + print(" - 按空格键重新开始") + print("\n每吃 5 个食物,速度会加快哦!") + print("=" * 60) + print("\n正在启动游戏...") + + game = SnakeGame() + game.run() + + +if __name__ == "__main__": + main()