You need to enable JavaScript to run this app.
优惠活动
大模型
产品
解决方案
定价
更多

Pygame可滚动窗口中变色按钮功能冲突问题求解

问题解决方案

问题核心原因

  • 事件队列重复消费:Button类的scrool方法和主循环都调用了pygame.event.get(),pygame的事件队列每次调用get后就会清空,导致两边总有一侧无法获取到完整事件
  • 数值传递是值拷贝:初始化按钮时传入的2 + screen_level是int类型的瞬时值,后续修改全局的screen_level不会自动同步到按钮内部存储的坐标
  • 全局变量修改问题:scrool方法内直接修改screen_level没有声明global,会触发局部变量未定义报错

修复方案

第一步:重构Button类

移除Button内部的滚动事件处理逻辑,改为存储原始基准y坐标,每次绘制时动态计算偏移后的位置:

class Button:
    def __init__(self, text, color_idle, base_y, function=''):
        # General
        self.pressed = False
        self.function_finished = False
        self.function = function
        # 存储基准y坐标,不包含滚动偏移
        self.base_y = base_y
        self.color_idle = color_idle # 存储闲置色,避免重复传参

        # 文本仅渲染一次,内容不变的话无需每帧重复生成
        font1 = pygame.font.Font(None, 30)
        self.text_surface = font1.render(text, True, WHITE)

    def draw(self, screen_level):
        # 动态计算当前按钮的位置,叠加滚动偏移
        current_y = self.base_y + screen_level
        self.button1 = pygame.Rect(0, current_y, 280, 24)
        self.text_rect = self.text_surface.get_rect(center=self.button1.center)

        # 绘制按钮
        pygame.draw.rect(window, self.color, self.button1, border_radius=3)
        window.blit(self.text_surface, self.text_rect)
        self.click()

    def click(self):
        mouse_pos = pygame.mouse.get_pos()
        if self.button1.collidepoint(mouse_pos):
            if pygame.mouse.get_pressed()[0]:
                self.pressed = True
            if pygame.mouse.get_pressed()[2]:
                self.pressed = False
        
        # 状态颜色切换
        if self.pressed:
            self.color = LIGHT_BLUE_PRESSED
            # 执行绑定函数
            if self.function and not self.function_finished:
                if self.function == 'unlock_every_character':
                    unlock_every_character()
                elif self.function == 'unlock_every_item':
                    unlock_every_item()
                elif self.function == 'unlock_every_animal':
                    unlock_every_animal()
                self.function_finished = True
        else:
            self.color = self.color_idle
            if self.button1.collidepoint(mouse_pos):
                self.color = LIGHT_BLUE_HOVER

第二步:调整主循环逻辑

按钮初始化放在主循环外,滚动事件统一在主循环处理:

# 初始化全局变量和按钮,仅执行一次
screen_level = 0
button1 = Button('Text1', LIGHT_BLUE_IDLE, 2, 'unlock_every_character')
# 其他按钮同理在此处初始化

# 主循环
while True:
    # 统一处理所有事件,避免重复消费
    for event in pygame.event.get():
        # 处理窗口关闭事件(建议补充避免程序卡死)
        if event.type == pygame.QUIT:
            pygame.quit()
            exit()
        # 处理滚动事件
        if event.type == pygame.MOUSEBUTTONDOWN:
            if event.button == 4:  # SCROLL UP
                screen_level += 10
            if event.button == 5:  # SCROLL DOWN
                screen_level -= 10
    
    # 绘制逻辑,传入当前的screen_level
    button1.draw(screen_level)
    # 其他UI绘制、画面更新逻辑
    pygame.display.update()

额外优化点

  • 文本渲染放到__init__里只执行一次,避免每帧重复渲染浪费性能
  • 把事件处理统一收拢到主循环,避免多个组件消费事件导致的冲突
  • 按钮绑定函数判断改成elif,减少不必要的分支判断

内容的提问来源于stack exchange,提问作者Eskimo868

相关产品推荐
方舟 Agent Plan

超全模态模型 × Harness 升级,最新支持 Deepseek-V4.1-Flash、GLM-5.3 系列、Doubao-Seedream-5.0-pro、Kimi-K3 (部分), 限时 9.9 元起

最近更新时间:2026.10.06 08:39:05