pygame.Surface.set_at()在for循环中的工作逻辑与失效排查
问题根因
- 坐标值完全错位:存在
SelectRect实例里的x、y是屏幕放大绘制用的画布坐标,不是32x32导出Surface需要的像素索引坐标。
假设TILE_SIZE=16,创建网格时第1个格子传入的x是0*16=0,第2个是1*16=16,第32个是31*16=496。但export_surf尺寸只有32x32,合法坐标范围是0~31,把16、496这类超出范围的值传给set_at,方法会直接静默忽略,不会写入任何像素。之前尝试用半径1的圆绘制也没效果,本质是同一个原因,传入的坐标完全不在Surface的范围内。 - 边界越界的无效调用:初始化时写的
export_surf.set_at((32, 32), CYAN),32已经超出32x32 Surface的最大坐标(索引从0开始,最大合法值是31),这行代码完全不会生效。 - 导出逻辑不全:原代码只遍历
selected_rects写入像素,没有选中的格子会保留Surface默认的纯黑底色,容易误判为写入失败。
修复步骤
- 拆分存储两类坐标,把用于导出的网格索引和用于屏幕绘制的放大坐标分开存
修改SelectRect类的初始化方法,新增属性存0~31的网格索引:
修改网格创建逻辑,把循环索引传入作为网格坐标:class SelectRect: def __init__(self, xpos: int, ypos: int, w: int, h: int, rect_colour: tuple or list, grid_x: int, grid_y: int): self.rect = pygame.Rect(xpos, ypos, w, h) self.colour = rect_colour # 保留原属性用于屏幕绘制 self.x = xpos self.y = ypos # 新增网格索引,用于导出Surface self.grid_x = grid_x self.grid_y = grid_y def render(self, surf): pygame.draw.rect(surf, self.colour, self.rect)rects = [] for y_idx in range(32): for x_idx in range(32): draw_x = x_idx * TILE_SIZE draw_y = y_idx * TILE_SIZE rects.append(SelectRect( draw_x, draw_y, TILE_SIZE, TILE_SIZE, None, grid_x=x_idx, grid_y=y_idx )) - 修正导出逻辑的坐标和Surface初始化
删除原来越界的export_surf.set_at((32, 32), CYAN)代码,修改e键触发的导出逻辑:if event.key == pygame.K_e: # 如需透明背景,加SRCALPHA参数创建带alpha通道的Surface export_surf = pygame.Surface((32, 32), pygame.SRCALPHA) # 遍历所有网格矩形写入像素,不要只遍历selected_rects for rect in rects: if rect.colour is not None: export_surf.set_at((rect.grid_x, rect.grid_y), rect.colour) # 导出为文件的话加这行即可 pygame.image.save(export_surf, "pixel_art.png") - 验证显示效果
原代码直接把32x32的export_surfblit在屏幕(0,0)位置,因为尺寸太小很难观察,可以临时加缩放显示确认写入是否成功:scaled_export = pygame.transform.scale(export_surf, (128, 128)) screen.blit(scaled_export, (0, 0))
注意事项
set_at()的工作逻辑是:传入(x,y)像素坐标和颜色值,直接修改Surface内部对应位置的像素内存值,没有内置边界校验,超出范围的坐标不会抛出任何错误,会被直接跳过,使用时必须自行确认坐标合法。- Surface宽高为W、H时,合法x范围是
0~W-1,合法y范围是0~H-1。 - 不要把屏幕绘制用的放大坐标直接传给导出Surface的绘制方法,两类坐标必须完全分开存储。
内容的提问来源于stack exchange,提问作者ruChikati
相关产品推荐
相关产品推荐

