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

Pygame生成多个敌人对象异常问题求助

Pygame躲避游戏敌人重叠问题解决

问题描述

编程新手尝试用Pygame制作简单躲避类游戏:玩家操控蓝色矩形躲避红色矩形敌人,但所有敌人都绘制在同一位置,看起来像单个快速移动的敌人。使用Sprite类实现敌人(仅为学习Sprite用法),尝试用类解决但未成功,代码如下:

from textwrap import fill
import pygame
import os
import random
pygame.font.init()

text = "GAMEOVER!"

WIDTH, HEIGHT = 450, 450
WIN = pygame.display.set_mode((WIDTH, HEIGHT))
pygame.display.set_caption("Rando game")
HEALTH_FONT = pygame.font.SysFont('comicsans', 40)
FPS = 60
WHITE = (255, 255, 255)
RED = (255, 0, 0)
BLUE = (0, 0, 255)
PURPLE = (255, 0, 255)
ENEMY_VEL = 5
PLAYER_VEL = 10
HIT = pygame.USEREVENT + 1
class enemy:
    enemy_x = random.randint(0, WIDTH)
    enemy_y = - 30
ENEMY_SPRITE = pygame.transform.scale(pygame.image.load(
    'RED.png'), (30, 30))


#DRAW STUFF
def draw_window(enemy, player, health, enemies):
    WIN.fill(WHITE)
    #health
    health_text = HEALTH_FONT.render(str(health), 1, PURPLE)
    WIN.blit(health_text,(0, 0))

    #enemies
    for enemy in enemies:
        WIN.blit(ENEMY_SPRITE, (enemy.enemy_x, enemy.enemy_y))
    #player
    pygame.draw.rect(WIN, BLUE, player)
   

    pygame.display.update()


def handle_player_movement(player, keys_pressed):
    if keys_pressed[pygame.K_d] and player.x + PLAYER_VEL <= WIDTH - 25:
        player.x += PLAYER_VEL
    if keys_pressed[pygame.K_a] and player.x - PLAYER_VEL >= 0:
        player.x -= PLAYER_VEL

def enemy_movement(enemy, enemies):
        for enemy in enemies:
            enemy.enemy_y += ENEMY_VEL
    
def handle_collision(player, enemy, enemies):
    for enemy in enemies:
        if player.x + 25 > enemy.enemy_x and player.x <= enemy.enemy_x + 25 and player.y == enemy.enemy_y + 25:
            enemies.remove(enemy)
            pygame.event.post(pygame.event.Event(HIT))
            enemies.append(enemy)
            enemy.enemy_y = 0
            enemy.enemy_x = random.randint(0, WIDTH - 30)
        elif enemy.enemy_y > HEIGHT:
            enemies.remove(enemy)
            enemies.append(enemy)
            enemy.enemy_y = 0
            enemy.enemy_x = random.randint(0, WIDTH - 30)        



def main():
    clock = pygame.time.Clock() #locks fps
    run = True

    
    player = pygame.Rect(0, 425, 25, 25)
    health = 3
  
    while run:
        clock.tick(FPS)
        enemies = []
        for n in range(6):
            enemies.append(enemy)
        for event in pygame.event.get():
            if event.type == pygame.QUIT: #quits
                run = False
                pygame.quit()
     
            if event.type == HIT:
                health -= 1
                
        if health <= 0:
            enemies.remove(enemy)
            draw_text = HEALTH_FONT.render(text, 1, PURPLE)
            WIN.blit(draw_text, (WIDTH / 2 - draw_text.get_width() / 2, HEIGHT / 2 - draw_text.get_height() / 2))
            pygame.display.update()
            pygame.time.delay(2000)
            break
                


        print(enemies)
        keys_pressed = pygame.key.get_pressed()
        enemy_movement(enemy, enemies)
        handle_player_movement(player, keys_pressed)
        handle_collision(player, enemy, enemies)
        draw_window(enemy, player, health, enemies)
    main()
    

if __name__ == '__main__':
        main()

错误原因分析

  • 类属性误用:enemy类中的enemy_x和enemy_y是类属性,所有类实例共享同一值,导致所有敌人初始位置完全相同。
  • 实例化错误:main循环中每次创建新的enemies列表,且添加的是enemy类本身而非类的实例(enemy()),所有元素指向同一个类对象,位置自然一致。
  • 循环逻辑错误:每次游戏循环都重新生成6个敌人,导致敌人数量暴增;重置敌人的remove/append逻辑会引发遍历异常。
  • 碰撞检测不准确:手动判断坐标的碰撞条件不严谨,易出现漏判或误判。

