pygame红温 贪吃蛇

image.png
import pygame
import math
import random
import os

# --- 初始化和常量定义 ---
pygame.init()

# 屏幕设置
SCREEN_WIDTH, SCREEN_HEIGHT = 800, 600
screen = pygame.display.set_mode((SCREEN_WIDTH, SCREEN_HEIGHT))
pygame.display.set_caption("贪吃蛇小游戏")

# 颜色
COLOR_SKYBLUE = 'skyblue'
COLOR_BLACK = 'black'
COLOR_WHITE = 'white'
COLOR_RED = 'red'
COLOR_GREEN = 'green'
COLOR_GOLD = 'gold'

# 字体
try:
    # 尝试加载一个常见的中文字体
    FONT = pygame.font.Font('simhei.ttf', 36)
    SCORE_FONT = pygame.font.Font('simhei.ttf', 28)
    LEADERBOARD_FONT = pygame.font.Font('simhei.ttf', 24)
except FileNotFoundError:
    # 如果找不到,使用默认字体
    FONT = pygame.font.Font(None, 48)
    SCORE_FONT = pygame.font.Font(None, 36)
    LEADERBOARD_FONT = pygame.font.Font(None, 30)


# 游戏时钟
clock = pygame.time.Clock()
FPS = 60

# 高分榜文件
HIGHSCORE_FILE = 'highscores.txt'

# --- 游戏核心类与函数 ---

