贪吃蛇

import pygame
import random
import sys
import time

======================

游戏配置

======================

class GameConfig:
"""游戏配置参数"""
# 网格设置
GRID_SIZE = 20
GRID_WIDTH = 30
GRID_HEIGHT = 20

# 窗口大小
WIDTH = GRID_SIZE * GRID_WIDTH
HEIGHT = GRID_SIZE * GRID_HEIGHT

# 游戏速度
BASE_SPEED = 10  # 初始速度(帧率)
SPEED_INCREMENT = 1  # 每得多少分增加速度

# 颜色定义
WHITE = (255, 255, 255)
BLACK = (0, 0, 0)
GREEN = (0, 255, 0)
RED = (255, 0, 0)
GRAY = (100, 100, 100)

# 字体设置
FONT_SIZE = 36
FONT_SMALL_SIZE = 24

======================

初始化游戏

======================

def init_game():
"""初始化游戏"""
pygame.init()
pygame.display.init()
pygame.font.init()

win = pygame.display.set_mode((GameConfig.WIDTH, GameConfig.HEIGHT))
pygame.display.set_caption("贪吃蛇游戏")
font = pygame.font.Font(None, GameConfig.FONT_SIZE)
small_font = pygame.font.Font(None, GameConfig.FONT_SMALL_SIZE)

return win, font, small_font

======================

蛇类

======================

class Snake:
"""贪吃蛇游戏中的蛇"""

def __init__(self):
    """初始化蛇"""
    # 计算中心位置
    center_x, center_y = GameConfig.GRID_WIDTH // 2, GameConfig.GRID_HEIGHT // 2
    self.body = [
        (center_x, center_y),
        (center_x - 1, center_y),
        (center_x - 2, center_y)
    ]
    self.direction = (1, 0)  # 初始方向向右
    self.last_direction = self.direction
    self.grow_next_move = False  # 标记是否需要增长

def change_direction(self, new_direction):
    """改变蛇的移动方向

    参数:
        new_direction (tuple): 新的方向向量 (x, y)
    """
    # 防止蛇直接反向移动
    if (new_direction[0] * -1, new_direction[1] * -1) != self.direction:
        self.last_direction = self.direction
        self.direction = new_direction

def move(self):
    """移动蛇"""
    head_x, head_y = self.body[0]
    new_head = (head_x + self.direction[0], head_y + self.direction[1])
    self.body.insert(0, new_head)
    if not self.grow_next_move:
        self.body.pop()  # 不增长则移除尾部
    else:
        self.grow_next_move = False  # 重置增长标记

def check_collision(self):
    """检查蛇是否发生碰撞

    返回:
        bool: 如果发生碰撞返回True,否则返回False
    """
    head = self.body[0]
    # 边界碰撞检测
    if (head[0] < 0 or head[0] >= GameConfig.GRID_WIDTH or
            head[1] < 0 or head[1] >= GameConfig.GRID_HEIGHT):
        return True
    # 自身碰撞检测(从第二个身体节点开始检查)
    if head in self.body[1:]:
        return True
    return False

def draw(self, win):
    """绘制蛇

    参数:
        win: pygame窗口对象
    """
    for i, segment in enumerate(self.body):
        # 蛇头用深绿色,身体用浅绿色
        color = (0, 200, 0) if i == 0 else GameConfig.GREEN  # 蛇头颜色加深
        pygame.draw.rect(win, color,
                         (segment[0] * GameConfig.GRID_SIZE,
                          segment[1] * GameConfig.GRID_SIZE,
                          GameConfig.GRID_SIZE, GameConfig.GRID_SIZE))
        # 绘制蛇身边框(黑色细线)
        pygame.draw.rect(win, GameConfig.BLACK,
                         (segment[0] * GameConfig.GRID_SIZE,
                          segment[1] * GameConfig.GRID_SIZE,
                          GameConfig.GRID_SIZE, GameConfig.GRID_SIZE), 1)

======================

食物类

======================

class Food:
"""贪吃蛇游戏中的食物"""

