Pygame中新生成僵尸Sprite无法被击杀的技术问题
僵尸击杀问题的修复方案
嘿,我看到你遇到了僵尸击杀的棘手问题——只能干掉第一只生成的僵尸,后面出来的都能免疫子弹是吧?这问题我之前做射击游戏时也碰到过,核心是碰撞检测的逻辑位置和目标对象都错了,让我给你拆解清楚,再把代码修好:
问题根源分析
- 只检测单个僵尸实例:你在鼠标点击事件里写的碰撞检测只针对
zombie_sprite这个变量,但每次生成新僵尸时,这个变量会被覆盖成新的僵尸实例,之前的僵尸就再也不会被检测到碰撞了。 - 碰撞检测时机错误:你把碰撞逻辑放在了鼠标点击事件里,这意味着只有点击鼠标的那一瞬间才会检查碰撞,但子弹是一直在飞行的,应该在每一帧的主循环中持续检测所有子弹和僵尸的碰撞。
修复后的完整代码
我已经把关键修改点用注释标出来了,直接替换你的代码就能用:
import random import pygame from sys import exit from random import randint import math from pygame.math import Vector2 from pygame.constants import K_LSHIFT, K_SPACE, MOUSEBUTTONDOWN, MOUSEBUTTONUP, K_e pygame.init() import time pygame.mouse.set_visible(False) class Player(pygame.sprite.Sprite): def __init__(self, x , y): super().__init__() self.x = x self.y = y self.image = pygame.image.load('graphics/Robot 1/robot1_gun.png').convert_alpha() self.rect = self.image.get_rect() self.orig_image = pygame.image.load('graphics/Robot 1/robot1_gun.png').convert_alpha() self.rotate_vel = 1 self.cross_image = pygame.image.load('graphics/crosshair049.png') def draw(self, surface): """ Draw on surface """ surface.blit(self.image, self.rect) dir_vec = pygame.math.Vector2() dir_vec.from_polar((180, -self.rotate_vel)) cross_pos = dir_vec + self.rect.center cross_x, cross_y = round(cross_pos.x), round(cross_pos.y) surface.blit(self.cross_image, self.cross_image.get_rect(center = (cross_x, cross_y))) def movement(self): key = pygame.key.get_pressed() self.rect = self.image.get_rect(center = (self.x, self.y)) dist = 3 # 可以改成5让移动更快 if key[pygame.K_DOWN] or key[pygame.K_s]: self.y += dist elif key[pygame.K_UP] or key[pygame.K_w]: self.y -= dist if key[pygame.K_RIGHT] or key[pygame.K_d]: self.x += dist elif key[pygame.K_LEFT] or key[pygame.K_a]: self.x -= dist def rotate(self, surface): keys = pygame.key.get_pressed() if keys[K_LSHIFT]: self.rotate_vel += 7 self.image = pygame.transform.rotate(self.orig_image, self.rotate_vel) self.rect = self.image.get_rect(center=self.rect.center) surface.blit(self.image, self.rect) if keys[K_SPACE]: self.rotate_vel += -7 self.image = pygame.transform.rotate(self.orig_image, self.rotate_vel) self.rect = self.image.get_rect(center=self.rect.center) surface.blit(self.image, self.rect) def update(self): self.movement() self.draw(screen) self.rotate(screen) class Bullet(pygame.sprite.Sprite): def __init__(self, pos, angle): super().__init__() self.image = pygame.image.load('graphics/weapons/bullets/default_bullet.png') self.image = pygame.transform.rotate(self.image, angle) self.rect = self.image.get_rect(center = pos) self.speed = 25 self.pos = Vector2(pos) # 改成Vector2避免整数移动的精度问题 self.dir_vec = pygame.math.Vector2() self.dir_vec.from_polar((self.speed, -angle)) def update(self, screen): self.pos += self.dir_vec self.rect.center = round(self.pos.x), round(self.pos.y) # 超出屏幕自动销毁子弹,节省资源 if not screen.get_rect().colliderect(self.rect): self.kill() class Zombie(pygame.sprite.Sprite): def __init__(self, x , y): super().__init__() self.x = x self.y = y self.image = pygame.image.load('graphics/zombie/zoimbie1_hold.png').convert_alpha() self.rect = self.image.get_rect(center = (x,y)) # 修正:之前错用了玩家的原图,现在改成僵尸的图 self.orig_image = pygame.image.load('graphics/zombie/zoimbie1_hold.png').convert_alpha() def draw(self, surface): surface.blit(self.image, self.rect) def update(self): self.draw(screen) # 屏幕初始化 clock = pygame.time.Clock() FPS = 60 screen = pygame.display.set_mode((1200, 600)) # 玩家 player_sprite = Player(600, 300) player = pygame.sprite.GroupSingle() player.add(player_sprite) # 子弹组 bullet_group = pygame.sprite.Group() XP = 0 # 僵尸组 zombie = pygame.sprite.Group() zombie_limit = 5 # 僵尸生成计时器 zombie_spawn_timer = pygame.USEREVENT + 1 pygame.time.set_timer(zombie_spawn_timer , 1000) running = True while running: for event in pygame.event.get(): if event.type == pygame.QUIT: pygame.quit() exit() if event.type == pygame.MOUSEBUTTONDOWN: if event.button == 1: pos = player_sprite.rect.center new_bullet = Bullet(pos, player_sprite.rotate_vel) bullet_group.add(new_bullet) if event.type == zombie_spawn_timer and len(zombie.sprites()) < zombie_limit: zombie_x = random.randint(100, 1100) zombie_y = random.randint(50, 450) zombie_sprite = Zombie(zombie_x, zombie_y) zombie.add(zombie_sprite) # --- 核心修改:移到主循环的碰撞检测 --- # 检测所有僵尸和子弹的碰撞,两个True表示碰撞后同时销毁僵尸和子弹 collisions = pygame.sprite.groupcollide(zombie, bullet_group, True, True) # 可以用len(collisions)统计击杀数,加到XP里 XP += len(collisions) # 绘制顺序要注意:先清屏,再画背景,然后是玩家、子弹、僵尸 screen.fill('grey') player.update() bullet_group.update(screen) bullet_group.draw(screen) zombie.update() clock.tick(FPS) pygame.display.update()
关键修改细节
碰撞检测逻辑重构:
- 用
pygame.sprite.groupcollide()代替单个精灵的碰撞检测,这个函数会自动遍历两个组里的所有精灵,检测所有可能的碰撞。 - 把碰撞检测移到主循环的每一帧,这样子弹飞行过程中任何时候击中僵尸都会触发击杀,而不是只有点击鼠标时才检查。
- 用
其他优化修复:
- 修正了
Zombie类里的orig_image引用错误(之前误用了玩家的图片)。 - 把子弹的位置改成
Vector2类型,避免整数运算导致的移动精度丢失。 - 添加了子弹超出屏幕后自动销毁的逻辑,防止内存里堆积大量无用子弹。
- 移除了初始的单个僵尸实例,统一由计时器生成,逻辑更统一。
- 修正了
这样修改后,所有生成的僵尸都能被正常击杀了,试试看效果吧!
内容的提问来源于stack exchange,提问作者Taha saqib
相关产品推荐
相关产品推荐