class Snake:
    """管理蛇的属性和行为的类"""
    def __init__(self):
        # 蛇的基础属性
        self.base_speed = 2  # 蛇的恒定速度
        # 定义尾部的半径锥度,使其看起来更自然
        self.tail_taper_radii = [9, 8, 7, 6, 5, 4, 3, 2, 1] 
        self.reset()

    def reset(self):
        """重置蛇的状态到初始位置"""
        # 定义初始的身体和头部半径
        self.main_body_radii = [
            12, 16, 14, 12, 10, 10, 10, 10, 10, 10,
            10, 10, 10, 10, 10, 10
        ]
        # 完整的半径列表 = 身体 + 尾巴
        self.circle_radius_list = self.main_body_radii + self.tail_taper_radii
        
        self.num_circles = len(self.circle_radius_list)
        self.circle_radians_list = [0] * self.num_circles
        
        # NEW: 蛇现在以恒定速度移动
        self.speed = self.base_speed 
        self.angle = 0
        self.angle_change = 2 

        # 初始化蛇的身体位置
        self.body = []
        for i in range(self.num_circles):
            self.body.append([SCREEN_WIDTH // 2 - i * 6 * 1.5, SCREEN_HEIGHT // 2])
        
        # 舌头参数
        self.tongue_count = 0

    def move(self):
        """根据当前方向移动蛇"""
        keys = pygame.key.get_pressed()
        
        # NEW: 移除了对W/UP键的依赖,蛇现在自动移动
        # 玩家只控制方向
        if keys[pygame.K_a] or keys[pygame.K_LEFT]:
            self.angle -= self.angle_change
        if keys[pygame.K_d] or keys[pygame.K_RIGHT]:
            self.angle += self.angle_change
        
        self.angle %= 360

        # 计算并更新头部位置
        head_x, head_y = self.body[0]
        dx = self.speed * math.cos(math.radians(self.angle))
        dy = self.speed * math.sin(math.radians(self.angle))
        self.body[0] = [head_x + dx, head_y + dy]
        self.circle_radians_list[0] = math.radians(self.angle)

        # 更新身体其他部分的位置
        for i in range(1, self.num_circles):
            prev_x, prev_y = self.body[i-1]
            curr_x, curr_y = self.body[i]
            
            distance = math.hypot(prev_x - curr_x, prev_y - curr_y)
            self.circle_radians_list[i] = math.atan2(prev_y - curr_y, prev_x - curr_x)
            
            # 保持每个身体部分之间的固定距离
            target_dist = (self.circle_radius_list[i-1] + self.circle_radius_list[i]) * 0.4
            if distance > target_dist:
                ratio = target_dist / distance
                self.body[i][0] = prev_x - (prev_x - curr_x) * ratio
                self.body[i][1] = prev_y - (prev_y - curr_y) * ratio

    def grow(self):
        """蛇吃掉苹果后变长,保持尾部锥度"""
        # 在尾部末端添加一个新的物理分段
        self.body.append(self.body[-1]) 
        
        # NEW: 在尾部锥度开始前插入一个新的身体半径,使身体变长
        self.circle_radius_list.insert(-len(self.tail_taper_radii), 10)

        # 添加一个对应的角度占位符
        self.circle_radians_list.append(self.circle_radians_list[-1])

        # 更新总节数
        self.num_circles = len(self.body)


    def check_collision(self):
        """检查蛇是否碰到边缘或自己"""
        head_x, head_y = self.body[0]
        head_radius = self.circle_radius_list[0]

        # 1. 检查边缘碰撞
        if not (head_radius < head_x < SCREEN_WIDTH - head_radius and \
                head_radius < head_y < SCREEN_HEIGHT - head_radius):
            return True
        
        # 2. 检查自身碰撞 (从第5节开始检查,避免头部刚转弯时误判)
        for i in range(5, self.num_circles):
            dist = math.hypot(head_x - self.body[i][0], head_y - self.body[i][1])
            if dist < (self.circle_radius_list[0] + self.circle_radius_list[i]) * 0.5:
                return True
        return False

    def draw(self):
        """在屏幕上绘制蛇"""
        # --- 计算身体多边形的锚点 ---
        polygon_point_pos_list = [(0, 0)] * (self.num_circles * 2)
        pattern_point_1_pos_list = [(0, 0)] * self.num_circles
        pattern_point_2_pos_list = [(0, 0)] * self.num_circles

        for j in range(self.num_circles):
            x, y = self.body[j]
            radius = self.circle_radius_list[j]
            angle = self.circle_radians_list[j]
            
            anchor_1 = (x + radius * math.cos(angle - math.pi/2), y + radius * math.sin(angle - math.pi/2))
            polygon_point_pos_list[j] = anchor_1
            pattern_point_1_pos_list[j] = anchor_1
            
            anchor_2 = (x + radius * math.cos(angle + math.pi/2), y + radius * math.sin(angle + math.pi/2))
            polygon_point_pos_list[self.num_circles * 2 - 1 - j] = anchor_2
            pattern_point_2_pos_list[j] = anchor_2

        # --- 绘制 ---
        # 身体
        pygame.draw.polygon(screen, COLOR_BLACK, polygon_point_pos_list)
        pygame.draw.polygon(screen, COLOR_WHITE, polygon_point_pos_list, 3)

        # 花纹
        for k in range(3, self.num_circles - len(self.tail_taper_radii), 2):
            pygame.draw.line(screen, COLOR_WHITE, pattern_point_1_pos_list[k], pattern_point_2_pos_list[k], 2)

        # NEW: 修复眼睛绘制逻辑,将其定位在头部 (self.body[0])
        eye_offset = 4    # 眼睛离头部边缘的距离
        eye_radius = 3    # 眼睛的大小
        head_center = self.body[0]
        head_radius = self.circle_radius_list[0]
        head_angle = self.circle_radians_list[0]

        # 计算眼睛相对于头部中心、半径和方向的位置
        eye_dist_from_center = head_radius - eye_offset
        
        # 眼睛1
        eye_1_angle = head_angle - math.pi/4.5 # 将眼睛放在中心线40度的位置
        eye_1_pos = (
            head_center[0] + eye_dist_from_center * math.cos(eye_1_angle),
            head_center[1] + eye_dist_from_center * math.sin(eye_1_angle)
        )
        pygame.draw.circle(screen, COLOR_WHITE, eye_1_pos, eye_radius)
        pygame.draw.circle(screen, COLOR_BLACK, eye_1_pos, eye_radius - 1) # 瞳孔

        # 眼睛2
        eye_2_angle = head_angle + math.pi/4.5
        eye_2_pos = (
            head_center[0] + eye_dist_from_center * math.cos(eye_2_angle),
            head_center[1] + eye_dist_from_center * math.sin(eye_2_angle)
        )
        pygame.draw.circle(screen, COLOR_WHITE, eye_2_pos, eye_radius)
        pygame.draw.circle(screen, COLOR_BLACK, eye_2_pos, eye_radius - 1) # 瞳孔


        # 舌头
        self.tongue_count = (self.tongue_count + 1) % 120 
        if self.tongue_count < 15: 
            tongue_length = self.circle_radius_list[0] * 1.5
            for i in range(int(tongue_length)):
                pos = (
                    self.body[0][0] + i * math.cos(self.circle_radians_list[0]),
                    self.body[0][1] + i * math.sin(self.circle_radians_list[0])
                )
                if i > tongue_length - 5: 
                     pygame.draw.circle(screen, COLOR_RED, (pos[0] + 3, pos[1] - 3), 1)
                     pygame.draw.circle(screen, COLOR_RED, (pos[0] - 3, pos[1] + 3), 1)
                else:
                     pygame.draw.circle(screen, COLOR_RED, pos, 1)


class Apple:
    """管理苹果的类"""
    def __init__(self):
        self.radius = 10
        self.spawn()

    def spawn(self):
        """在随机位置生成一个新苹果"""
        self.pos = [
            random.randint(self.radius, SCREEN_WIDTH - self.radius),
            random.randint(self.radius, SCREEN_HEIGHT - self.radius)
        ]

    def draw(self):
        """在屏幕上绘制苹果"""
        pygame.draw.circle(screen, COLOR_RED, self.pos, self.radius)
        pygame.draw.circle(screen, COLOR_BLACK, self.pos, self.radius, 1)


def draw_text(text, font, color, surface, x, y, center=False):
    """通用文本绘制函数"""
    textobj = font.render(text, 1, color)
    textrect = textobj.get_rect()
    if center:
        textrect.center = (x, y)
    else:
        textrect.topleft = (x, y)
    surface.blit(textobj, textrect)
    return textrect

def load_high_scores():
    """从文件加载高分榜"""
    if not os.path.exists(HIGHSCORE_FILE):
        return []
    with open(HIGHSCORE_FILE, 'r') as f:
        scores = [int(line.strip()) for line in f if line.strip().isdigit()]
    return sorted(scores, reverse=True)[:10]

def save_high_scores(score, scores_list):
    """将新分数添加到高分榜并保存"""
    scores_list.append(score)
    scores_list = sorted(list(set(scores_list)), reverse=True)[:10] # 去重并排序
    with open(HIGHSCORE_FILE, 'w') as f:
        for s in scores_list:
            f.write(str(s) + '\n')
    return scores_list

# --- 游戏状态管理 ---
class Game:
    """管理整个游戏流程和状态的类"""
    def __init__(self):
        self.game_state = 'start_screen'
        self.score = 0
        self.snake = Snake()
        self.apple = Apple()
        self.high_scores = load_high_scores()

    def reset_game(self):
        """重置游戏到初始状态"""
        self.score = 0
        self.snake.reset()
        self.apple.spawn()
        self.game_state = 'playing'

    def run(self):
        """游戏主循环"""
        running = True
        while running:
            for event in pygame.event.get():
                if event.type == pygame.QUIT:
                    running = False
                if event.type == pygame.MOUSEBUTTONDOWN:
                    self.handle_mouse_click(event.pos)

            screen.fill(COLOR_SKYBLUE)

            if self.game_state == 'start_screen':
                self.draw_start_screen()
            elif self.game_state == 'playing':
                self.run_game_logic()
            elif self.game_state == 'game_over':
                self.draw_game_over_screen()

            pygame.display.flip()
            clock.tick(FPS)
        
        pygame.quit()
        quit()
        
    def handle_mouse_click(self, pos):
        """处理不同游戏状态下的鼠标点击"""
        if self.game_state == 'start_screen':
            if self.start_button_rect.collidepoint(pos):
                self.reset_game()
        elif self.game_state == 'game_over':
            if self.restart_button_rect.collidepoint(pos):
                self.reset_game()
            if self.quit_button_rect.collidepoint(pos):
                pygame.quit()
                quit()

    def draw_start_screen(self):
        """绘制开始界面"""
        draw_text('程序性动画贪吃蛇', FONT, COLOR_BLACK, screen, SCREEN_WIDTH / 2, SCREEN_HEIGHT / 4, center=True)
        self.start_button_rect = draw_text('开始游戏', FONT, COLOR_GREEN, screen, SCREEN_WIDTH / 2, SCREEN_HEIGHT / 2, center=True)

    def run_game_logic(self):
        """运行核心游戏逻辑"""
        # 移动和绘制蛇
        self.snake.move()
        self.snake.draw()
        
        # 绘制苹果
        self.apple.draw()

        # 检查蛇头和苹果的碰撞
        head_pos = self.snake.body[0]
        dist = math.hypot(head_pos[0] - self.apple.pos[0], head_pos[1] - self.apple.pos[1])
        if dist < self.snake.circle_radius_list[0] + self.apple.radius:
            self.score += 10
            self.snake.grow()
            self.apple.spawn()

        # 检查游戏结束条件
        if self.snake.check_collision():
            self.high_scores = save_high_scores(self.score, self.high_scores)
            self.game_state = 'game_over'

        # 绘制分数
        draw_text(f'分数: {self.score}', SCORE_FONT, COLOR_BLACK, screen, SCREEN_WIDTH - 150, 10)


    def draw_game_over_screen(self):
        """绘制游戏结束界面"""
        draw_text('游戏结束', FONT, COLOR_RED, screen, SCREEN_WIDTH / 2, 80, center=True)
        draw_text(f'你的分数: {self.score}', SCORE_FONT, COLOR_BLACK, screen, SCREEN_WIDTH / 2, 150, center=True)
        
        # 绘制排行榜
        draw_text('排行榜', SCORE_FONT, COLOR_GOLD, screen, SCREEN_WIDTH / 2, 220, center=True)
        y_pos = 260
        for i, score in enumerate(self.high_scores):
            draw_text(f'{i+1}. {score}', LEADERBOARD_FONT, COLOR_BLACK, screen, SCREEN_WIDTH / 2, y_pos, center=True)
            y_pos += 30

        # 绘制按钮
        self.restart_button_rect = draw_text('重新开始', FONT, COLOR_GREEN, screen, SCREEN_WIDTH / 2, SCREEN_HEIGHT - 120, center=True)
        self.quit_button_rect = draw_text('退出', FONT, COLOR_RED, screen, SCREEN_WIDTH / 2, SCREEN_HEIGHT - 60, center=True)


# --- 启动游戏 ---
if __name__ == '__main__':
    game = Game()
    game.run()

image.png
©著作权归作者所有,转载或内容合作请联系作者
平台声明:文章内容(如有图片或视频亦包括在内)由作者上传并发布,文章内容仅代表作者本人观点,简书系信息发布平台,仅提供信息存储服务。

推荐阅读更多精彩内容