Files

383 lines
13 KiB
Python
Raw Permalink Blame History

This file contains ambiguous Unicode characters
This file contains Unicode characters that might be confused with other characters. If you think that this is intentional, you can safely ignore this warning. Use the Escape button to reveal them.
"""
经典贪吃蛇游戏
使用方向键控制蛇的移动,吃到食物后蛇会变长
撞墙或撞到自己则游戏结束
"""
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()