Pygame开发Flappy Bird:Colliderect碰撞检测误触发问题求助
Flappy Bird Pygame 碰撞检测异常问题解决
问题根源
你的碰撞检测异常是因为pipes列表没有被正确维护:每次调用Pipe.draw()时,都会往列表里追加新的管道Rect,但从未清空旧的。随着游戏运行,列表里会堆积大量已经移出屏幕的历史管道Rect,小鸟会和这些不存在的“幽灵管道”发生错误碰撞。
另外,代码里直接硬编码操作pipe和pipe2对象,后续扩展多管道时会更难维护。
修复方案
- 每帧清空管道Rect列表:在绘制管道前清空
pipes,只保留当前帧的有效管道Rect。 - 批量控制管道滚动:不要单独操作
pipe和pipe2,改用列表管理所有管道实例,统一设置滚动速度。 - 优化管道生成逻辑:确保管道重置时的Y轴范围合理,避免管道位置异常。
修改后的完整代码
import pygame, sys from pygame.locals import QUIT import random WHITE = (255, 255, 255) YELLOW = (255, 255, 0) GREY = (100, 100, 100) CLOCK = pygame.time.Clock() class Bird: def __init__(self): self.__x = 20 self.__y = screen_y / 2 self.__vel = 16 self.death = False self.rect = None # 提前初始化rect def flap(self): if not self.death: self.__vel = 16 def gravity(self): if not self.death: self.__y -= self.__vel self.__vel -= 2 if self.__vel > 15: self.__vel = 15 # 限制小鸟不超出屏幕底部,碰到直接死亡 if self.__y > (screen_y - 50): self.__y = screen_y - 50 self.death = True def draw(self): self.rect = pygame.draw.rect(DISPLAYWINDOW, YELLOW, (self.__x, self.__y, 50, 50)) def collide(self, pipes_rects, pipe_objects): for pipe_rect in pipes_rects: if self.rect.colliderect(pipe_rect): # 停止所有管道滚动 for pipe in pipe_objects: pipe.set_scroll(0) print("damn you collision") self.death = True class Pipe: def __init__(self, x): self.__x = x self.__y = random.randint(275, 450) # 初始化就随机Y轴位置 self.__w = 70 self.__h = 700 self.__scroll = 7.5 # world scroll def get_w(self): return self.__w def set_scroll(self, x): self.__scroll = x def move(self): if self.__scroll > 0: self.__x -= self.__scroll def draw(self): # 返回当前管道的Rect,而非直接追加到全局列表 bottom_rect = pygame.draw.rect(DISPLAYWINDOW, WHITE, (self.__x, self.__y, self.__w, self.__h)) # 如果需要顶部管道,取消注释下面一行 # top_rect = pygame.draw.rect(DISPLAYWINDOW, WHITE, (self.__x, (self.__y - self.__h) - 250, self.__w, self.__h)) # return [bottom_rect, top_rect] return [bottom_rect] def generation(self): if self.__x < 0 - self.__w: self.__y = random.randint(275, 450) self.__x = screen_x + self.__w pygame.init() screen_x = 500 screen_y = 700 DISPLAYWINDOW = pygame.display.set_mode((screen_x, screen_y)) # 用列表管理所有管道实例,替代硬编码的pipe和pipe2 pipe_objects = [] pipe_objects.append(Pipe(screen_x)) pipe_objects.append(Pipe((screen_x * 1.5) + pipe_objects[0].get_w())) bird = Bird() while True: for event in pygame.event.get(): if event.type == QUIT: pygame.quit() sys.exit() if event.type == pygame.KEYDOWN and event.key == pygame.K_SPACE: bird.flap() DISPLAYWINDOW.fill(GREY) bird.draw() bird.gravity() # 每帧清空管道Rect列表,重新收集当前帧的有效管道Rect pipes_rects = [] for pipe in pipe_objects: pipes_rects.extend(pipe.draw()) pipe.move() pipe.generation() # 传入管道Rect列表和实例列表进行碰撞检测 bird.collide(pipes_rects, pipe_objects) pygame.display.update() CLOCK.tick(30)
关键修改点
- 新增
pipe_objects列表管理所有管道实例,避免硬编码操作单个管道 pipes_rects每帧清空后重新收集当前管道的Rect,彻底解决历史Rect堆积问题- 碰撞检测时批量控制所有管道停止滚动,扩展性更强
- 增加小鸟碰地面直接死亡的逻辑,符合游戏规则
- 管道初始化时直接随机Y轴位置,逻辑更简洁
内容的提问来源于stack exchange,提问作者Apple_Retainer
相关产品推荐
相关产品推荐

