Pygame开发俄罗斯方块:锁定方块时单帧闪烁问题求助
Pygame俄罗斯方块锁定时单帧闪烁问题修复
问题现象
开发Pygame俄罗斯方块克隆版时,方块锁定瞬间会出现单帧黑色闪烁——这一帧里,待锁定的方块精灵已消失,但网格精灵还没把该方块绘制出来,导致对应位置空白。

当前实现逻辑
- Piece类负责生成自身图像与矩形,参与精灵组绘制
- Grid类用Numpy数组存储所有已锁定的小方块(mino),并根据数组内容生成自身图像
- 方块锁定触发流程:Tetris类检测到Piece锁定后,调用Grid更新视觉,随后销毁Piece
核心代码片段
Piece类锁定逻辑
if self.lock_delay <= 0: self.lock() self.kill()
Tetris类锁定处理逻辑
def update_game_state(self): if self.current_piece.locked: self.score += self.grid.clear_lines() self.grid.update_visual() self.spawn_piece()
Grid类视觉更新方法
def update_visual(self): #vars for where to draw block x_draw = 0 y_draw = 0 #copy based on cached images for row in self.grid: for val in row: self.update_surface.blit(self.block_cache[val], (x_draw, y_draw)) x_draw += self.block_width y_draw += self.block_width x_draw = 0 self.image, self.rect = self.update_surface, self.update_surface.get_rect() self.rect.bottomleft = (self.x,self.y)
游戏循环核心流程
#update gamestate tetris_game.update_game_state() #update sprites allsprites.update() #draw screen.blit(background, (0, 0)) allsprites.draw(screen) pygame.display.flip()
问题根源
闪烁的本质是帧更新顺序导致的绘制真空:
- Piece调用
kill()后会立即从精灵组移除,当前帧绘制时不会再渲染它 - Grid的
update_visual()是在Piece销毁后才执行,新的网格图像要到下一帧才会生效 - 这就导致当前帧中,待锁定方块的位置既没有Piece绘制,也没有Grid绘制,出现空白闪烁
修复方案
方案1:调整执行顺序(推荐)
把Piece的销毁操作移到Grid视觉更新之后,确保Grid已经包含了该方块的视觉内容,再移除Piece:
- 修改Piece类的锁定逻辑,只标记锁定状态,不立即销毁:
if self.lock_delay <= 0: self.lock() # 移除self.kill(),交给Tetris统一处理
- 修改Tetris类的
update_game_state方法,先更新Grid视觉,再销毁旧Piece:
def update_game_state(self): if self.current_piece.locked: self.score += self.grid.clear_lines() self.grid.update_visual() # 先更新Grid,再销毁方块 self.current_piece.kill() self.spawn_piece()
方案2:延迟销毁Piece
让Piece在锁定后多存活一帧,等Grid完成视觉更新后再销毁:
- 在Piece类中添加标记并修改锁定逻辑:
# Piece类初始化时添加 self.pending_kill = False # 锁定逻辑修改 if self.lock_delay <= 0: self.lock() self.pending_kill = True # 在Piece的update方法中添加销毁判断 def update(self): # 保留原有update逻辑... if self.pending_kill: self.kill()
这样Piece会在锁定后的下一帧才被销毁,当前帧绘制时Piece依然存在,Grid的新图像也会在下一帧生效,不会出现空白。
可选优化:减少Grid重绘范围
当前update_visual()每次全量重绘整个网格,效率较低。可以只重绘锁定方块所在的行,减少绘制开销:
修改Grid类的update_visual方法:
def update_visual(self, updated_rows=None): # 只重绘指定行,未指定则全量重绘 if updated_rows is not None: for row_idx in updated_rows: x_draw = 0 y_draw = row_idx * self.block_width for val in self.grid[row_idx]: self.update_surface.blit(self.block_cache[val], (x_draw, y_draw)) x_draw += self.block_width else: x_draw = 0 y_draw = 0 for row in self.grid: for val in row: self.update_surface.blit(self.block_cache[val], (x_draw, y_draw)) x_draw += self.block_width y_draw += self.block_width x_draw = 0 self.image, self.rect = self.update_surface, self.update_surface.get_rect() self.rect.bottomleft = (self.x,self.y)
调用时传入锁定方块所在的行(需要Piece实现获取自身行的方法):
# Tetris类中 locked_rows = self.current_piece.get_locked_rows() self.grid.update_visual(locked_rows)
内容的提问来源于stack exchange,提问作者cadcrafter
相关产品推荐
相关产品推荐

