Pygame瓦片颜色切换异常:get_at方法返回值不符问题求助
问题排查与优化方案
问题描述
开发答题生成二维码的小游戏,需要实现瓦片黑白颜色切换(支持多次修改答案)。当前尝试用get_at()方法获取瓦片颜色状态,但点击瓦片变黑后,get_at()仍返回(255,255,255,255),需排查原因并提供更优实现方案。
原代码:
back = pygame.image.load("qrcode3outerM.jpg").convert() def draw_scene(screen): screen.fill(WHITE) #draw the outer from a picture because I am too lazy to generate it screen.blit(back,(0,0)) for row in range(size): for col in range(size): rect = pygame.Rect((MARGIN + row)*BOX_SIZE,(MARGIN+col)*BOX_SIZE, BOX_SIZE, BOX_SIZE) pygame.draw.rect(screen, BLACK, rect, 1) # if answ[row][col]: # pygame.draw.rect(screen, BLACK, ((MARGIN + col) * BOX_SIZE, (MARGIN + row)*BOX_SIZE, BOX_SIZE, BOX_SIZE)) if __name__ == '__main__': done = False draw_scene(screen) while not done: state = 0 for event in pygame.event.get(): if event.type == pygame.QUIT: done = True #get the location of the cell in terms of tiles where the mouse was clicked elif event.type == pygame.MOUSEBUTTONDOWN: pos = pygame.mouse.get_pos() x = pos[0] // BOX_SIZE - 12 #magic numbers here to make it work for the time being y = pos[1] // BOX_SIZE - 12 state = screen.get_at((x,y)) print(f'Current state of the pixel {state}') print(f'coordinate of the mouse press: {x} {y} ') print(answ[x][y]) print('black' if answ[x][y] else 'white') user_answ[x][y] = state pygame.draw.rect(screen, BLACK if state else WHITE, ((MARGIN + x) * BOX_SIZE, (MARGIN + y) * BOX_SIZE, BOX_SIZE, BOX_SIZE)) pygame.display.update() pygame.quit()
问题原因
- 坐标混淆:你用
x = pos[0] // BOX_SIZE -12算出的是瓦片的行列索引,但get_at()需要的是屏幕上的像素坐标。直接把行列索引传给get_at(),读取的是错误位置的像素,自然拿不到瓦片的真实颜色。 get_at()的局限性:就算坐标正确,get_at()是逐个像素读取,频繁调用会拖慢性能;而且屏幕上的瓦片可能被背景图、边框覆盖,读取的像素不一定是瓦片的填充色。- 状态与视图脱节:试图从屏幕像素反推瓦片状态,本质是把视图当数据源,一旦屏幕内容被其他元素覆盖,状态判断就会出错。
优化方案
核心思路:用数据结构维护瓦片状态,视图完全根据状态渲染,保证状态和视图同步,同时避免性能问题。
1. 初始化状态数组
先定义二维列表维护正确答案和用户答案的状态(True表示黑色,False表示白色):
# 假设二维码边长为21,可根据实际调整 size = 21 # 正确答案,这里可以根据实际二维码逻辑初始化 answ = [[False for _ in range(size)] for _ in range(size)] # 用户答案,初始全白,或与正确答案一致 user_answ = [[False for _ in range(size)] for _ in range(size)]
2. 修正点击坐标计算
去掉魔法数字,用MARGIN和BOX_SIZE计算点击的瓦片索引,同时判断是否点击在有效区域内:
pos = pygame.mouse.get_pos() # 计算点击位置相对于二维码区域的坐标 rel_x = pos[0] - MARGIN * BOX_SIZE rel_y = pos[1] - MARGIN * BOX_SIZE # 判断是否点击在二维码瓦片范围内 if 0 <= rel_x < size * BOX_SIZE and 0 <= rel_y < size * BOX_SIZE: x = rel_x // BOX_SIZE # 瓦片的列索引 y = rel_y // BOX_SIZE # 瓦片的行索引
3. 切换状态并渲染
直接操作user_answ数组切换状态,然后根据状态渲染瓦片:
# 切换当前瓦片状态:黑变白,白变黑 user_answ[x][y] = not user_answ[x][y] # 局部刷新该瓦片(比刷新整个场景更高效) tile_x = (MARGIN + x) * BOX_SIZE tile_y = (MARGIN + y) * BOX_SIZE # 先画边框覆盖旧的填充色 pygame.draw.rect(screen, BLACK, (tile_x, tile_y, BOX_SIZE, BOX_SIZE), 1) # 根据新状态画填充色 if user_answ[x][y]: pygame.draw.rect(screen, BLACK, (tile_x, tile_y, BOX_SIZE, BOX_SIZE)) # 只更新这个瓦片区域 pygame.display.update(pygame.Rect(tile_x, tile_y, BOX_SIZE, BOX_SIZE))
4. 完整优化后代码
import pygame # 初始化pygame pygame.init() # 配置参数 size = 21 # 二维码瓦片边长 BOX_SIZE = 20 # 单个瓦片大小 MARGIN = 12 # 二维码与屏幕边缘的间距(替换原魔法数字) WHITE = (255, 255, 255) BLACK = (0, 0, 0) screen = pygame.display.set_mode(((MARGIN + size)*BOX_SIZE, (MARGIN + size)*BOX_SIZE)) pygame.display.set_caption("二维码答题游戏") back = pygame.image.load("qrcode3outerM.jpg").convert() # 初始化状态数组 answ = [[False for _ in range(size)] for _ in range(size)] user_answ = [[False for _ in range(size)] for _ in range(size)] def draw_scene(screen): screen.fill(WHITE) screen.blit(back, (0, 0)) # 绘制所有瓦片的边框和填充色 for row in range(size): for col in range(size): tile_x = (MARGIN + col) * BOX_SIZE tile_y = (MARGIN + row) * BOX_SIZE # 绘制边框 pygame.draw.rect(screen, BLACK, (tile_x, tile_y, BOX_SIZE, BOX_SIZE), 1) # 根据用户答案绘制填充色 if user_answ[col][row]: pygame.draw.rect(screen, BLACK, (tile_x, tile_y, BOX_SIZE, BOX_SIZE)) if __name__ == '__main__': done = False draw_scene(screen) pygame.display.update() while not done: for event in pygame.event.get(): if event.type == pygame.QUIT: done = True elif event.type == pygame.MOUSEBUTTONDOWN: pos = pygame.mouse.get_pos() rel_x = pos[0] - MARGIN * BOX_SIZE rel_y = pos[1] - MARGIN * BOX_SIZE if 0 <= rel_x < size * BOX_SIZE and 0 <= rel_y < size * BOX_SIZE: x = rel_x // BOX_SIZE y = rel_y // BOX_SIZE # 切换状态 user_answ[x][y] = not user_answ[x][y] # 局部刷新瓦片 tile_x = (MARGIN + x) * BOX_SIZE tile_y = (MARGIN + y) * BOX_SIZE pygame.draw.rect(screen, BLACK, (tile_x, tile_y, BOX_SIZE, BOX_SIZE), 1) if user_answ[x][y]: pygame.draw.rect(screen, BLACK, (tile_x, tile_y, BOX_SIZE, BOX_SIZE)) pygame.display.update(pygame.Rect(tile_x, tile_y, BOX_SIZE, BOX_SIZE)) # 可选:如果有其他需要实时刷新的元素,放在这里 pygame.quit()
关键优势
- 状态与视图同步:用数组维护状态,不会出现屏幕读取错误的问题。
- 性能更优:局部刷新减少不必要的绘制,比全量刷新+
get_at()高效得多。 - 逻辑清晰:状态修改和视图渲染分离,后续扩展(比如答案校验)更方便。
内容的提问来源于stack exchange,提问作者Hero Yu
相关产品推荐
相关产品推荐

