Pygame太空侵略者游戏:解决子弹击中错误敌人的问题
Pygame太空侵略者:精准删除被击中敌人的解决方案
问题描述
在基于Pygame开发的太空侵略者游戏中,所有敌人存储在列表中。当玩家发射的子弹与敌人距离足够近时,希望让被击中的特定敌人消失,但实际效果总是删除列表末尾(x值最大)的敌人,需要实现精准删除。
参考代码
import pygame import random import math # 初始化pygame pygame.init() # 创建游戏窗口 game_window = pygame.display.set_mode((800, 570)) # 窗口标题和图标 pygame.display.set_caption("Power of the Doctor") icon = pygame.image.load('tardisShooter.png') pygame.display.set_icon(icon) # 背景图片 background = pygame.image.load('SpaceTardissmall.png') # 玩家信息 Playerimg = pygame.image.load('tardisShooter.png') playerx = 370 # 玩家x轴坐标 playery = 470 # 玩家y轴坐标 playerx_change = 0 # 玩家x轴移动增量 def player(x, y): # blit方法在屏幕绘制图片 game_window.blit(Playerimg, (x, y)) # 敌人信息 Enemyimg = [] enemyx = [] enemyy = [] enemyx_change = 1 enemyy_change = 0 num_enemies = 2 enemyspawnx = 50 enemyspawny = 200 for i in range(num_enemies): Enemyimg.append(pygame.image.load('CybermanShooter.png')) enemyx.append(enemyspawnx) # 敌人x轴初始坐标 enemyy.append(enemyspawny) # 敌人y轴初始坐标 enemyspawnx += 70 def enemy(x, y, i): game_window.blit(Enemyimg[i], (x, y)) # 子弹信息 bulletimg = pygame.image.load('torpedo32.png') bullety = playery bulletx = playerx bulletstate = "idle" bullety_change = 5 def bullet(x, y): global bulletstate bulletstate = "FIRE" game_window.blit(bulletimg, (x + 15.5, y - 25)) def collision(enemyx, enemyy, bulletx, bullety): distance = math.sqrt((math.pow(bulletx - enemyx, 2)) + (math.pow(bullety - enemyy, 2))) if distance < 27: return True else: return False rand = random.randint(0, num_enemies) # 计时器事件 bulletevent = pygame.USEREVENT pygame.time.set_timer(bulletevent, 2000) # 敌人炸弹信息 bombimg = [] bombx = [] bomby = [] bomby_change = 1 bombstate = "idle" num_bombs = 2 for i in range(num_bombs): bombimg.append(pygame.image.load('bomb.png')) bombx.append(enemyx[i]) bomby.append(enemyy[i]) def bomb(x, y, i): global bombstate bombstate = "fire" game_window.blit(bombimg[i], (x + 15, y + 65)) # 玩家被击中检测 def enemyhit(bombx, bomby, x, y): distance2 = math.sqrt((math.pow(bombx - x, 2)) + (math.pow(bomby - (y - 50), 2))) if distance2 < 20: return True else: return False # 游戏主循环 Running = True while Running: # 填充背景色 game_window.fill((0, 0, 0)) # 绘制背景图片 game_window.blit(background, (0, 0)) # 事件处理 for event in pygame.event.get(): # 关闭窗口事件 if event.type == pygame.QUIT: Running = False if event.type == bulletevent: if bombstate == 'idle': bomb(bombx[rand], bomby[rand], i) bombx[rand] = enemyx[rand] bomby[rand] = enemyy[rand] # 键盘按下事件 if event.type == pygame.KEYDOWN: # ESC键退出游戏 if event.key == pygame.K_ESCAPE: Running = False # 右键移动玩家 if event.key == pygame.K_RIGHT: playerx_change = 5 # 左键移动玩家 if event.key == pygame.K_LEFT: playerx_change = -5 # 空格键发射子弹 if event.key == pygame.K_SPACE: bulletx = playerx if bulletstate == "idle": bullet(bulletx, bullety) # 键盘松开事件 if event.type == pygame.KEYUP: if event.key == pygame.K_RIGHT or event.key == pygame.K_LEFT: playerx_change = 0 # 更新玩家位置 playerx += playerx_change # 玩家边界限制 if playerx >= 740: playerx = 740 if playerx <= 0: playerx = 0 # 绘制玩家 player(playerx, playery) # 遍历并更新所有敌人 for i in range(num_enemies): # 绘制敌人 enemy(enemyx[i], enemyy[i], i) enemyx[i] += enemyx_change # 敌人左右边界碰撞处理 if enemyx[i] >= 735: enemyx_change = -.8 enemyy[i] += 10 if enemyx[i] <= 0: enemyx_change = .8 enemyy[i] += 10 # 处理子弹发射状态 if bulletstate == "FIRE": bullet(bulletx, bullety) bullety -= bullety_change # 子弹向上移动 # 子弹超出屏幕后重置状态 if bullety <= 0: bulletstate = "idle" bullety = playery # 碰撞检测与敌人删除(原问题代码) for i in range(num_enemies): collided = collision(enemyx[i], enemyy[i], bulletx, bullety) if collided == True: bulletstate = 'idle' bullety = playery Enemyimg.remove(Enemyimg[i]) num_enemies -= 1 # 处理敌人炸弹发射 if bombstate == 'fire': bomb(bombx[rand], bomby[rand], i) bomby[rand] += bomby_change if bomby[rand] > 570: bombstate = 'idle' bomby[rand] = enemyy[rand] rand = random.randint(0, 1) # 玩家被炸弹击中处理 enemyhits = enemyhit(bombx[rand], bomby[rand], playerx, playery) if enemyhits == True: playerx = 370 bombstate = 'idle' bomby[rand] = enemyy[rand] # 更新屏幕 pygame.display.update()
问题根源
- 错误的删除方式:原代码使用
Enemyimg.remove(Enemyimg[i]),该方法会删除列表中第一个匹配的元素,而非指定索引的元素。当多个敌人使用同一张图片时,会误删第一个出现的敌人,而非被击中的那个。 - 未同步删除关联数据:敌人的坐标存储在
enemyx、enemyy列表中,仅删除Enemyimg中的元素会导致数据不一致,后续遍历出现索引错位。 - 遍历中修改列表长度:在
for i in range(num_enemies)循环内直接减少num_enemies,会导致循环提前终止或索引越界。
解决方案
修改碰撞检测与删除逻辑
替换原代码中碰撞检测的循环部分,改为先收集被击中的敌人索引,再倒序删除所有关联数据:
# 碰撞检测与敌人删除(修正后) hit_indices = [] for i in range(num_enemies): collided = collision(enemyx[i], enemyy[i], bulletx, bullety) if collided: hit_indices.append(i) bulletstate = 'idle' bullety = playery # 倒序删除,避免索引错位 for i in reversed(hit_indices): del Enemyimg[i] del enemyx[i] del enemyy[i] num_enemies -= 1
关键说明
- 收集索引再删除:先遍历所有敌人,记录被击中的索引,避免在遍历过程中修改列表结构。
- 倒序删除:倒序处理索引可以避免删除前面元素后,后续元素索引前移导致的错位问题。
- 同步删除所有关联数据:同时删除
Enemyimg、enemyx、enemyy中对应索引的元素,保证数据一致性。
内容的提问来源于stack exchange,提问作者ColdBeefStew
相关产品推荐
相关产品推荐

