pygame紅溫 貪吃蛇

image.png
import pygame
import math
import random
import os

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

# 屏幕設(shè)置
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'

# --- 游戲核心類與函數(shù) ---

class Snake:
    """管理蛇的屬性和行為的類"""
    def __init__(self):
        # 蛇的基礎(chǔ)屬性
        self.base_speed = 2  # 蛇的恒定速度
        # 定義尾部的半徑錐度,使其看起來更自然
        self.tail_taper_radii = [9, 8, 7, 6, 5, 4, 3, 2, 1] 
        self.reset()

    def reset(self):
        """重置蛇的狀態(tài)到初始位置"""
        # 定義初始的身體和頭部半徑
        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: 蛇現(xiàn)在以恒定速度移動
        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])
        
        # 舌頭參數(shù)
        self.tongue_count = 0

    def move(self):
        """根據(jù)當(dāng)前方向移動蛇"""
        keys = pygame.key.get_pressed()
        
        # NEW: 移除了對W/UP鍵的依賴,蛇現(xiàn)在自動移動
        # 玩家只控制方向
        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)

        # 添加一個對應(yīng)的角度占位符
        self.circle_radians_list.append(self.circle_radians_list[-1])

        # 更新總節(jié)數(shù)
        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節(jié)開始檢查,避免頭部剛轉(zhuǎn)彎時誤判)
        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: 修復(fù)眼睛繪制邏輯,將其定位在頭部 (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):
    """通用文本繪制函數(shù)"""
    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):
    """將新分?jǐn)?shù)添加到高分榜并保存"""
    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

# --- 游戲狀態(tài)管理 ---
class Game:
    """管理整個游戲流程和狀態(tài)的類"""
    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):
        """重置游戲到初始狀態(tài)"""
        self.score = 0
        self.snake.reset()
        self.apple.spawn()
        self.game_state = 'playing'

    def run(self):
        """游戲主循環(huán)"""
        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):
        """處理不同游戲狀態(tài)下的鼠標(biāo)點擊"""
        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()

        # 檢查游戲結(jié)束條件
        if self.snake.check_collision():
            self.high_scores = save_high_scores(self.score, self.high_scores)
            self.game_state = 'game_over'

        # 繪制分?jǐn)?shù)
        draw_text(f'分?jǐn)?shù): {self.score}', SCORE_FONT, COLOR_BLACK, screen, SCREEN_WIDTH - 150, 10)


    def draw_game_over_screen(self):
        """繪制游戲結(jié)束界面"""
        draw_text('游戲結(jié)束', FONT, COLOR_RED, screen, SCREEN_WIDTH / 2, 80, center=True)
        draw_text(f'你的分?jǐn)?shù): {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
?著作權(quán)歸作者所有,轉(zhuǎn)載或內(nèi)容合作請聯(lián)系作者
【社區(qū)內(nèi)容提示】社區(qū)部分內(nèi)容疑似由AI輔助生成,瀏覽時請結(jié)合常識與多方信息審慎甄別。
平臺聲明:文章內(nèi)容(如有圖片或視頻亦包括在內(nèi))由作者上傳并發(fā)布,文章內(nèi)容僅代表作者本人觀點,簡書系信息發(fā)布平臺,僅提供信息存儲服務(wù)。

相關(guān)閱讀更多精彩內(nèi)容

友情鏈接更多精彩內(nèi)容