Python开发俄罗斯方块时方块顶部卡顿重复生成问题求助
俄罗斯方块碰撞检测问题:方块卡顶重复生成无法堆叠
我用Python结合Pygame开发俄罗斯方块,添加方块间碰撞检测后,出现方块卡在顶部并重复生成的问题(如图所示)。当前方块可触底、触边停止,但无法实现方块堆叠效果,调整边界参数要么问题依旧,要么方块失去碰撞检测直接落到底部。

原完整代码
import pygame import random # Define Colors BLACK = (0, 0, 0) WHITE = (255, 255, 255) GRAY = (128, 128, 128) RED = (255, 0, 0) BLUE = (0, 0, 255) GREEN = (0, 255, 0) YELLOW = (255, 255, 0) ORANGE = (255, 165, 0) CYAN = (0, 255, 255) # Define shapes SHAPES = [ [[0, 1, 1, 1], [0, 0, 1, 0]], [[0, 0, 2, 2], [0, 2, 2, 0]], [[0, 3, 3, 0], [0, 0, 3, 3]], [[0, 4, 0, 0], [0, 4, 4, 4]], [[0, 0, 5, 0], [0, 5, 5, 5]], [[0, 6, 6], [0, 6, 6]], [[0, 7, 0, 0], [0, 7, 7, 7]] ] # Define shape class class Shape: def __init__(self, shape): self.shape = shape self.color = random.choice([RED, BLUE, GREEN, YELLOW, ORANGE, CYAN]) self.x = (WINDOW_WIDTH // 2) // block_size - len(self.shape[0]) // 2 self.y = 1 def draw(self, screen, block_size): for i in range(len(self.shape)): for j in range(len(self.shape[0])): if self.shape[i][j] != 0: pygame.draw.rect(screen, self.color, (self.x * block_size + j * block_size, self.y * block_size + i * block_size, block_size, block_size)) pygame.draw.rect(screen, 'white', (self.x * block_size + j * block_size, self.y * block_size + i * block_size, block_size, block_size), 1) def move_down(self): self.y += 1 def move_up(self): self.y -= 1 def collides_with(self, other): shape_width = len(self.shape[0]) shape_height = len(self.shape) for i in range(shape_height): for j in range(shape_width): if self.shape[i][j] != 0: x_cor, y_cor = self.x + j, self.y + i if y_cor >= len(other.shape) or x_cor < 0 or x_cor >= len(other.shape[0]) \ or (other.shape[y_cor][x_cor] != 0): return True return False # Initialize Pygame pygame.init() # Set up window WINDOW_WIDTH = 220 WINDOW_HEIGHT = 500 screen = pygame.display.set_mode((WINDOW_WIDTH, WINDOW_HEIGHT)) pygame.display.set_caption('Tetris') # Set up clock clock = pygame.time.Clock() # Set up game variables block_size = 20 shapes = [] current_shape = Shape(random.choice(SHAPES)) # Game loop while True: # Handle events for event in pygame.event.get(): if event.type == pygame.QUIT: pygame.quit() quit() keys = pygame.key.get_pressed() if keys[pygame.K_LEFT] and current_shape.x > 0: current_shape.x -= 1 if keys[pygame.K_RIGHT] and current_shape.x + len(current_shape.shape[0]) < 200 / block_size: current_shape.x += 1 # Move current shape down current_shape.move_down() for shape in shapes: if current_shape.collides_with(shape): current_shape.move_up() if current_shape.y <= 0: pygame.quit() quit() else: shapes.append(current_shape) current_shape = Shape(random.choice(SHAPES)) break # Check if current shape has collided with another shape or reached the bottom if current_shape.y + len(current_shape.shape) >= 24: shapes.append(current_shape) current_shape = Shape(random.choice(SHAPES)) # Draw current shape and all other shapes on screen screen.fill('dark gray') for x in range(1, 10): for y in range(1, 24): # First creates square with gray color. Second draws black border for square pygame.draw.rect(screen, 'light gray', (10 * x * 2, 10 * y * 2, 20, 20), 0) pygame.draw.rect(screen, 'black', (10 * x * 2, 10 * y * 2, 20, 20), 1) for shape in shapes: shape.draw(screen, block_size) current_shape.draw(screen, block_size) pygame.display.update() # Tick clock clock.tick(10)
核心碰撞检测代码(原错误版本)
def collides_with(self, other): shape_width = len(self.shape[0]) shape_height = len(self.shape) for i in range(shape_height): for j in range(shape_width): if self.shape[i][j] != 0: x_cor, y_cor = self.x + j, self.y + i if y_cor >= len(other.shape) or x_cor < 0 or x_cor >= len(other.shape[0]) \ or (other.shape[y_cor][x_cor] != 0): return True return False for shape in shapes: if current_shape.collides_with(shape): current_shape.move_up() if current_shape.y <= 0: pygame.quit() quit() else: shapes.append(current_shape) current_shape = Shape(random.choice(SHAPES)) break
问题根源与修正方案
核心问题
- 碰撞检测逻辑错误:原方法拿当前形状和单个已落地形状的小矩阵对比,而非整个游戏场的全局网格,导致碰撞判断完全失效
- 落地判断流程混乱:先移动再检测碰撞,之后又单独判断触底,逻辑重复且冲突
- 新形状生成无校验:刚生成的形状未检查是否与已落地方块碰撞,直接触发卡顶重复生成
修正后完整代码
import pygame import random # Define Colors BLACK = (0, 0, 0) WHITE = (255, 255, 255) GRAY = (128, 128, 128) RED = (255, 0, 0) BLUE = (0, 0, 255) GREEN = (0, 255, 0) YELLOW = (255, 255, 0) ORANGE = (255, 165, 0) CYAN = (0, 255, 255) # Define shapes SHAPES = [ [[0, 1, 1, 1], [0, 0, 1, 0]], [[0, 0, 2, 2], [0, 2, 2, 0]], [[0, 3, 3, 0], [0, 0, 3, 3]], [[0, 4, 0, 0], [0, 4, 4, 4]], [[0, 0, 5, 0], [0, 5, 5, 5]], [[0, 6, 6], [0, 6, 6]], [[0, 7, 0, 0], [0, 7, 7, 7]] ] # Define shape class class Shape: def __init__(self, shape, grid_width): self.shape = shape self.color = random.choice([RED, BLUE, GREEN, YELLOW, ORANGE, CYAN]) self.x = (grid_width // 2) - len(self.shape[0]) // 2 self.y = 0 def draw(self, screen, block_size): for i in range(len(self.shape)): for j in range(len(self.shape[0])): if self.shape[i][j] != 0: pygame.draw.rect(screen, self.color, (self.x * block_size + j * block_size, self.y * block_size + i * block_size, block_size, block_size)) pygame.draw.rect(screen, 'white', (self.x * block_size + j * block_size, self.y * block_size + i * block_size, block_size, block_size), 1) def move_down(self): self.y += 1 def check_collision(self, game_grid, grid_width, grid_height): for i in range(len(self.shape)): for j in range(len(self.shape[0])): if self.shape[i][j] != 0: x = self.x + j y = self.y + i # 检查边界与已落地方块碰撞 if x < 0 or x >= grid_width or y >= grid_height or (y >= 0 and game_grid[y][x] != 0): return True return False # Initialize Pygame pygame.init() # Set up window WINDOW_WIDTH = 220 WINDOW_HEIGHT = 500 screen = pygame.display.set_mode((WINDOW_WIDTH, WINDOW_HEIGHT)) pygame.display.set_caption('Tetris') # Set up clock clock = pygame.time.Clock() # Set up game variables block_size = 20 grid_width = 10 grid_height = 24 # 全局游戏网格:0为空,非0为对应颜色的方块 game_grid = [[0 for _ in range(grid_width)] for _ in range(grid_height)] current_shape = Shape(random.choice(SHAPES), grid_width) # Game loop while True: # Handle events for event in pygame.event.get(): if event.type == pygame.QUIT: pygame.quit() quit() keys = pygame.key.get_pressed() # 左右移动:先移动再校验碰撞,碰撞则回退 if keys[pygame.K_LEFT]: current_shape.x -= 1 if current_shape.check_collision(game_grid, grid_width, grid_height): current_shape.x += 1 if keys[pygame.K_RIGHT]: current_shape.x += 1 if current_shape.check_collision(game_grid, grid_width, grid_height): current_shape.x -= 1 # 自动下落逻辑 current_shape.move_down() if current_shape.check_collision(game_grid, grid_width, grid_height): current_shape.move_up() # 将当前形状写入全局网格 for i in range(len(current_shape.shape)): for j in range(len(current_shape.shape[0])): if current_shape.shape[i][j] != 0: x = current_shape.x + j y = current_shape.y + i if y < 0: # 方块超出顶部,游戏结束 pygame.quit() quit() game_grid[y][x] = current_shape.color # 生成新形状并校验初始碰撞 current_shape = Shape(random.choice(SHAPES), grid_width) if current_shape.check_collision(game_grid, grid_width, grid_height): pygame.quit() quit() # 绘制游戏内容 screen.fill('dark gray') # 绘制网格背景 for x in range(grid_width): for y in range(grid_height): pygame.draw.rect(screen, 'light gray', (x * block_size, y * block_size, block_size, block_size), 0) pygame.draw.rect(screen, 'black', (x * block_size, y * block_size, block_size, block_size), 1) # 绘制已落地方块 for y in range(grid_height): for x in range(grid_width): if game_grid[y][x] != 0: pygame.draw.rect(screen, game_grid[y][x], (x * block_size, y * block_size, block_size, block_size)) pygame.draw.rect(screen, 'white', (x * block_size, y * block_size, block_size, block_size), 1) # 绘制当前形状 current_shape.draw(screen, block_size) pygame.display.update() clock.tick(10)
关键修正点
- 新增全局游戏网格:用二维数组记录每个位置的方块状态,碰撞检测基于全局网格更准确
- 重写碰撞检测方法:
check_collision统一处理边界碰撞和已落地方块的碰撞 - 优化下落流程:先移动再检测碰撞,碰撞则回退并将方块写入网格,再生成新形状
- 新形状初始校验:生成新形状后立即检查碰撞,避免刚生成就卡顶触发游戏结束
内容的提问来源于stack exchange,提问作者Theo 17
相关产品推荐
相关产品推荐