def __init__(self, snake):
    """初始化食物

    参数:
        snake: 蛇对象,用于确保食物不会生成在蛇身上
    """
    self.position = self.generate_new_position(snake)
    self.spawn_time = time.time()  # 记录生成时间(用于未来扩展)

def generate_new_position(self, snake):
    """生成新的食物位置

    参数:
        snake: 蛇对象

    返回:
        tuple: 新的食物位置 (x, y)
    """
    while True:
        x = random.randint(0, GameConfig.GRID_WIDTH - 1)
        y = random.randint(0, GameConfig.GRID_HEIGHT - 1)
        if (x, y) not in snake.body:  # 确保不在蛇身上
            return x, y

def draw(self, win):
    """绘制食物

    参数:
        win: pygame窗口对象
    """
    # 绘制食物主体(红色)
    pygame.draw.rect(win, GameConfig.RED,
                     (self.position[0] * GameConfig.GRID_SIZE,
                      self.position[1] * GameConfig.GRID_SIZE,
                      GameConfig.GRID_SIZE, GameConfig.GRID_SIZE))
    # 绘制食物高光(白色小方块)
    highlight_size = GameConfig.GRID_SIZE // 3
    pygame.draw.rect(win, GameConfig.WHITE,
                     (self.position[0] * GameConfig.GRID_SIZE + 2,
                      self.position[1] * GameConfig.GRID_SIZE + 2,
                      highlight_size, highlight_size))

======================

游戏功能函数

======================

def draw_grid(win):
"""绘制网格背景

参数:
    win: pygame窗口对象
"""
for x in range(0, GameConfig.WIDTH, GameConfig.GRID_SIZE):
    pygame.draw.line(win, GameConfig.GRAY, (x, 0), (x, GameConfig.HEIGHT), 1)  # 水平线
for y in range(0, GameConfig.HEIGHT, GameConfig.GRID_SIZE):
    pygame.draw.line(win, GameConfig.GRAY, (0, y), (GameConfig.WIDTH, y), 1)  # 垂直线

def draw_score(score, win, font):
"""绘制得分

参数:
    score (int): 当前得分
    win: pygame窗口对象
    font: 字体对象
"""
score_text = font.render(f"得分: {score}", True, GameConfig.WHITE)
win.blit(score_text, (10, 10))  # 左上角显示得分

def show_game_over(score, win, font, small_font):
"""显示游戏结束界面

参数:
    score (int): 最终得分
    win: pygame窗口对象
    font: 主字体对象
    small_font: 小字体对象
"""
win.fill(GameConfig.BLACK)

