Pygame Sprite问题:关卡4的Box移除列表后仍显示在屏幕
Pygame关卡切换后Box Sprite未消失问题修复
问题描述
我正在开发一款带关卡(lvl)的2D游戏,设定为lvl4时显示一个Box Sprite,通关lvl4后该Box需从boxes列表移除并消失。目前Box已从列表中移除,但仍显示在屏幕上,切换到lvl5时boxes列表为空,但Box Sprite未消失。
相关代码
import pygame pygame.init() clock = pygame.time.Clock() win_res = [800, 600] window = pygame.display.set_mode(win_res) pygame.display.set_caption("Mini Golf") bg_color = (120, 200, 255) bg_image = pygame.image.load("data/images/background.png") ground = pygame.image.load("data/images/ground.png").convert_alpha() ground.set_colorkey((255, 255, 255)) class Sling_Shot(pygame.sprite.Sprite): def __init__(self, x, y, vel): super().__init__() self.image = pygame.image.load("data/images/sling_shot.png").convert_alpha() self.image.set_colorkey((255, 255, 255)) self.rect = self.image.get_rect() self.rect.x = x self.rect.y = y self.vel = vel class Stone(pygame.sprite.Sprite): def __init__(self, x, y, vel): super().__init__() self.image = pygame.image.load("data/images/stone.png") self.image.set_colorkey((255, 255, 255)) self.rect = self.image.get_rect() self.rect.x = x self.rect.y = y self.vel = vel self.fired = False def update(self): if self.fired: self.rect.y -= self.vel class Bird(pygame.sprite.Sprite): def __init__(self, x, y, vel, moving, direction): super().__init__() self.image = pygame.image.load("data/images/bird.png").convert_alpha() self.image.set_colorkey((255, 255, 255)) self.rect = self.image.get_rect() self.rect.x = x self.rect.y = y self.vel = vel self.moving = moving self.dir = direction def update(self): if self.moving: self.rect.x += self.vel * self.dir if self.rect.x <= 100 or self.rect.x + self.rect.width >= 700: self.vel = -self.vel def lvl_selector(lvl): birds = [] match lvl: case 1: bird = Bird(365, 85, 2, False, 1) birds.append(bird) case 2: bird = Bird(365, 85, 2, True, 1) birds.append(bird) bird2 = Bird(365, 85, 2, True, 1) birds.append(bird2) case 3: bird = Bird(365, 85, 2, True, 1) bird2 = Bird(365, 205, 2, True, -1) birds.append(bird) birds.append(bird2) case 4: bird = Bird(365, 85, 2, True, 1) birds.append(bird) case 5: bird = Bird(365, 85, 2, True, -1) birds.append(bird) return birds class Box(pygame.sprite.Sprite): def __init__(self, x, y): super().__init__() self.image = pygame.image.load("data/images/box.png").convert_alpha() self.image.set_colorkey((255, 255, 255)) self.rect = self.image.get_rect() self.rect.x = x self.rect.y = y def box_selector(lvl): boxes = [] match lvl: case 4: box = Box(590, 119) boxes.append(box) case _: boxes = [] return boxes def game_loop(): sling_shot = Sling_Shot(375, 500, 5) stone = Stone(387.5, 485, 5) lvl = 3 boxes = [] all_sprites = pygame.sprite.Group() all_sprites.add(sling_shot, stone) birds = lvl_selector(lvl) for bird in birds: all_sprites.add(bird) running = True while running: for event in pygame.event.get(): if event.type == pygame.QUIT: running = False if event.type == pygame.MOUSEBUTTONUP: stone.fired = True x = pygame.mouse.get_pos()[0] # Get only the x-coordinate of the mouse if 120 <= x <= 670: sling_shot.rect.x = x - 25 if not stone.fired: stone.rect.x = sling_shot.rect.x + 17.5 if pygame.sprite.collide_rect(stone, sling_shot) and stone.vel < 0: stone.vel = -stone.vel # Stop the stone when it hits the slingshot stone.rect.y = sling_shot.rect.y - 15 stone.fired = False # Reset the stone's position if needed if not stone.fired: stone.rect.x = sling_shot.rect.x + 17.5 for bird in birds.copy(): if pygame.sprite.collide_rect(stone, bird): bird.kill() birds.remove(bird) if not birds: stone.rect.y = sling_shot.rect.y-12.5 stone.rect.x = sling_shot.rect.x stone.fired = False lvl += 1 birds = lvl_selector(lvl) boxes = box_selector(lvl) print(len(boxes)) for bird in birds: all_sprites.add(bird) for box in boxes: if pygame.sprite.collide_rect(stone, box): stone.vel = -stone.vel if lvl == 4: for box in boxes: all_sprites.add(box) for bird in birds: if bird.rect.x + bird.rect.width >= 580 and bird.rect.y == 85: bird.vel = -bird.vel if stone.rect.y <= 0 or stone.rect.y + stone.rect.width >= win_res[1]: stone.rect.y = sling_shot.rect.y - 12.5 stone.rect.x = sling_shot.rect.x stone.fired = False # Clear the old birds for bird in birds: bird.kill() birds.clear() # Add new birds for the current level birds.extend(lvl_selector(lvl)) boxes = box_selector(lvl) window.blit(bg_image, (0, 0)) window.blit(ground, (0, 40)) all_sprites.draw(window) all_sprites.update() pygame.display.update() clock.tick(60) pygame.quit() game_loop()
问题原因
核心问题是:切换关卡时,仅清空了boxes列表,但之前添加到all_sprites组中的Box Sprite实例并未被移除。all_sprites.draw()会绘制组内所有存在的Sprite,所以即使列表为空,组里的Box还在,就会继续显示。
修复方案
1. 通关升级关卡时清理旧Box
找到通关后升级关卡的代码块,添加移除旧Box的逻辑:
if not birds: stone.rect.y = sling_shot.rect.y-12.5 stone.rect.x = sling_shot.rect.x stone.fired = False # 移除所有旧的Box Sprite for box in boxes: box.kill() lvl += 1 birds = lvl_selector(lvl) boxes = box_selector(lvl)
2. 石头飞出边界重置关卡时清理旧Box
在石头飞出边界重置关卡的代码中,同样添加Box清理逻辑:
# Clear the old birds for bird in birds: bird.kill() birds.clear() # 清理旧的Box Sprite for box in boxes: box.kill() # Add new birds for the current level birds.extend(lvl_selector(lvl)) boxes = box_selector(lvl)
3. 优化Box添加逻辑(可选)
当前代码在lvl == 4的判断里每次循环都执行all_sprites.add(box),会重复添加同一个Box到组中。可以改为在获取新关卡的boxes后统一处理:
# 替换原来lvl==4的判断代码 # 先移除所有旧的Box for sprite in all_sprites.sprites(): if isinstance(sprite, Box): sprite.kill() # 添加当前关卡的Box for box in boxes: all_sprites.add(box) # lvl4的bird逻辑单独保留 if lvl == 4: for bird in birds: if bird.rect.x + bird.rect.width >= 580 and bird.rect.y == 85: bird.vel = -bird.vel
修复原理
Pygame的Sprite.kill()方法会将Sprite从所有它所属的Group中移除,这样all_sprites.draw()就不会再绘制这个Box,从而实现切换关卡后Box消失的效果。
内容的提问来源于stack exchange,提问作者Ibtasaam Mughal
相关产品推荐
相关产品推荐

