Add 经典贪吃蛇游戏 - Pygame 实现的精美贪吃蛇小游戏

This commit is contained in:
traetest
2026-04-13 01:57:04 +08:00
parent ab57f0cd33
commit 4bcce60d32
4 changed files with 617 additions and 0 deletions
+225
View File
@@ -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
```
祝你玩得开心!🐍🎮
+1
View File
@@ -0,0 +1 @@
pygame>=2.5.0
+9
View File
@@ -0,0 +1,9 @@
"""
Python 小游戏合集
贪吃蛇游戏启动文件
"""
from snake_game import main
if __name__ == "__main__":
main()
+382
View File
@@ -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()