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
+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()