# 游戏结束标题
game_over_text = font.render("GAME OVER", True, GameConfig.WHITE)
game_over_rect = game_over_text.get_rect(center=(GameConfig.WIDTH // 2, GameConfig.HEIGHT // 2 - 50))
win.blit(game_over_text, game_over_rect)

# 最终得分
score_text = font.render(f"最终得分: {score}", True, GameConfig.WHITE)
score_rect = score_text.get_rect(center=(GameConfig.WIDTH // 2, GameConfig.HEIGHT // 2))
win.blit(score_text, score_rect)

# 提示信息(小字体)
restart_text = small_font.render("按 R 键重新开始", True, GameConfig.WHITE)
restart_rect = restart_text.get_rect(center=(GameConfig.WIDTH // 2, GameConfig.HEIGHT // 2 + 40))
win.blit(restart_text, restart_rect)

quit_text = small_font.render("按 Q 键退出游戏", True, GameConfig.WHITE)
quit_rect = quit_text.get_rect(center=(GameConfig.WIDTH // 2, GameConfig.HEIGHT // 2 + 70))
win.blit(quit_text, quit_rect)

pygame.display.update()

def pause_game(win, font):
"""暂停游戏

参数:
    win: pygame窗口对象
    font: 字体对象
"""
paused = True
while paused:
    for event in pygame.event.get():
        if event.type == pygame.QUIT:
            pygame.quit()
            sys.exit()
        elif event.type == pygame.KEYDOWN:
            if event.key == pygame.K_p:  # 按P键继续
                paused = False
            elif event.key == pygame.K_q:  # 按Q键退出
                pygame.quit()
                sys.exit()

    # 绘制暂停界面
    win.fill(GameConfig.BLACK)
    pause_text = font.render("游戏暂停", True, GameConfig.WHITE)
    pause_rect = pause_text.get_rect(center=(GameConfig.WIDTH // 2, GameConfig.HEIGHT // 2))
    win.blit(pause_text, pause_rect)

    continue_text = font.render("按 P 继续游戏", True, GameConfig.WHITE)
    continue_rect = continue_text.get_rect(center=(GameConfig.WIDTH // 2, GameConfig.HEIGHT // 2 + 40))
    win.blit(continue_text, continue_rect)

    quit_text = font.render("按 Q 退出游戏", True, GameConfig.WHITE)
    quit_rect = quit_text.get_rect(center=(GameConfig.WIDTH // 2, GameConfig.HEIGHT // 2 + 80))
    win.blit(quit_text, quit_rect)

    pygame.display.update()

======================

游戏主循环

======================

def game_loop(win, font, small_font):
"""游戏主循环

参数:
    win: pygame窗口对象
    font: 主字体对象
    small_font: 小字体对象

返回:
    bool: 游戏是否正常结束(未崩溃)
"""
snake = Snake()
food = Food(snake)
score = 0
clock = pygame.time.Clock()
game_over = False

while not game_over:
    # 事件处理
    for event in pygame.event.get():
        if event.type == pygame.QUIT:
            pygame.quit()
            sys.exit()
        elif event.type == pygame.KEYDOWN:
            if event.key in [pygame.K_w, pygame.K_UP, pygame.K_i]:
                snake.change_direction((0, -1))  # 向上
            elif event.key in [pygame.K_s, pygame.K_DOWN, pygame.K_k]:
                snake.change_direction((0, 1))  # 向下
            elif event.key in [pygame.K_a, pygame.K_LEFT, pygame.K_j]:
                snake.change_direction((-1, 0))  # 向左
            elif event.key in [pygame.K_d, pygame.K_RIGHT, pygame.K_l]:
                snake.change_direction((1, 0))  # 向右
            elif event.key == pygame.K_p:  # 暂停/继续
                pause_game(win, font)

    # 移动蛇
    grow = False
    if snake.body[0] == food.position:
        score += 1
        grow = True
        snake.grow_next_move = True  # 标记需要增长
        food = Food(snake)  # 生成新食物

    snake.move()  # 执行移动

    # 碰撞检测
    if snake.check_collision():
        show_game_over(score, win, font, small_font)
        return False  # 游戏结束(非正常退出)

    # 绘制界面
    win.fill(GameConfig.BLACK)
    draw_grid(win)  # 绘制网格背景
    snake.draw(win)  # 绘制蛇
    food.draw(win)  # 绘制食物
    draw_score(score, win, font)  # 绘制得分

    # 调整游戏速度(分数越高,速度越快)
    speed = GameConfig.BASE_SPEED + (score // GameConfig.SPEED_INCREMENT)
    clock.tick(speed)  # 控制帧率

    pygame.display.update()  # 更新屏幕

return True  # 游戏正常结束(未崩溃)

======================

主函数

======================

def main():
"""游戏主函数"""
win, font, small_font = init_game() # 初始化游戏窗口和字体

while True:
    # 运行一局游戏
    game_result = game_loop(win, font, small_font)

    # 游戏结束后等待用户操作
    if not game_result:  # 游戏异常结束(如碰撞)
        waiting = True
        while waiting:
            for event in pygame.event.get():
                if event.type == pygame.QUIT:
                    pygame.quit()
                    sys.exit()
                elif event.type == pygame.KEYDOWN:
                    if event.key == pygame.K_r:  # 按R键重新开始
                        waiting = False
                    elif event.key == pygame.K_q:  # 按Q键退出
                        pygame.quit()
                        sys.exit()

if name == "main":
main() # 启动游戏

posted @ 2025-06-22 02:06  邓雁戈  阅读(12)  评论(0)    收藏  举报