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
相关产品推荐
相关产品推荐

