You need to enable JavaScript to run this app.
优惠活动
大模型
产品
解决方案
定价
更多

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()

问题根源

  1. 错误的删除方式:原代码使用Enemyimg.remove(Enemyimg[i]),该方法会删除列表中第一个匹配的元素,而非指定索引的元素。当多个敌人使用同一张图片时,会误删第一个出现的敌人,而非被击中的那个。
  2. 未同步删除关联数据:敌人的坐标存储在enemyx、enemyy列表中,仅删除Enemyimg中的元素会导致数据不一致,后续遍历出现索引错位。
  3. 遍历中修改列表长度:在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

相关产品推荐
方舟 Agent Plan

超全模态模型 × Harness 升级,最新支持 Deepseek-V4.1-Flash、GLM-5.3 系列、Doubao-Seedream-5.0-pro、Kimi-K3 (部分), 限时 9.9 元起

最近更新时间:2026.08.17 12:10:38