修正后的代码

import pygame
import random

pygame.font.init()

text = "GAMEOVER!"

WIDTH, HEIGHT = 450, 450
WIN = pygame.display.set_mode((WIDTH, HEIGHT))
pygame.display.set_caption("Rando game")
HEALTH_FONT = pygame.font.SysFont('comicsans', 40)
FPS = 60
WHITE = (255, 255, 255)
RED = (255, 0, 0)
BLUE = (0, 0, 255)
PURPLE = (255, 0, 255)
ENEMY_VEL = 5
PLAYER_VEL = 10
HIT = pygame.USEREVENT + 1

# 重构Enemy类,继承pygame.sprite.Sprite符合学习需求
class Enemy(pygame.sprite.Sprite):
    def __init__(self):
        super().__init__()
        self.image = pygame.transform.scale(pygame.image.load('RED.png'), (30, 30))
        self.rect = self.image.get_rect()
        # 每个实例随机生成初始位置
        self.rect.x = random.randint(0, WIDTH - self.rect.width)
        self.rect.y = -self.rect.height

    def update(self):
        # 敌人移动逻辑,Sprite类统一更新方法
        self.rect.y += ENEMY_VEL
        # 超出屏幕后重置位置
        if self.rect.y > HEIGHT:
            self.rect.x = random.randint(0, WIDTH - self.rect.width)
            self.rect.y = -self.rect.height

# 用精灵组统一管理敌人
enemies_group = pygame.sprite.Group()
for _ in range(6):
    enemies_group.add(Enemy())

def draw_window(player, health):
    WIN.fill(WHITE)
    # 绘制生命值
    health_text = HEALTH_FONT.render(str(health), 1, PURPLE)
    WIN.blit(health_text, (0, 0))
    # 精灵组自动绘制所有敌人
    enemies_group.draw(WIN)
    # 绘制玩家
    pygame.draw.rect(WIN, BLUE, player)
    pygame.display.update()

def handle_player_movement(player, keys_pressed):
    if keys_pressed[pygame.K_d] and player.x + PLAYER_VEL <= WIDTH - player.width:
        player.x += PLAYER_VEL
    if keys_pressed[pygame.K_a] and player.x - PLAYER_VEL >= 0:
        player.x -= PLAYER_VEL

def handle_collision(player, health):
    # 用精灵组方法检测碰撞,准确高效
    collided_enemies = pygame.sprite.spritecollide(player, enemies_group, False)
    for enemy in collided_enemies:
        enemy.rect.x = random.randint(0, WIDTH - enemy.rect.width)
        enemy.rect.y = -enemy.rect.height
        return health - 1
    return health

def main():
    clock = pygame.time.Clock()
    run = True
    player = pygame.Rect(0, 425, 25, 25)
    health = 3

    while run:
        clock.tick(FPS)
        for event in pygame.event.get():
            if event.type == pygame.QUIT:
                run = False
                pygame.quit()
                return

        # 更新所有敌人状态
        enemies_group.update()
        keys_pressed = pygame.key.get_pressed()
        handle_player_movement(player, keys_pressed)
        # 处理碰撞并更新生命值
        health = handle_collision(player, health)

        if health <= 0:
            WIN.fill(WHITE)
            draw_text = HEALTH_FONT.render(text, 1, PURPLE)
            WIN.blit(draw_text, (WIDTH/2 - draw_text.get_width()/2, HEIGHT/2 - draw_text.get_height()/2))
            pygame.display.update()
            pygame.time.delay(2000)
            break

        draw_window(player, health)

if __name__ == '__main__':
    main()

关键修改说明

  • 重构Enemy类:继承pygame.sprite.Sprite,将位置改为实例属性,每个敌人创建时随机生成初始位置,符合Sprite使用规范。
  • 使用精灵组管理:pygame.sprite.Group统一处理敌人的绘制、更新和碰撞检测,简化代码逻辑。
  • 修正循环逻辑:敌人初始化移到main函数外,避免重复生成;改用重置位置替代remove/append逻辑,避免遍历异常。
  • 优化碰撞检测:用pygame.sprite.spritecollide替代手动坐标判断,检测更准确。
  • 修复游戏结束逻辑:移除递归调用main()的错误,直接退出循环。

内容的提问来源于stack exchange,提问作者Felipe Sancho

相关产品推荐
方舟 Agent Plan

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

最近更新时间:2026.08.16 02:01:19