实战项目5:贪吃蛇游戏¶
项目概述¶
实现经典的贪吃蛇游戏,玩家控制蛇移动吃食物,蛇会逐渐变长。这是一个非常好的游戏开发入门项目,可以帮助你学习游戏编程的基本概念。
学习目标¶
- 掌握pygame游戏开发框架
- 学习游戏循环和事件处理
- 理解碰撞检测算法
- 掌握游戏状态管理
- 学习面向对象游戏编程
功能需求¶
基本功能¶
- 蛇的移动:使用方向键控制蛇的移动方向
- 食物生成:随机生成食物
- 吃食物:蛇吃到食物后变长,得分增加
- 碰撞检测:检测蛇是否撞墙或撞到自己
- 游戏结束:碰撞后游戏结束,显示最终分数
进阶功能¶
- 难度选择:简单、普通、困难三种难度
- 最高分记录:保存和显示最高分
- 暂停功能:按空格键暂停游戏
- 音效:添加游戏音效
- 动画效果:平滑的移动和转向动画
高级功能¶
- 多种模式:经典模式、无尽模式、关卡模式
- 障碍物:添加障碍物增加难度
- 特殊食物:不同食物有不同效果
- 多人游戏:支持双人游戏
- 排行榜:在线排行榜
实现方案¶
基础实现¶
import pygame
import random
import sys
# 初始化pygame
pygame.init()
# 颜色定义
WHITE = (255, 255, 255)
BLACK = (0, 0, 0)
RED = (255, 0, 0)
GREEN = (0, 255, 0)
BLUE = (0, 0, 255)
# 游戏配置
SCREEN_WIDTH = 800
SCREEN_HEIGHT = 600
GRID_SIZE = 20
GRID_WIDTH = SCREEN_WIDTH // GRID_SIZE
GRID_HEIGHT = SCREEN_HEIGHT // GRID_SIZE
FPS = 10
class SnakeGame:
"""贪吃蛇游戏类"""
def __init__(self):
"""初始化游戏"""
self.screen = pygame.display.set_mode((SCREEN_WIDTH, SCREEN_HEIGHT))
pygame.display.set_caption("贪吃蛇游戏")
self.clock = pygame.time.Clock()
self.font = pygame.font.Font(None, 36)
self.reset_game()
def reset_game(self):
"""重置游戏"""
# 蛇的初始位置
self.snake = [(GRID_WIDTH // 2, GRID_HEIGHT // 2)]
self.direction = (0, -1) # 向上移动
# 生成食物
self.food = self.generate_food()
# 分数
self.score = 0
self.high_score = self.load_high_score()
# 游戏状态
self.game_over = False
self.paused = False
def generate_food(self):
"""生成食物"""
while True:
food = (random.randint(0, GRID_WIDTH - 1),
random.randint(0, GRID_HEIGHT - 1))
if food not in self.snake:
return food
def load_high_score(self):
"""加载最高分"""
try:
with open('high_score.txt', 'r') as f:
return int(f.read())
except:
return 0
def save_high_score(self):
"""保存最高分"""
if self.score > self.high_score:
self.high_score = self.score
with open('high_score.txt', 'w') as f:
f.write(str(self.high_score))
def handle_events(self):
"""处理事件"""
for event in pygame.event.get():
if event.type == pygame.QUIT:
return False
if event.type == pygame.KEYDOWN:
if event.key == pygame.K_ESCAPE:
return False
if event.key == pygame.K_SPACE:
if self.game_over:
self.reset_game()
else:
self.paused = not self.paused
if not self.paused and not self.game_over:
if event.key == pygame.K_UP and self.direction != (0, 1):
self.direction = (0, -1)
elif event.key == pygame.K_DOWN and self.direction != (0, -1):
self.direction = (0, 1)
elif event.key == pygame.K_LEFT and self.direction != (1, 0):
self.direction = (-1, 0)
elif event.key == pygame.K_RIGHT and self.direction != (-1, 0):
self.direction = (1, 0)
return True
def update(self):
"""更新游戏状态"""
if self.game_over or self.paused:
return
# 计算新的头部位置
head = self.snake[0]
new_head = (head[0] + self.direction[0],
head[1] + self.direction[1])
# 检查碰撞
if (new_head[0] < 0 or new_head[0] >= GRID_WIDTH or
new_head[1] < 0 or new_head[1] >= GRID_HEIGHT or
new_head in self.snake):
self.game_over = True
self.save_high_score()
return
# 移动蛇
self.snake.insert(0, new_head)
# 检查是否吃到食物
if new_head == self.food:
self.score += 10
self.food = self.generate_food()
else:
self.snake.pop()
def draw(self):
"""绘制游戏画面"""
self.screen.fill(BLACK)
# 绘制蛇
for segment in self.snake:
rect = pygame.Rect(segment[0] * GRID_SIZE,
segment[1] * GRID_SIZE,
GRID_SIZE, GRID_SIZE)
pygame.draw.rect(self.screen, GREEN, rect)
pygame.draw.rect(self.screen, WHITE, rect, 1) # 边框
# 绘制食物
food_rect = pygame.Rect(self.food[0] * GRID_SIZE,
self.food[1] * GRID_SIZE,
GRID_SIZE, GRID_SIZE)
pygame.draw.rect(self.screen, RED, food_rect)
# 绘制分数
score_text = self.font.render(f"分数: {self.score}", True, WHITE)
high_score_text = self.font.render(f"最高分: {self.high_score}", True, WHITE)
self.screen.blit(score_text, (10, 10))
self.screen.blit(high_score_text, (10, 50))
# 游戏结束提示
if self.game_over:
game_over_text = self.font.render("游戏结束!按空格键重新开始", True, WHITE)
text_rect = game_over_text.get_rect(center=(SCREEN_WIDTH//2, SCREEN_HEIGHT//2))
self.screen.blit(game_over_text, text_rect)
# 暂停提示
if self.paused and not self.game_over:
pause_text = self.font.render("游戏暂停 - 按空格键继续", True, WHITE)
text_rect = pause_text.get_rect(center=(SCREEN_WIDTH//2, SCREEN_HEIGHT//2))
self.screen.blit(pause_text, text_rect)
pygame.display.flip()
def run(self):
"""运行游戏"""
running = True
while running:
running = self.handle_events()
self.update()
self.draw()
self.clock.tick(FPS)
pygame.quit()
sys.exit()
# 运行游戏
if __name__ == "__main__":
game = SnakeGame()
game.run()
面向对象版本¶
class Snake:
"""蛇类"""
def __init__(self, start_pos):
"""初始化蛇"""
self.body = [start_pos]
self.direction = (0, -1)
self.grow_pending = 0
def change_direction(self, new_direction):
"""改变方向"""
# 不能反向移动
if (new_direction[0] * -1, new_direction[1] * -1) != self.direction:
self.direction = new_direction
def move(self):
"""移动蛇"""
head = self.body[0]
new_head = (head[0] + self.direction[0],
head[1] + self.direction[1])
self.body.insert(0, new_head)
if self.grow_pending > 0:
self.grow_pending -= 1
else:
self.body.pop()
def check_collision(self, width, height):
"""检查碰撞"""
head = self.body[0]
# 检查墙壁碰撞
if (head[0] < 0 or head[0] >= width or
head[1] < 0 or head[1] >= height):
return True
# 检查自身碰撞
if head in self.body[1:]:
return True
return False
def eat(self):
"""吃食物"""
self.grow_pending += 1
class Food:
"""食物类"""
def __init__(self, width, height):
"""初始化食物"""
self.width = width
self.height = height
self.position = (0, 0)
self.respawn([])
def respawn(self, snake_body):
"""重新生成食物"""
while True:
self.position = (random.randint(0, self.width - 1),
random.randint(0, self.height - 1))
if self.position not in snake_body:
break
扩展功能¶
1. 添加难度选择¶
DIFFICULTY_LEVELS = {
'简单': {'speed': 8, 'points': 5},
'普通': {'speed': 10, 'points': 10},
'困难': {'speed': 15, 'points': 20}
}
def select_difficulty():
"""选择难度"""
print("选择难度:")
for i, level in enumerate(DIFFICULTY_LEVELS.keys(), 1):
print(f"{i}. {level}")
choice = input("请选择(1-3):")
levels = list(DIFFICULTY_LEVELS.keys())
if choice.isdigit() and 1 <= int(choice) <= len(levels):
return DIFFICULTY_LEVELS[levels[int(choice) - 1]]
return DIFFICULTY_LEVELS['普通']
2. 添加特殊食物¶
SPECIAL_FOODS = {
'normal': {'color': RED, 'points': 10, 'effect': None},
'speed': {'color': BLUE, 'points': 20, 'effect': 'speed_up'},
'slow': {'color': (255, 255, 0), 'points': 15, 'effect': 'slow_down'},
'shrink': {'color': (255, 0, 255), 'points': 30, 'effect': 'shrink'}
}
def generate_special_food():
"""生成特殊食物"""
rand = random.random()
if rand < 0.7:
return 'normal'
elif rand < 0.85:
return 'speed'
elif rand < 0.95:
return 'slow'
else:
return 'shrink'
3. 添加音效¶
import pygame.mixer
def init_sounds():
"""初始化音效"""
pygame.mixer.init()
sounds = {}
try:
sounds['eat'] = pygame.mixer.Sound('eat.wav')
sounds['crash'] = pygame.mixer.Sound('crash.wav')
sounds['game_over'] = pygame.mixer.Sound('game_over.wav')
except:
print("音效文件未找到,将使用静音模式")
sounds = None
return sounds
def play_sound(sounds, sound_name):
"""播放音效"""
if sounds and sound_name in sounds:
sounds[sound_name].play()
测试方法¶
def test_snake_movement():
"""测试蛇的移动"""
snake = Snake((10, 10))
assert len(snake.body) == 1
assert snake.direction == (0, -1)
snake.move()
assert len(snake.body) == 1
assert snake.body[0] == (10, 9)
snake.change_direction((1, 0))
snake.move()
assert snake.body[0] == (11, 9)
snake.eat()
snake.move()
assert len(snake.body) == 2
print("✓ 蛇的移动测试通过")
def test_collision_detection():
"""测试碰撞检测"""
snake = Snake((10, 10))
snake.body = [(10, 10), (10, 9), (10, 8)]
# 测试墙壁碰撞
snake.direction = (0, -1)
snake.move()
assert snake.check_collision(20, 20) == True
# 测试自身碰撞
snake.body = [(10, 10), (10, 9), (10, 8)]
snake.direction = (0, 1)
snake.move()
assert snake.check_collision(20, 20) == True
print("✓ 碰撞检测测试通过")
if __name__ == "__main__":
test_snake_movement()
test_collision_detection()
使用说明¶
- 安装pygame:
pip install pygame - 运行游戏:
python snake_game.py - 使用方向键控制蛇的移动
- 按空格键暂停/继续游戏
- 按ESC键退出游戏
扩展建议¶
- 添加关卡系统:不同的关卡有不同的地图和难度
- 多人游戏:支持双人游戏,一个键盘控制两条蛇
- 道具系统:添加各种道具增加游戏趣味性
- 存档功能:保存游戏进度
- 皮肤系统:选择不同的蛇和食物外观
- 成就系统:完成特定目标获得成就
- 排行榜:在线排行榜功能
- 移动端支持:移植到移动平台
学习要点¶
通过这个项目,你将学习到:
- 游戏开发基础:游戏循环、事件处理
- pygame框架:图形绘制、声音处理
- 面向对象编程:游戏对象的设计
- 算法应用:碰撞检测、路径规划
- 状态管理:游戏状态的控制
- 用户交互:键盘、鼠标输入处理
- 性能优化:帧率控制、资源管理
相关资源¶
开始制作你的游戏吧! 从基础版本开始,逐步添加更多功能。祝你游戏开发愉快!