Python 贪吃蛇游戏 - 全屏功能
2026-01-29 21:06:57
发布于:浙江
以下内容为AI生成,作者转载
import warnings
warnings.filterwarnings("ignore",
message="pkg_resources is deprecated as an API")
import pygame
import random
import sys
import os
# 初始化pygame
pygame.init()
# 游戏常量(窗口模式下的默认尺寸)
SCREEN_WIDTH = 800
SCREEN_HEIGHT = 600
GRID_SIZE = 20
FPS = 10
# 方向
UP = (0, -1)
DOWN = (0, 1)
LEFT = (-1, 0)
RIGHT = (1, 0)
# 颜色定义
BACKGROUND_COLOR = (15, 15, 30)
GRID_COLOR = (30, 30, 50)
SNAKE_HEAD_COLOR = (0, 200, 100)
SNAKE_BODY_COLOR = (0, 180, 80)
FOOD_COLOR = (220, 50, 50)
TEXT_COLOR = (255, 255, 255)
BUTTON_COLOR = (70, 130, 180)
BUTTON_HOVER_COLOR = (100, 160, 210)
GAME_OVER_BG = (0, 0, 0, 180) # 半透明黑色
# 尝试加载中文字体,如果失败则使用默认英文字体
def load_font():
# 常见的中文字体列表,按优先级尝试
chinese_fonts = [
"msyh.ttc", # 微软雅黑
"simhei.ttf", # 黑体
"simsun.ttc", # 宋体
"STKAITI.TTF", # 楷体
"FZSTK.TTF", # 方正舒体
"DENG.TTF", # 等线
]
# 在Windows字体目录中查找中文字体
if sys.platform == "win32":
windir = os.environ.get("WINDIR", "C:\\Windows")
font_dirs = [os.path.join(windir, "Fonts")]
elif sys.platform == "darwin": # macOS
font_dirs = ["/System/Library/Fonts", "/Library/Fonts"]
else: # Linux
font_dirs = ["/usr/share/fonts", "/usr/local/share/fonts", os.path.expanduser("~/.fonts")]
# 添加当前目录
font_dirs.append(".")
# 尝试加载中文字体
for font_dir in font_dirs:
for font_name in chinese_fonts:
font_path = os.path.join(font_dir, font_name)
if os.path.exists(font_path):
try:
# 测试是否能成功加载
test_font = pygame.font.Font(font_path, 20)
print(f"成功加载字体: {font_path}")
return font_path
except:
continue
print("未找到中文字体,使用默认英文字体")
return None # 返回None将使用pygame默认字体
# 加载字体
font_path = load_font()
if font_path:
try:
chinese_supported = True
except:
chinese_supported = False
else:
chinese_supported = False
# 根据字体支持情况选择文本
if chinese_supported:
TITLE_TEXT = "贪吃蛇游戏"
SCORE_TEXT = "得分: "
HIGH_SCORE_TEXT = "最高分: "
LENGTH_TEXT = "长度: "
GAME_OVER_TEXT = "游戏结束!"
RESTART_TEXT = "重新开始"
EXIT_TEXT = "退出游戏"
PAUSE_TEXT = "游戏暂停"
CONTINUE_TEXT = "按空格键继续"
INSTRUCTIONS = [
"使用方向键或WASD控制蛇的移动",
"按空格键暂停/继续游戏",
"吃到红色食物可以增加长度和得分",
"避免撞到墙壁或自己的身体",
"F11: 全屏切换"
]
else:
TITLE_TEXT = "Snake Game"
SCORE_TEXT = "Score: "
HIGH_SCORE_TEXT = "High Score: "
LENGTH_TEXT = "Length: "
GAME_OVER_TEXT = "Game Over!"
RESTART_TEXT = "Restart"
EXIT_TEXT = "Exit Game"
PAUSE_TEXT = "Game Paused"
CONTINUE_TEXT = "Press SPACE to continue"
INSTRUCTIONS = [
"Use arrow keys or WASD to control the snake",
"Press SPACE to pause/resume the game",
"Eat red food to grow longer and earn points",
"Avoid walls and your own body",
"F11: Toggle Fullscreen"
]
class Snake:
def __init__(self, grid_width, grid_height):
self.grid_width = grid_width
self.grid_height = grid_height
self.reset()
def reset(self):
self.length = 3
self.positions = [(self.grid_width // 2, self.grid_height // 2)]
self.direction = RIGHT
self.next_direction = RIGHT
self.grow_pending = 2 # 初始长度为3,所以需要再增长2次
self.score = 0
def update_grid_size(self, new_grid_width, new_grid_height):
"""更新网格尺寸,并调整蛇的位置以适应新网格"""
# 计算位置比例并映射到新网格
old_width, old_height = self.grid_width, self.grid_height
self.grid_width = new_grid_width
self.grid_height = new_grid_height
# 调整蛇的位置,保持相对位置比例
new_positions = []
for x, y in self.positions:
# 计算在新网格中的位置(保持相对位置)
new_x = int(x * new_grid_width / old_width)
new_y = int(y * new_grid_height / old_height)
# 确保在网格范围内
new_x = min(max(new_x, 0), new_grid_width - 1)
new_y = min(max(new_y, 0), new_grid_height - 1)
new_positions.append((new_x, new_y))
# 更新位置,确保没有重复位置
unique_positions = []
for pos in new_positions:
if pos not in unique_positions:
unique_positions.append(pos)
self.positions = unique_positions
def get_head_position(self):
return self.positions[0]
def update(self):
# 更新方向
self.direction = self.next_direction
# 获取头部位置
head = self.get_head_position()
x, y = self.direction
new_x = (head[0] + x) % self.grid_width
new_y = (head[1] + y) % self.grid_height
new_head = (new_x, new_y)
# 检查是否撞到自己
if new_head in self.positions[1:]:
return False # 游戏结束
# 添加新头部
self.positions.insert(0, new_head)
# 如果需要增长,则保留尾部,否则移除尾部
if self.grow_pending > 0:
self.grow_pending -= 1
else:
self.positions.pop()
return True # 游戏继续
def grow(self):
self.grow_pending += 1
self.score += 10
def draw(self, surface, grid_size, grid_left, grid_top):
for i, p in enumerate(self.positions):
# 绘制蛇身
color = SNAKE_HEAD_COLOR if i == 0 else SNAKE_BODY_COLOR
rect = pygame.Rect(grid_left + p[0] * grid_size, grid_top + p[1] * grid_size, grid_size, grid_size)
pygame.draw.rect(surface, color, rect)
pygame.draw.rect(surface, (color[0]//2, color[1]//2, color[2]//2), rect, 1)
# 绘制蛇头眼睛
if i == 0:
eye_size = grid_size // 5
# 根据方向确定眼睛位置
if self.direction == RIGHT:
eye1_pos = (grid_left + p[0] * grid_size + grid_size - eye_size*2, grid_top + p[1] * grid_size + eye_size*2)
eye2_pos = (grid_left + p[0] * grid_size + grid_size - eye_size*2, grid_top + p[1] * grid_size + grid_size - eye_size*3)
elif self.direction == LEFT:
eye1_pos = (grid_left + p[0] * grid_size + eye_size, grid_top + p[1] * grid_size + eye_size*2)
eye2_pos = (grid_left + p[0] * grid_size + eye_size, grid_top + p[1] * grid_size + grid_size - eye_size*3)
elif self.direction == UP:
eye1_pos = (grid_left + p[0] * grid_size + eye_size*2, grid_top + p[1] * grid_size + eye_size)
eye2_pos = (grid_left + p[0] * grid_size + grid_size - eye_size*3, grid_top + p[1] * grid_size + eye_size)
else: # DOWN
eye1_pos = (grid_left + p[0] * grid_size + eye_size*2, grid_top + p[1] * grid_size + grid_size - eye_size*2)
eye2_pos = (grid_left + p[0] * grid_size + grid_size - eye_size*3, grid_top + p[1] * grid_size + grid_size - eye_size*2)
pygame.draw.rect(surface, (255, 255, 255), (eye1_pos[0], eye1_pos[1], eye_size, eye_size))
pygame.draw.rect(surface, (255, 255, 255), (eye2_pos[0], eye2_pos[1], eye_size, eye_size))
class Food:
def __init__(self, grid_width, grid_height):
self.grid_width = grid_width
self.grid_height = grid_height
self.position = (0, 0)
self.randomize_position()
def update_grid_size(self, new_grid_width, new_grid_height, snake_positions):
"""更新网格尺寸,调整食物位置以适应新网格,不重新随机化"""
# 计算旧位置在新网格中的相对位置
old_x, old_y = self.position
old_width, old_height = self.grid_width, self.grid_height
# 计算新位置(保持相对位置)
new_x = int(old_x * new_grid_width / old_width)
new_y = int(old_y * new_grid_height / old_height)
# 确保在网格范围内
new_x = min(max(new_x, 0), new_grid_width - 1)
new_y = min(max(new_y, 0), new_grid_height - 1)
# 更新网格尺寸和位置
self.grid_width = new_grid_width
self.grid_height = new_grid_height
self.position = (new_x, new_y)
# 如果食物位置与蛇重叠,则稍微调整位置
if self.position in snake_positions:
# 尝试在附近找一个空闲位置
for dx in range(-1, 2):
for dy in range(-1, 2):
if dx == 0 and dy == 0:
continue
test_x = (new_x + dx) % new_grid_width
test_y = (new_y + dy) % new_grid_height
if (test_x, test_y) not in snake_positions:
self.position = (test_x, test_y)
return
# 如果还是重叠,则保持原位置(蛇会吃掉它)
def randomize_position(self):
self.position = (random.randint(0, self.grid_width - 1), random.randint(0, self.grid_height - 1))
def draw(self, surface, grid_size, grid_left, grid_top):
rect = pygame.Rect(grid_left + self.position[0] * grid_size, grid_top + self.position[1] * grid_size, grid_size, grid_size)
pygame.draw.rect(surface, FOOD_COLOR, rect)
pygame.draw.rect(surface, (FOOD_COLOR[0]//2, FOOD_COLOR[1]//2, FOOD_COLOR[2]//2), rect, 1)
# 绘制食物内部细节
inner_margin = grid_size // 4
inner_rect = pygame.Rect(
grid_left + self.position[0] * grid_size + inner_margin,
grid_top + self.position[1] * grid_size + inner_margin,
grid_size - inner_margin * 2,
grid_size - inner_margin * 2
)
pygame.draw.rect(surface, (255, 150, 150), inner_rect)
class Button:
def __init__(self, x, y, width, height, text, action=None):
self.rect = pygame.Rect(x, y, width, height)
self.text = text
self.action = action
self.hovered = False
def draw(self, surface, font_small):
color = BUTTON_HOVER_COLOR if self.hovered else BUTTON_COLOR
pygame.draw.rect(surface, color, self.rect, border_radius=10)
pygame.draw.rect(surface, (255, 255, 255), self.rect, 2, border_radius=10)
text_surf = font_small.render(self.text, True, TEXT_COLOR)
text_rect = text_surf.get_rect(center=self.rect.center)
surface.blit(text_surf, text_rect)
def check_hover(self, pos):
self.hovered = self.rect.collidepoint(pos)
return self.hovered
def handle_event(self, event):
if event.type == pygame.MOUSEBUTTONDOWN and event.button == 1:
if self.hovered and self.action:
return self.action()
return None
class Game:
def __init__(self):
# 初始化为窗口模式
self.fullscreen = False
self.screen = pygame.display.set_mode((SCREEN_WIDTH, SCREEN_HEIGHT))
pygame.display.set_caption(TITLE_TEXT)
self.clock = pygame.time.Clock()
self.game_over = False
self.paused = False
self.high_score = 0
# 加载字体(现在在calculate_layout中重新创建字体对象)
self.font_path = font_path
self.chinese_supported = chinese_supported
# 计算布局参数(包括全屏模式下的自适应布局)
self.calculate_layout()
# 创建游戏对象
self.reset_game()
def calculate_layout(self):
"""计算游戏区域布局(包括全屏模式下的自适应布局)"""
if self.fullscreen:
# 获取全屏分辨率
screen_info = pygame.display.Info()
screen_width = screen_info.current_w
screen_height = screen_info.current_h
# 保持网格数量不变,只调整格子大小
# 这是关键:保持网格数量与窗口模式一致,避免蛇和食物位置无效
original_grid_width = SCREEN_WIDTH // GRID_SIZE
original_grid_height = SCREEN_HEIGHT // GRID_SIZE
# 基于高度和宽度计算合适的格子大小,取较小值以确保完全显示
grid_size_by_height = screen_height // original_grid_height
grid_size_by_width = screen_width // original_grid_width
self.grid_size = min(grid_size_by_height, grid_size_by_width)
# 限制格子大小的范围
self.grid_size = max(15, min(40, self.grid_size))
# 保持网格数量不变
self.grid_width = original_grid_width
self.grid_height = original_grid_height
# 计算游戏区域总宽度和高度
game_area_width = self.grid_width * self.grid_size
game_area_height = self.grid_height * self.grid_size
# 计算游戏区域位置(居中)
self.grid_left = (screen_width - game_area_width) // 2
self.grid_top = (screen_height - game_area_height) // 2
# 调整字体大小(根据屏幕大小缩放)
self.font_scale = min(screen_width / SCREEN_WIDTH, screen_height / SCREEN_HEIGHT)
# 重新创建字体对象
if self.font_path and self.chinese_supported:
try:
self.font_large = pygame.font.Font(self.font_path, int(48 * self.font_scale))
self.font_medium = pygame.font.Font(self.font_path, int(36 * self.font_scale))
self.font_small = pygame.font.Font(self.font_path, int(24 * self.font_scale))
self.font_tiny = pygame.font.Font(self.font_path, int(18 * self.font_scale))
except:
self.create_default_fonts()
else:
self.create_default_fonts()
else:
# 窗口模式使用固定布局
self.grid_size = GRID_SIZE
self.grid_width = SCREEN_WIDTH // GRID_SIZE
self.grid_height = SCREEN_HEIGHT // GRID_SIZE
self.grid_left = 0
self.grid_top = 0
self.font_scale = 1.0
# 创建字体对象
if self.font_path and self.chinese_supported:
try:
self.font_large = pygame.font.Font(self.font_path, 48)
self.font_medium = pygame.font.Font(self.font_path, 36)
self.font_small = pygame.font.Font(self.font_path, 24)
self.font_tiny = pygame.font.Font(self.font_path, 18)
except:
self.create_default_fonts()
else:
self.create_default_fonts()
def create_default_fonts(self):
"""创建默认字体对象"""
self.font_large = pygame.font.SysFont(None, int(48 * self.font_scale))
self.font_medium = pygame.font.SysFont(None, int(36 * self.font_scale))
self.font_small = pygame.font.SysFont(None, int(24 * self.font_scale))
self.font_tiny = pygame.font.SysFont(None, int(18 * self.font_scale))
def reset_game(self):
"""重置游戏状态"""
# 重新计算网格参数(如果全屏状态变化)
self.calculate_layout()
# 创建蛇和食物对象
self.snake = Snake(self.grid_width, self.grid_height)
self.food = Food(self.grid_width, self.grid_height)
# 确保食物不在蛇身上
while self.food.position in self.snake.positions:
self.food.randomize_position()
# 创建按钮
self.create_buttons()
self.game_over = False
self.paused = False
def create_buttons(self):
"""创建按钮"""
screen_width, screen_height = self.screen.get_size()
button_width = int(200 * self.font_scale)
button_height = int(50 * self.font_scale)
button_x = screen_width // 2 - button_width // 2
self.restart_button = Button(
button_x, screen_height // 2 + int(60 * self.font_scale),
button_width, button_height,
RESTART_TEXT, self.restart_game
)
self.exit_button = Button(
button_x, screen_height // 2 + int(130 * self.font_scale),
button_width, button_height,
EXIT_TEXT, self.quit_game
)
self.buttons = [self.restart_button, self.exit_button]
def restart_game(self):
self.reset_game()
return True
def quit_game(self):
pygame.quit()
sys.exit()
def toggle_fullscreen(self):
"""切换全屏/窗口模式"""
# 保存当前的暂停状态
was_paused = self.paused
# 切换全屏状态
self.fullscreen = not self.fullscreen
if self.fullscreen:
# 切换到全屏模式
self.screen = pygame.display.set_mode((0, 0), pygame.FULLSCREEN)
else:
# 切换到窗口模式
self.screen = pygame.display.set_mode((SCREEN_WIDTH, SCREEN_HEIGHT))
# 保存当前的网格尺寸
old_grid_width = self.grid_width
old_grid_height = self.grid_height
# 重新计算布局
self.calculate_layout()
# 更新蛇的网格尺寸(保持相对位置)
if self.snake:
self.snake.update_grid_size(self.grid_width, self.grid_height)
# 更新食物的网格尺寸(保持相对位置,不重新随机化)
if self.food:
self.food.update_grid_size(self.grid_width, self.grid_height, self.snake.positions)
# 重新创建按钮
self.create_buttons()
# 恢复暂停状态
self.paused = was_paused
# 更新显示
pygame.display.flip()
def handle_events(self):
for event in pygame.event.get():
if event.type == pygame.QUIT:
self.quit_game()
if event.type == pygame.KEYDOWN:
if not self.game_over:
if event.key == pygame.K_SPACE:
self.paused = not self.paused
if event.key == pygame.K_F11:
self.toggle_fullscreen()
return # 直接返回,避免处理其他按键
if not self.paused:
# 方向控制
if event.key in (pygame.K_UP, pygame.K_w) and self.snake.direction != DOWN:
self.snake.next_direction = UP
elif event.key in (pygame.K_DOWN, pygame.K_s) and self.snake.direction != UP:
self.snake.next_direction = DOWN
elif event.key in (pygame.K_LEFT, pygame.K_a) and self.snake.direction != RIGHT:
self.snake.next_direction = LEFT
elif event.key in (pygame.K_RIGHT, pygame.K_d) and self.snake.direction != LEFT:
self.snake.next_direction = RIGHT
else:
# 游戏结束后按空格重新开始
if event.key == pygame.K_SPACE:
self.restart_game()
if event.key == pygame.K_F11:
self.toggle_fullscreen()
return # 直接返回,避免处理其他按键
# 处理按钮事件
if self.game_over:
mouse_pos = pygame.mouse.get_pos()
for button in self.buttons:
button.check_hover(mouse_pos)
button.handle_event(event)
def update(self):
if self.game_over or self.paused:
return
# 更新蛇的位置
if not self.snake.update():
self.game_over = True
# 更新最高分
if self.snake.score > self.high_score:
self.high_score = self.snake.score
return
# 检查是否吃到食物
if self.snake.get_head_position() == self.food.position:
self.snake.grow()
self.food.randomize_position()
# 确保食物不在蛇身上
while self.food.position in self.snake.positions:
self.food.randomize_position()
def draw_grid(self):
screen_width, screen_height = self.screen.get_size()
# 绘制游戏区域背景
game_area_rect = pygame.Rect(
self.grid_left,
self.grid_top,
self.grid_width * self.grid_size,
self.grid_height * self.grid_size
)
pygame.draw.rect(self.screen, BACKGROUND_COLOR, game_area_rect)
# 绘制网格线
for x in range(0, self.grid_width * self.grid_size + 1, self.grid_size):
pygame.draw.line(self.screen, GRID_COLOR,
(self.grid_left + x, self.grid_top),
(self.grid_left + x, self.grid_top + self.grid_height * self.grid_size))
for y in range(0, self.grid_height * self.grid_size + 1, self.grid_size):
pygame.draw.line(self.screen, GRID_COLOR,
(self.grid_left, self.grid_top + y),
(self.grid_left + self.grid_width * self.grid_size, self.grid_top + y))
def draw_score(self):
# 根据字体缩放比例调整垂直间距
base_spacing = 50
spacing = int(base_spacing * self.font_scale)
# 计算起始Y位置,也根据缩放比例调整
start_y = int(20 * self.font_scale)
score_text = self.font_medium.render(f"{SCORE_TEXT}{self.snake.score}", True, TEXT_COLOR)
self.screen.blit(score_text, (20, start_y))
high_score_text = self.font_medium.render(f"{HIGH_SCORE_TEXT}{self.high_score}", True, TEXT_COLOR)
self.screen.blit(high_score_text, (20, start_y + spacing))
# 显示当前长度,使用较小的间距
length_spacing = int(40 * self.font_scale)
length_text = self.font_small.render(f"{LENGTH_TEXT}{len(self.snake.positions)}", True, TEXT_COLOR)
self.screen.blit(length_text, (20, start_y + spacing + length_spacing))
def draw_instructions(self):
screen_width, _ = self.screen.get_size()
# 计算行距,根据字体缩放比例调整
line_spacing = int(30 * self.font_scale)
for i, line in enumerate(INSTRUCTIONS):
instruction_text = self.font_tiny.render(line, True, (200, 200, 255))
# 动态计算垂直位置,使用缩放后的行距
text_y = int(20 * self.font_scale) + i * line_spacing
self.screen.blit(instruction_text, (screen_width - instruction_text.get_width() - 20, text_y))
def draw_game_over(self):
screen_width, screen_height = self.screen.get_size()
# 半透明覆盖层
overlay = pygame.Surface((screen_width, screen_height), pygame.SRCALPHA)
overlay.fill(GAME_OVER_BG)
self.screen.blit(overlay, (0, 0))
# 游戏结束文本
game_over_text = self.font_large.render(GAME_OVER_TEXT, True, (255, 100, 100))
game_over_rect = game_over_text.get_rect(center=(screen_width // 2, screen_height // 3))
self.screen.blit(game_over_text, game_over_rect)
# 最终得分
score_text = self.font_medium.render(f"{SCORE_TEXT}{self.snake.score}", True, TEXT_COLOR)
score_rect = score_text.get_rect(center=(screen_width // 2, screen_height // 3 + int(80 * self.font_scale)))
self.screen.blit(score_text, score_rect)
# 绘制按钮
for button in self.buttons:
button.draw(self.screen, self.font_small)
def draw_pause_screen(self):
screen_width, screen_height = self.screen.get_size()
# 半透明覆盖层
overlay = pygame.Surface((screen_width, screen_height), pygame.SRCALPHA)
overlay.fill((0, 0, 0, 128))
self.screen.blit(overlay, (0, 0))
# 暂停文本
pause_text = self.font_large.render(PAUSE_TEXT, True, TEXT_COLOR)
pause_rect = pause_text.get_rect(center=(screen_width // 2, screen_height // 3))
self.screen.blit(pause_text, pause_rect)
# 继续提示
continue_text = self.font_medium.render(CONTINUE_TEXT, True, TEXT_COLOR)
continue_rect = continue_text.get_rect(center=(screen_width // 2, screen_height // 3 + int(80 * self.font_scale)))
self.screen.blit(continue_text, continue_rect)
def draw(self):
# 填充背景色
self.screen.fill(BACKGROUND_COLOR)
# 绘制游戏区域
self.draw_grid()
self.snake.draw(self.screen, self.grid_size, self.grid_left, self.grid_top)
self.food.draw(self.screen, self.grid_size, self.grid_left, self.grid_top)
# 绘制分数和说明
self.draw_score()
self.draw_instructions()
# 绘制游戏结束画面
if self.game_over:
self.draw_game_over()
# 绘制暂停画面
if self.paused and not self.game_over:
self.draw_pause_screen()
pygame.display.flip()
def run(self):
while True:
self.handle_events()
self.update()
self.draw()
self.clock.tick(FPS)
if __name__ == "__main__":
game = Game()
game.run()
运行这个程序,你需要安装Python
并安装PyGame:
pip install pygame
(安装指南)
这里空空如也



















有帮助,赞一个