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

Pygame中新生成僵尸Sprite无法被击杀的技术问题

僵尸击杀问题的修复方案

嘿,我看到你遇到了僵尸击杀的棘手问题——只能干掉第一只生成的僵尸,后面出来的都能免疫子弹是吧?这问题我之前做射击游戏时也碰到过,核心是碰撞检测的逻辑位置和目标对象都错了,让我给你拆解清楚,再把代码修好:

问题根源分析

  1. 只检测单个僵尸实例:你在鼠标点击事件里写的碰撞检测只针对zombie_sprite这个变量,但每次生成新僵尸时,这个变量会被覆盖成新的僵尸实例,之前的僵尸就再也不会被检测到碰撞了。
  2. 碰撞检测时机错误:你把碰撞逻辑放在了鼠标点击事件里,这意味着只有点击鼠标的那一瞬间才会检查碰撞,但子弹是一直在飞行的,应该在每一帧的主循环中持续检测所有子弹和僵尸的碰撞。

修复后的完整代码

我已经把关键修改点用注释标出来了,直接替换你的代码就能用:

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

关键修改细节

  1. 碰撞检测逻辑重构:

    • 用pygame.sprite.groupcollide()代替单个精灵的碰撞检测,这个函数会自动遍历两个组里的所有精灵,检测所有可能的碰撞。
    • 把碰撞检测移到主循环的每一帧,这样子弹飞行过程中任何时候击中僵尸都会触发击杀,而不是只有点击鼠标时才检查。
  2. 其他优化修复:

    • 修正了Zombie类里的orig_image引用错误(之前误用了玩家的图片)。
    • 把子弹的位置改成Vector2类型,避免整数运算导致的移动精度丢失。
    • 添加了子弹超出屏幕后自动销毁的逻辑,防止内存里堆积大量无用子弹。
    • 移除了初始的单个僵尸实例,统一由计时器生成,逻辑更统一。

这样修改后,所有生成的僵尸都能被正常击杀了,试试看效果吧!

内容的提问来源于stack exchange,提问作者Taha saqib

相关产品推荐
方舟 Agent Plan

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

最近更新时间:2026.04.30 10:07:36