展开菜单
首页 精品内容 本月促销 装机必备 Windows PDF教程 macOS软件 IOS软件 Android AI 专题
全部分类

当前位置:

首页 > 编程开发 > 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
    相关文章 更多
    精品专题 更多
    装机必备

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

    Windows

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

    PDF教程

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

    macOS软件

    正软商城macOS软件专区,精选适用于Mac电脑的办公、设计、影音、效率、开发和系统工具,提供软件功能介绍、macOS兼容版本、正版授权及购买下载服务。

    IOS软件

    正软商城iOS软件专区,精选适用于iPhone和iPad的办公、学习、影音、设计、效率及AI应用,提供功能介绍、适用设备、系统要求和正版获取方式等信息。

    AI

    正软商城AI软件专区,汇集AI写作、AI绘画、AI视频、AI办公、AI编程、AI翻译、智能客服和数据分析等人工智能工具,提供功能介绍、适用平台、收费方式及正版购买信息。

    Mac软件 更多
    火山引擎 网页版
    火山引擎 网页版

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

    立刻MV 网页版
    立刻MV 网页版

    立刻MV 面向音乐人、内容创作者和品牌团队,帮助用户低成本完成歌曲 MV、歌词视频、AI 分镜、图生视频与最终合成。

    蛙蛙写作 网页版
    蛙蛙写作 网页版

    蛙蛙写作 AI 创作工具,覆盖小说、剧本、文案,一键生成文稿,打通文字转视频全流程,高效解决创作难题。

    讯飞智作 网页版
    讯飞智作 网页版

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

    超级简历WonderCV 网页版
    超级简历WonderCV 网页版

    超级简历WonderCV是面向应届生和AI小白的智能求职平台,覆盖简历、面试与职业规划。

    音述AI 网页版
    音述AI 网页版

    音述AI,是一个以“用声音述说故事”为核心的 AI 音乐创作与声音分享社区。

    AionClaw macOS版
    AionClaw macOS版

    AionClaw 是可在桌面端运行的任务型 AI 助手,通过数字专家与技能市场组合办公、创作、数据和开发能力。

    Loomy macOS版
    Loomy macOS版

    Loomy 是科大讯飞推出的桌面 AI 助手,可通过对话、任务与工作目录协助处理办公、内容创作和资料整理。

    AirBuddy 3 macOS版
    AirBuddy 3 macOS版

    AirBuddy 3 是面向 macOS 的蓝牙外设增强工具,可集中查看 AirPods、Beats 与苹果设备电量,提供快速连接、播放控制、电量提醒、桌面小组件和多台 Mac 外设接力等功能。

    AirServer macOS版
    AirServer macOS版

    AirServer macOS版可把 Mac 转换为无线投屏接收端,通过 AirPlay 或 Google Cast 接收手机、平板和电脑画面,并支持多设备同时连接、高清镜像及会话录制,适合家庭娱乐、课堂教学、会议演示和应用展示。

    AnyDesk macOS版
    AnyDesk macOS版

    AnyDesk macOS版是一款适用于远程办公、设备维护和客户支持的远程桌面工具,可连接并操作异地计算机,支持文件传输、无人值守访问、会话权限控制及跨平台协作。

    Beamer 4 macOS版
    Beamer 4 macOS版

    Beamer 4是面向macOS的本地视频投屏工具,可将Mac中的影片投放到Apple TV、Chromecast及兼容设备,支持常见格式、多类字幕、环绕声、播放列表和电视遥控操作。

    WINDOWS 更多
    火山引擎 网页版
    火山引擎 网页版

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

    立刻MV 网页版
    立刻MV 网页版

    立刻MV 面向音乐人、内容创作者和品牌团队,帮助用户低成本完成歌曲 MV、歌词视频、AI 分镜、图生视频与最终合成。

    蛙蛙写作 网页版
    蛙蛙写作 网页版

    蛙蛙写作 AI 创作工具,覆盖小说、剧本、文案,一键生成文稿,打通文字转视频全流程,高效解决创作难题。

    讯飞智作 网页版
    讯飞智作 网页版

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

    超级简历WonderCV 网页版
    超级简历WonderCV 网页版

    超级简历WonderCV是面向应届生和AI小白的智能求职平台,覆盖简历、面试与职业规划。

    音述AI 网页版
    音述AI 网页版

    音述AI,是一个以“用声音述说故事”为核心的 AI 音乐创作与声音分享社区。

    AionClaw Windows版
    AionClaw Windows版

    AionClaw 是可在桌面端运行的任务型 AI 助手,通过数字专家与技能市场组合办公、创作、数据和开发能力。

    Loomy Windows版
    Loomy Windows版

    Loomy 是科大讯飞推出的桌面 AI 助手,可通过对话、任务与工作目录协助处理办公、内容创作和资料整理。

    讯飞绘文 网页版
    讯飞绘文 网页版

    讯飞绘文网页版是科大讯飞推出的AI图文创作与内容运营工具,覆盖热点选题、文章生成、稿件校对、智能配图、排版发布和数据复盘等环节,适合自媒体创作者及内容团队在线使用。

    AirServer Windows版
    AirServer Windows版

    AirServer Windows版可把电脑变成AirPlay、Google Cast和Miracast无线投屏接收端,适合会议演示、课堂教学及家庭大屏分享。发送设备通常无需另装客户端,并可同时接收和智能排列多个镜像会话。

    AlwaysUp Windows版
    AlwaysUp Windows版

    AlwaysUp是一款面向Windows的系统服务注册与程序托管工具,可让普通应用、脚本及批处理在无人登录时随系统启动,并通过状态检测、异常重启、资源监控和邮件通知保障关键任务持续运行。

    AnyDesk Windows版
    AnyDesk Windows版

    AnyDesk Windows版是一款轻量化远程桌面与技术支持工具,可用于控制异地电脑、传输文件、远程打印及无人值守访问。它兼顾连接响应、安全权限和部署灵活性,适合个人远程办公、企业设备维护与客服支持。