当前位置:

首页 > 编程开发 > Python基于Pygame开发的记忆训练小游戏

Python基于Pygame开发的记忆训练小游戏

基于Pygame开发的记忆方格游戏,玩家需在短暂展示中记住网格内高亮方格位置,随后凭记忆点击。网格从3×3逐步扩大至6×6,高亮数量随16级关卡递增。答对得分为高亮方格数×10,答错游戏结束,可重试或重开。采用深色调界面与状态机驱动流程。

本文目录

    项目概述

    说到用 Python 做 2D 游戏,Pygame 绝对是个绕不开的老朋友。它把图形、声音、输入和事件循环打包得整整齐齐,拿来练手或者做些小项目都挺顺手。这次我们用 Pygame 做一个经典记忆类小游戏——记忆方格(Memory Grid)。

    玩法很直接:玩家需要在短暂的展示时间里,记住网格中哪些方格被高亮了,然后等亮光消失后,凭记忆把刚才的位置逐个点出来。随着关卡推进,网格从 3×3 一路扩大到 6×6,高亮方格的数量也在不断爬坡,对短期记忆的挑战一步步升级。具体细节如下:

    • 记忆阶段:每轮开始时,目标方格会以金色高亮显示 1.2 秒,屏幕底部还有一根倒计时进度条,时刻提醒你抓紧时间。
    • 作答阶段:高亮消失后,玩家逐一点击自己认为正确的方格。点对了方格变成青绿色并触发粒子爆炸特效,点错了则变红并立即结束本轮。
    • 关卡递进:全部点对后自动进入下一关,难度按照预先设计好的表格线性提升——先增加高亮数量,再扩大网格尺寸,总共 16 级。
    • 得分规则:每关得分为 高亮方格数 × 10,方格越多单关分值越高,最高分跨局保留。
    • 失败处理:点错即游戏结束,结束画面会同时展示正确的图案(半透明金色),并提供“再试本关”和“重新开始”两个选项。
    • 键盘操作:R 键重新开始,Enter 键再试本关(仅游戏结束时有效)。

    Python基于Pygame开发的记忆训练小游戏

    游戏实现

    初始化与基础设置

    游戏启动时先初始化 Pygame,然后定义好屏幕尺寸和 HUD 高度。游戏区是 520×520 像素的正方形,顶部留出 70 像素作为 HUD 信息栏,总窗口高度就是 590。

    pygame.init()
    
    SIZE  = 520
    HUD_H = 70
    screen = pygame.display.set_mode((SIZE, SIZE + HUD_H))
    pygame.display.set_caption("记忆方格")
    clock  = pygame.time.Clock()
    

    颜色定义

    BG      = (12,  10,  18)    # 全局深色背景
    HUD_BG  = (18,  14,  28)    # HUD 背景
    GRID_BG = (22,  18,  34)    # 网格背景板
    EMPTY   = (35,  28,  55)    # 空格底色
    CORRECT = (93,  202, 165)   # 答对颜色(青绿)
    WRONG   = (226,  75,  74)   # 答错颜色(红)
    ACTIVE  = (83,   74, 183)   # 强调色(靛蓝)
    REVEAL  = (250, 199, 117)   # 展示阶段高亮色(金)
    WHITE   = (230, 225, 210)   # 主文字
    GRAY    = (110, 100, 130)   # 次要文字
    DIM     = (60,   52,  90)   # 暗色按钮底色
    

    整体采用深紫色调背景,金色、青绿色、红色三种功能色分工明确,视觉语义一眼就能看懂。

    难度表格

    LEVELS = [
        (3, 2),   # lv1 : 3×3, 2 个高亮格
        (3, 3),   # lv2
        (3, 4),   # lv3
        (4, 3),   # lv4 : 网格扩大至 4×4
        (4, 4),   # lv5
        # ...共 16 级
        (6, 9),   # lv16: 6×6, 9 个高亮格
    ]
    

    每个元素是 (grid_size, num_lit) 二元组,高亮比例始终控制在 20%~40% 之间,保证难度循序渐进又不至于卡关。如果关卡数超过了表格长度,就自动停留在最高难度。

    字体加载

    CHINESE_FONT_PATH = r"C:/Windows/Fonts/simsun.ttc"
    try:
        font_hud = pygame.font.Font(CHINESE_FONT_PATH, 22)
        font_big = pygame.font.Font(CHINESE_FONT_PATH, 52)
        font_mid = pygame.font.Font(CHINESE_FONT_PATH, 26)
        font_sm  = pygame.font.Font(CHINESE_FONT_PATH, 18)
    except FileNotFoundError:
        font_hud = pygame.font.SysFont("Arial", 20, bold=True)
        # ...
    

    优先加载系统宋体,这样中文能正常显示;万一路径不对,就回退到 SysFont,保证跨平台都能跑。

    布局计算

    网格尺寸会随关卡动态变化,所以每个方格的大小也需要实时算。布局函数 compute_layout 专门干这个事:

    def compute_layout(cols):
        area  = SIZE - GAP * (cols + 1)   # 可用像素宽度(扣除所有间距)
        tw    = area // cols               # 单格宽度
        th    = tw                         # 保持正方形
        total_w = cols * tw + GAP * (cols + 1)
        ox    = (SIZE - total_w) // 2      # 水平居中偏移
        return tw, th, ox
    

    固定间距 GAP = 10,均匀分配可用宽度,整个网格水平居中。当网格从 3 列增加到 6 列时,方格边长会自动从约 160px 缩小到约 73px,始终填满同一区域。

    而 tile_rect 则通过行列索引直接算出每个方格的像素坐标,供绘制和碰撞检测复用:

    def tile_rect(row, col, cols, tile_w, tile_h, ox):
        x = ox + GAP + col * (tile_w + GAP)
        y = HUD_H + GAP + row * (tile_h + GAP)
        return pygame.Rect(x, y, tile_w, tile_h)
    

    核心类设计

    粒子类(Particle)

    跟打砖块游戏里的粒子效果类似,点击方格时触发小型爆炸动画,答对和答错用不同颜色区分:

    class Particle:
        def __init__(self, x, y, color):
            self.vx  = random.uniform(-2.5, 2.5)
            self.vy  = random.uniform(-3.5, 0.5)
            self.life = 22
            self.color = color
    
        def update(self):
            self.x  += self.vx
            self.y  += self.vy
            self.vy += 0.18    # 模拟重力
            self.life -= 1
    
        def draw(self, surf):
            a = min(255, self.life * 11)   # 透明度线性衰减
            s = pygame.Surface((5, 5), pygame.SRCALPHA)
            s.fill((*self.color[:3], a))
            surf.blit(s, (int(self.x), int(self.y)))
    

    答对生成青绿色粒子,答错生成红色粒子,颜色直接告诉玩家结果。

    游戏主类(Game)

    Game 类用状态机驱动整个流程,所有逻辑集中在一处,清晰又好扩展。

    状态机设计:

    reveal → input → correct → (下一关 reveal)
                    ↘ wrong → gameover → restart / retry
    

    五个状态各有各的绘制逻辑和事件响应方式,职责分明。

    构造函数 __init__:

    def __init__(self):
        self.score     = 0
        self.best      = 0
        self.level     = 0      # LEVELS 表格索引
        self.particles = []
        self._new_round()
    

    新回合初始化 _new_round:

    def _new_round(self):
        cols, num_lit = LEVELS[min(self.level, len(LEVELS) - 1)]
        self.cols    = cols
        self.num_lit = num_lit
        self.tile_w, self.tile_h, self.ox = compute_layout(cols)
        self.pattern = set(random.sample(range(cols * cols), num_lit))
        self.clicked  = set()    # 玩家已点中的正确格
        self.wrong_set = set()   # 玩家点错的格
        self.state   = "reveal"
        self.timer   = time.time()
    

    pattern 用 random.sample 从所有格子索引中无重复抽取 num_lit 个,保证每局图案随机且不重叠。clicked 和 wrong_set 分开存储,绘制时分颜色渲染。

    重开与重试:

    def _restart(self):
        """完全重开:分数和关卡归零。"""
        self.score = 0
        self.level = 0
        self.particles = []
        self._new_round()
    
    def _retry(self):
        """再试本关:保留分数和最高分,重置图案。"""
        self.particles = []
        self._new_round()
    

    两种重置策略分开处理,_retry 不修改 self.level,玩家可以在同一关卡反复练,不影响历史最高分。

    事件处理 handle_events

    def handle_events(self):
        for event in pygame.event.get():
            ...
            if event.type == pygame.MOUSEBUTTONDOWN and event.button == 1:
                if self.state == "input":
                    idx = self._cell_at(*event.pos)
                    if idx is not None and idx not in self.clicked and idx not in self.wrong_set:
                        if idx in self.pattern:
                            self.clicked.add(idx)
                            # 生成青绿粒子
                            if self.clicked == self.pattern:
                                self.score += self.num_lit * 10
                                self.state = "correct"
                        else:
                            self.wrong_set.add(idx)
                            # 生成红色粒子
                            self.state = "wrong"
    

    点击逻辑分三层:① 点到有效格子、② 该格尚未被点过、③ 是否在答案集合中。用 self.clicked == self.pattern 集合相等判断是否全部答对,避免了逐一遍历。

    单元格点击检测 _cell_at:

    def _cell_at(self, mx, my):
        for r in range(self.cols):
            for c in range(self.cols):
                rect = tile_rect(r, c, self.cols, self.tile_w, self.tile_h, self.ox)
                if rect.collidepoint(mx, my):
                    return r * self.cols + c
        return None
    

    遍历所有格子矩形,找到鼠标位置对应的线性索引,与 pattern 集合中的索引格式一致。

    核心更新 update

    def update(self):
        now = time.time()
        if self.state == "reveal":
            if now - self.timer >= REVEAL_SECS:    # 展示 1.2 秒后切换
                self.state = "input"
        elif self.state == "correct":
            if now - self.timer >= 0.7:            # 短暂绿色反馈后进入下一关
                self.level = min(self.level + 1, len(LEVELS) - 1)
                self._new_round()
        elif self.state == "wrong":
            if now - self.timer >= 1.0:            # 短暂红色反馈后显示结束画面
                self.state = "gameover"
    
        for p in self.particles[:]:
            p.update()
            if p.life <= 0:
                self.particles.remove(p)
    

    用 time.time() 计时,而不是帧计数,这样不管机器帧率多少,状态切换时间都是一致的。“答对”和“答错”都预留了一小段视觉反馈时间,画面不会突然跳变。

    绘制方法 draw

    倒计时进度条:

    if self.state == "reveal":
        remaining = max(0.0, REVEAL_SECS - (time.time() - self.timer))
        bar_w = int((remaining / REVEAL_SECS) * (SIZE - 40))
        pygame.draw.rect(screen, DIM,    (20, y, SIZE - 40, 6), border_radius=3)
        pygame.draw.rect(screen, REVEAL, (20, y, bar_w,    6), border_radius=3)
    

    进度条的宽度跟剩余展示时间线性对应,实时缩短,给玩家一种“时间在跑”的紧迫感。

    方格多状态渲染:

    for idx in range(cols * cols):
        r, c = divmod(idx, cols)
        rect = tile_rect(r, c, cols, tw, th, ox)
    
        pygame.draw.rect(screen, EMPTY, rect, border_radius=6)   # 基础底色
    
        if revealing and idx in self.pattern:
            pygame.draw.rect(screen, REVEAL, rect, border_radius=6)   # 金色展示
        elif idx in self.clicked:
            pygame.draw.rect(screen, CORRECT, rect, border_radius=6)  # 青绿已答
        elif idx in self.wrong_set:
            pygame.draw.rect(screen, WRONG, rect, border_radius=6)    # 红色答错
        elif showing_answer and idx in self.pattern:
            # 游戏结束时半透明显示正确答案
            s = pygame.Surface((tw, th), pygame.SRCALPHA)
            pygame.draw.rect(s, (*REVEAL, 120), (0, 0, tw, th), border_radius=6)
            screen.blit(s, rect.topleft)
    

    每个方格根据当前状态和所属集合选择渲染颜色,逻辑清晰。游戏结束时用半透明金色叠加显示正确答案,方便玩家对照学习。

    鼠标悬停高亮:

    if self.state == "input":
        mx, my = pygame.mouse.get_pos()
        if rect.collidepoint(mx, my) and idx not in self.clicked and idx not in self.wrong_set:
            s = pygame.Surface((tw, th), pygame.SRCALPHA)
            pygame.draw.rect(s, (255, 255, 255, 30), (0, 0, tw, th), border_radius=6)
            screen.blit(s, rect.topleft)
    

    仅在作答阶段、鼠标悬停在未点击格子上时叠加一个半透明白色高亮,交互反馈很轻,不会干扰信息。

    结束画面按钮:

    for btn, label, color in [
        (btn_retry,   "再试本关  Enter", ACTIVE),
        (btn_restart, "重新开始  R",     DIM),
    ]:
        hover = btn.collidepoint(mx, my)
        bg    = tuple(min(255, v + 30) for v in color) if hover else color
        pygame.draw.rect(screen, bg,    btn, border_radius=8)
        pygame.draw.rect(screen, WHITE, btn, width=1, border_radius=8)
    

    鼠标悬停时按钮颜色各通道提亮 30,模拟简单的 hover 效果;同时按钮内显示对应的键盘快捷键,提醒玩家也能用键盘操作。

    绘制顺序是:背景 → HUD → 网格底板 → 方格 → 粒子 → 状态遮罩 → 结束按钮,层次严格保证。

    主循环 run:

    def run(self):
        while True:
            self.handle_events()
            self.update()
            self.draw()
            clock.tick(60)
    

    固定 60 FPS,保证动画和计时器在不同性能的机器上表现一致。

    全部代码

    import pygame
    import sys
    import random
    import time
    
    pygame.init()
    
    SIZE     = 520
    HUD_H    = 70
    screen   = pygame.display.set_mode((SIZE, SIZE + HUD_H))
    pygame.display.set_caption("记忆方格")
    clock    = pygame.time.Clock()
    
    # ── Colors ──────────────────────────────────────────────────────────────────
    BG        = (12,  10,  18)
    HUD_BG    = (18,  14,  28)
    GRID_BG   = (22,  18,  34)
    EMPTY     = (35,  28,  55)
    CORRECT   = (93, 202, 165)    # teal-green
    WRONG     = (226, 75,  74)    # red
    ACTIVE    = (83,  74, 183)    # indigo
    REVEAL    = (250,199, 117)    # gold
    WHITE     = (230, 225, 210)
    GRAY      = (110, 100, 130)
    DIM       = (60,  52,  90)
    
    CHINESE_FONT_PATH = r"C:/Windows/Fonts/simsun.ttc"
    try:
        font_hud  = pygame.font.Font(CHINESE_FONT_PATH, 22)
        font_big  = pygame.font.Font(CHINESE_FONT_PATH, 52)
        font_mid  = pygame.font.Font(CHINESE_FONT_PATH, 26)
        font_sm   = pygame.font.Font(CHINESE_FONT_PATH, 18)
    except FileNotFoundError:
        font_hud  = pygame.font.SysFont("Arial", 20, bold=True)
        font_big  = pygame.font.SysFont("Arial", 48, bold=True)
        font_mid  = pygame.font.SysFont("Arial", 24, bold=True)
        font_sm   = pygame.font.SysFont("Arial", 16)
    
    # ── Difficulty table  (grid_size, num_lit) ──────────────────────────────────
    # Keep lit-ratio between ~20%-40% for good difficulty
    LEVELS = [
        (3, 2),   # lv1  : 3×3, 2 lit
        (3, 3),   # lv2
        (3, 4),   # lv3
        (4, 3),   # lv4  : 4×4, 3 lit  (grid grows)
        (4, 4),   # lv5
        (4, 5),   # lv6
        (4, 6),   # lv7
        (5, 4),   # lv8  : 5×5
        (5, 5),   # lv9
        (5, 6),   # lv10
        (5, 7),   # lv11
        (6, 5),   # lv12 : 6×6
        (6, 6),   # lv13
        (6, 7),   # lv14
        (6, 8),   # lv15
        (6, 9),   # lv16
    ]
    
    REVEAL_SECS = 1.2      # how long to show the pattern
    
    GAP = 10               # gap between tiles
    
    
    def compute_layout(cols):
        """Return (tile_w, tile_h, offset_x) for a given grid size."""
        area  = SIZE - GAP * (cols + 1)
        tw    = area // cols
        th    = tw
        total_w = cols * tw + GAP * (cols + 1)
        ox    = (SIZE - total_w) // 2
        return tw, th, ox
    
    
    def tile_rect(row, col, cols, tile_w, tile_h, ox):
        x = ox + GAP + col * (tile_w + GAP)
        y = HUD_H + GAP + row * (tile_h + GAP)
        return pygame.Rect(x, y, tile_w, tile_h)
    
    
    class Particle:
        def __init__(self, x, y, color):
            self.x   = float(x)
            self.y   = float(y)
            self.vx  = random.uniform(-2.5, 2.5)
            self.vy  = random.uniform(-3.5, 0.5)
            self.life = 22
            self.color = color
    
        def update(self):
            self.x  += self.vx
            self.y  += self.vy
            self.vy += 0.18
            self.life -= 1
    
        def draw(self, surf):
            if self.life <= 0: return
            a = min(255, self.life * 11)
            s = pygame.Surface((5, 5), pygame.SRCALPHA)
            s.fill((*self.color[:3], a))
            surf.blit(s, (int(self.x), int(self.y)))
    
    
    class Game:
        def __init__(self):
            self.score      = 0
            self.best       = 0
            self.level      = 0          # index into LEVELS
            self.particles  = []
            self._new_round()
    
        # ── State machine ──────────────────────────────────────────────────────
        # state: "reveal"  → showing pattern
        #        "input"   → waiting for player clicks
        #        "correct" → brief green flash
        #        "wrong"   → brief red flash
        #        "gameover"
        def _restart(self):
            """Full restart: score and level reset."""
            self.score = 0
            self.level = 0
            self.particles = []
            self._new_round()
    
        def _retry(self):
            """Same level, keep score and best."""
            self.particles = []
            self._new_round()
    
        def _new_round(self):
            cols, num_lit = LEVELS[min(self.level, len(LEVELS) - 1)]
            self.cols       = cols
            self.num_lit    = num_lit
            self.tile_w, self.tile_h, self.ox = compute_layout(cols)
            self.pattern    = set(random.sample(range(cols * cols), num_lit))
            self.clicked    = set()
            self.wrong_set  = set()
            self.state      = "reveal"
            self.timer      = time.time()
            self.flash_alpha = 0
    
        def _cell_at(self, mx, my):
            """Return (row, col) or None."""
            cols = self.cols
            for r in range(cols):
                for c in range(cols):
                    rect = tile_rect(r, c, cols, self.tile_w, self.tile_h, self.ox)
                    if rect.collidepoint(mx, my):
                        return r * cols + c
            return None
    
        def _btn_rects(self):
            """Return (btn_restart_rect, btn_retry_rect) for the gameover overlay."""
            cx = SIZE // 2
            cy = HUD_H + SIZE // 2
            bw, bh = 160, 38
            gap = 20
            btn_retry   = pygame.Rect(cx - bw - gap // 2, cy + 60, bw, bh)
            btn_restart = pygame.Rect(cx + gap // 2,       cy + 60, bw, bh)
            return btn_restart, btn_retry
    
        def handle_events(self):
            for event in pygame.event.get():
                if event.type == pygame.QUIT:
                    pygame.quit(); sys.exit()
                if event.type == pygame.KEYDOWN:
                    if event.key == pygame.K_r:
                        self._restart()
                    if event.key == pygame.K_RETURN and self.state == "gameover":
                        self._retry()
                if event.type == pygame.MOUSEBUTTONDOWN and event.button == 1:
                    if self.state == "input":
                        idx = self._cell_at(*event.pos)
                        if idx is not None and idx not in self.clicked and idx not in self.wrong_set:
                            if idx in self.pattern:
                                self.clicked.add(idx)
                                cols = self.cols
                                r, c  = divmod(idx, cols)
                                rect  = tile_rect(r, c, cols, self.tile_w, self.tile_h, self.ox)
                                for _ in range(10):
                                    self.particles.append(Particle(rect.centerx, rect.centery, CORRECT))
                                if self.clicked == self.pattern:
                                    self.score += self.num_lit * 10
                                    if self.score > self.best: self.best = self.score
                                    self.state = "correct"
                                    self.timer = time.time()
                            else:
                                self.wrong_set.add(idx)
                                cols = self.cols
                                r, c  = divmod(idx, cols)
                                rect  = tile_rect(r, c, cols, self.tile_w, self.tile_h, self.ox)
                                for _ in range(10):
                                    self.particles.append(Particle(rect.centerx, rect.centery, WRONG))
                                self.state = "wrong"
                                self.timer = time.time()
                    elif self.state == "gameover":
                        btn_restart, btn_retry = self._btn_rects()
                        if btn_restart.collidepoint(event.pos):
                            self._restart()
                        elif btn_retry.collidepoint(event.pos):
                            self._retry()
    
        def update(self):
            now = time.time()
            if self.state == "reveal":
                if now - self.timer >= REVEAL_SECS:
                    self.state = "input"
            elif self.state == "correct":
                if now - self.timer >= 0.7:
                    self.level  = min(self.level + 1, len(LEVELS) - 1)
                    self._new_round()
            elif self.state == "wrong":
                if now - self.timer >= 1.0:
                    # show correct pattern then game over
                    self.state = "gameover"
    
            for p in self.particles[:]:
                p.update()
                if p.life <= 0:
                    self.particles.remove(p)
    
        def draw(self):
            screen.fill(BG)
            cols  = self.cols
            tw, th, ox = self.tile_w, self.tile_h, self.ox
    
            # ── HUD ─────────────────────────────────────────────────────────────
            pygame.draw.rect(screen, HUD_BG, (0, 0, SIZE, HUD_H))
            pygame.draw.line(screen, ACTIVE, (0, HUD_H), (SIZE, HUD_H), 1)
            screen.blit(font_hud.render("记忆方格", True, REVEAL), (14, 10))
            lv_text = f"LV {self.level + 1}  ({cols}×{cols}  {self.num_lit}格)"
            screen.blit(font_hud.render(lv_text,                True, WHITE), (14, 38))
            screen.blit(font_hud.render(f"得分 {self.score}",  True, WHITE), (SIZE // 2 - 50, 10))
            screen.blit(font_hud.render(f"最高 {self.best}",   True, GRAY),  (SIZE // 2 - 50, 38))
            screen.blit(font_sm.render("R=重开",               True, GRAY),  (SIZE - 70, 42))
    
            # ── Grid background ──────────────────────────────────────────────────
            grid_total_h = cols * (th + GAP) + GAP
            grid_rect = pygame.Rect(ox - 2, HUD_H + GAP - 2,
                                    cols * (tw + GAP) + GAP + 4,
                                    grid_total_h + 4)
            pygame.draw.rect(screen, GRID_BG, grid_rect, border_radius=10)
    
            revealing = self.state == "reveal"
            showing_answer = self.state in ("wrong", "gameover")
    
            # ── Tiles ────────────────────────────────────────────────────────────
            for idx in range(cols * cols):
                r, c = divmod(idx, cols)
                rect = tile_rect(r, c, cols, tw, th, ox)
    
                # base slot
                pygame.draw.rect(screen, EMPTY, rect, border_radius=6)
    
                if revealing and idx in self.pattern:
                    pygame.draw.rect(screen, REVEAL, rect, border_radius=6)
    
                elif self.state == "input" or self.state in ("correct","wrong","gameover"):
                    if idx in self.clicked:
                        pygame.draw.rect(screen, CORRECT, rect, border_radius=6)
                    elif idx in self.wrong_set:
                        pygame.draw.rect(screen, WRONG, rect, border_radius=6)
                    elif showing_answer and idx in self.pattern:
                        # dim reveal of correct answer
                        s = pygame.Surface((tw, th), pygame.SRCALPHA)
                        pygame.draw.rect(s, (*REVEAL, 120), (0, 0, tw, th), border_radius=6)
                        screen.blit(s, rect.topleft)
    
                # hover highlight in input state
                if self.state == "input":
                    mx, my = pygame.mouse.get_pos()
                    if rect.collidepoint(mx, my) and idx not in self.clicked and idx not in self.wrong_set:
                        s = pygame.Surface((tw, th), pygame.SRCALPHA)
                        pygame.draw.rect(s, (255, 255, 255, 30), (0, 0, tw, th), border_radius=6)
                        screen.blit(s, rect.topleft)
    
            # ── Particles ────────────────────────────────────────────────────────
            for p in self.particles:
                p.draw(screen)
    
            # ── Status overlays ──────────────────────────────────────────────────
            if self.state == "reveal":
                remaining = max(0.0, REVEAL_SECS - (time.time() - self.timer))
                bar_w = int((remaining / REVEAL_SECS) * (SIZE - 40))
                pygame.draw.rect(screen, DIM, (20, HUD_H + grid_total_h + GAP * 2 + 4, SIZE - 40, 6), border_radius=3)
                pygame.draw.rect(screen, REVEAL, (20, HUD_H + grid_total_h + GAP * 2 + 4, bar_w, 6), border_radius=3)
                label = font_sm.render("记住这些方格!", True, REVEAL)
                screen.blit(label, (SIZE // 2 - label.get_width() // 2,
                                    HUD_H + grid_total_h + GAP * 2 + 14))
    
            elif self.state == "input":
                left = self.num_lit - len(self.clicked)
                label = font_sm.render(f"还需选 {left} 个方格", True, GRAY)
                screen.blit(label, (SIZE // 2 - label.get_width() // 2, SIZE + HUD_H - 28))
    
            elif self.state == "correct":
                overlay = pygame.Surface((SIZE, SIZE), pygame.SRCALPHA)
                overlay.fill((93, 202, 165, 40))
                screen.blit(overlay, (0, HUD_H))
                t = font_mid.render("✓ 全部正确!", True, CORRECT)
                screen.blit(t, (SIZE // 2 - t.get_width() // 2, HUD_H + SIZE // 2 - 20))
    
            elif self.state in ("wrong", "gameover"):
                overlay = pygame.Surface((SIZE, SIZE), pygame.SRCALPHA)
                overlay.fill((226, 75, 74, 35))
                screen.blit(overlay, (0, HUD_H))
    
            if self.state == "gameover":
                overlay2 = pygame.Surface((SIZE, SIZE), pygame.SRCALPHA)
                overlay2.fill((12, 10, 18, 180))
                screen.blit(overlay2, (0, HUD_H))
                t1 = font_big.render("GAME OVER", True, WRONG)
                t2 = font_hud.render(f"得分 {self.score}   最高 {self.best}", True, WHITE)
                cx = SIZE // 2
                cy = HUD_H + SIZE // 2
                screen.blit(t1, (cx - t1.get_width() // 2, cy - 80))
                screen.blit(t2, (cx - t2.get_width() // 2, cy - 10))
    
                btn_restart, btn_retry = self._btn_rects()
                mx, my = pygame.mouse.get_pos()
                for btn, label, color in [
                    (btn_retry,   "再试本关  Enter", ACTIVE),
                    (btn_restart, "重新开始  R",     DIM),
                ]:
                    hover = btn.collidepoint(mx, my)
                    bg    = tuple(min(255, v + 30) for v in color) if hover else color
                    pygame.draw.rect(screen, bg,    btn, border_radius=8)
                    pygame.draw.rect(screen, WHITE, btn, width=1, border_radius=8)
                    txt = font_sm.render(label, True, WHITE)
                    screen.blit(txt, (btn.centerx - txt.get_width() // 2,
                                      btn.centery - txt.get_height() // 2))
    
            pygame.display.flip()
    
        def run(self):
            while True:
                self.handle_events()
                self.update()
                self.draw()
                clock.tick(60)
    
    
    if __name__ == "__main__":
        Game().run()
    
    本站声明:本文内容由网友自发贡献,版权归原作者所有,本站不承担相应法律责任。如您发现有涉嫌抄袭侵权的内容,请联系bd@zhengruan.com
    作者最新文章
    编程开发 Python
    相关文章 更多
    python删除一个列表里的重复元素的方法与代码示例
    python删除一个列表里的重复元素的方法与代码示例

    学习如何使用Python高效删除列表中的重复元素。本文提供5种常见方法的代码示例,涵盖保持顺序与不保持顺序的场景,适合初学者和进阶开发者参考。

    Python写接口的步骤及完整代码示例
    Python写接口的步骤及完整代码示例

    详细讲解使用Python编写Web接口的核心步骤与完整代码示例,涵盖环境准备、路由定义、数据校验、服务启动及调试测试方法。

    codex怎么读取word文档及docx文件操作教程
    codex怎么读取word文档及docx文件操作教程

    介绍如何利用编程工具读取和解析Word文档(docx格式),涵盖基础文本提取代码示例、结构化数据处理逻辑,以及处理复杂排版时的技术局限与替代方案。

    怎么在Python中调用C语言代码提升性能
    怎么在Python中调用C语言代码提升性能

    详细讲解在Python中调用C语言代码提升性能的完整实践指南,包含原理分析、编译步骤、ctypes与CFFI具体实现、性能对比及常见陷阱。

    Python与C语言的性能差异及底层原理
    Python与C语言的性能差异及底层原理

    深入解析Python与C语言在执行效率上的数量级差异,从编译机制、变量内存布局、动态类型推断到GIL锁,系统阐明底层核心原理。

    Python和C语言哪个更适合编程入门
    Python和C语言哪个更适合编程入门

    客观对比Python与C语言在心智负担、底层机制与学习反馈上的核心差异,提供清晰有据的选择标准与切实的入门学习路径。

    selenium上传文件教程本地文件路径设置方法
    selenium上传文件教程本地文件路径设置方法

    深入解析Selenium处理文件上传的技术细节,涵盖标准HTML控件的路径直接注入法与非标准控件的系统级模拟方案,提供Windows与Linux环境下的路径设置最佳实践及常见异常排查。

    大数据开发语言选型:Java对比Scala与Python的优缺点及适用场景
    大数据开发语言选型:Java对比Scala与Python的优缺点及适用场景

    针对大数据工程场景,系统对比Java、Scala与Python在执行性能、生态结合、开发效率和维护成本上的核心差异,给出清晰的架构选型依据与落地方案。

    langgraph python guide中文入门教程详解
    langgraph python guide中文入门教程详解

    本篇LangGraph Python中文入门教程详解,系统梳理StateGraph状态机定义、节点编写、边与条件路由、检查点持久化(MemorySaver)等核心机制,提供从环境安装到完整循环智能体构建的代码示例与实操指导。

    Windows 安装 Python:从官方入口到虚拟环境的可复现实践
    Windows 安装 Python:从官方入口到虚拟环境的可复现实践

    面向后端开发者,基于 Python 官方文档说明如何在 Windows 上安装 Python、创建 venv 虚拟环境及使用 pip 管理依赖。包含可复制命令、验证方法及工具选择边界,避免常见配置误区。

    查看更多
    精品专题 更多
    装机必备
    装机必备

    正软商城装机必备专区,精选办公、浏览器、安全防护、影音播放、压缩解压、设计创作和系统工具等电脑常用正版软件,帮助用户快速完成新电脑软件配置。

    Windows
    Windows

    正软商城Windows软件专区,汇集适用于Windows电脑的办公、设计、安全防护、影音播放、开发工具和系统优化软件,提供软件介绍、系统要求、正版授权及购买下载服务。

    PDF教程
    PDF教程

    正软商城PDF教程频道提供PDF编辑、转换、合并、拆分、压缩及格式处理方法,同时介绍常用PDF软件和工具的使用技巧。

    Mac软件 更多
    讯飞智作 网页版
    讯飞智作 网页版
    Windows/ macOS/ Linux

    讯飞智作是一款基于语音合成技术、虚拟人驱动技术与数字人形象声音构建能力的 AI 数字人音视频创作平台。

    火山引擎 网页版
    火山引擎 网页版
    Windows/ macOS/ Linux

    火山引擎是字节跳动旗下企业级云与AI服务平台。聚焦大模型核心能力,依托字节自研豆包多模态大模型,凭借火山方舟一站式平台,提供模型精调、推理部署、智能应用搭建等全链路服务,结合高性能AI算力,助力各行业快速落地大模型数字化与智能化应用。

    Aperty macOS版
    Aperty macOS版
    macOS

    Aperty 是 Skylum 推出的 AI 人像修图软件,支持自然肤质修饰、瑕疵处理、面部与身体调整、预设及批量同步。

    WINDOWS 更多
    讯飞智作 网页版
    讯飞智作 网页版
    Windows/ macOS/ Linux

    讯飞智作是一款基于语音合成技术、虚拟人驱动技术与数字人形象声音构建能力的 AI 数字人音视频创作平台。

    火山引擎 网页版
    火山引擎 网页版
    Windows/ macOS/ Linux

    火山引擎是字节跳动旗下企业级云与AI服务平台。聚焦大模型核心能力,依托字节自研豆包多模态大模型,凭借火山方舟一站式平台,提供模型精调、推理部署、智能应用搭建等全链路服务,结合高性能AI算力,助力各行业快速落地大模型数字化与智能化应用。

    PixTV 网页版
    PixTV 网页版
    Windows

    PixTV 是一款在浏览器中使用的 AI 视觉内容创作平台,提供图片、视频、音频、文字生成、AI 分镜、局部重绘、提示词反推和无限画布等工具,适合短剧、漫剧、广告及社交媒体内容制作。