Pygame中如何实现玩家碰撞敌人时触发死亡逻辑
Pygame 玩家与敌人碰撞触发死亡逻辑实现
现有代码影响碰撞功能的问题
- Button类重复定义了
__init__方法,运行时前一个初始化逻辑会被后一个覆盖,属于语法隐患 - 玩家、敌人的矩形碰撞盒(rect)都是临时在绘制逻辑里生成的,没有在位置更新后同步状态,也没有做碰撞判定
- 实际绘制的是旋转后的角色图片,但之前临时生成的rect是基于未旋转的原始图,碰撞范围和实际显示的角色位置不匹配,检测结果会不准
- FPS设置为1000过高,且同个渲染循环里同时调用
pygame.display.flip()和pygame.display.update(),会造成画面重复刷新、移动速度不稳定
具体实现方法
直接用Pygame内置的矩形碰撞检测方法colliderect()即可,不需要自己写碰撞判定算法,步骤如下:
- 删掉Button类里重复的第二个
__init__方法,只保留一个初始化逻辑 - 把FPS调整为常规的60,删掉Start为True的逻辑块里多余的
pygame.display.flip(),统一用循环末尾的pygame.display.update()刷新画面 - 每帧更新完玩家、敌人的坐标后,生成玩家和所有敌人旋转后的实际显示rect
- 遍历所有敌人的rect,和玩家rect做碰撞检测,一旦判定重叠就触发死亡逻辑:示例里实现了重置玩家位置、返回开始菜单的基础效果,后续可以自行扩展死亡动画、结算界面
修改后可直接运行的完整代码
import pygame, math pygame.init() SCREEN_WIDTH = 750 SCREEN_HEIGHT = 500 SCREEN_COLOR = (160, 160, 160) START_BUTTON_SIZE = .5 START_BUTTON_X = 280 START_BUTTON_Y = 320 EXIT_BUTTON_SIZE = .1 EXIT_BUTTON_X = 695 EXIT_BUTTON_Y = 5 player_x = 450 player_y = 250 player_width = 35 player_height = 40 player_vel = 4 player_color = (1, 40, 100) Enemy_width = 30 Enemy_height = 30 # 存储敌人初始位置,死亡重置时调用 enemy_init_positions = [[0, 0], [750, 500], [0, 500], [750, 0], [0, 250], [750, 250], [375, 0], [375, 500]] enemy_positions = [pos.copy() for pos in enemy_init_positions] Enemy_Vel = 2 FPS = 60 Start = False screen = pygame.display.set_mode((SCREEN_WIDTH, SCREEN_HEIGHT)) Start_Menu = pygame.image.load('Start_Menu.bmp').convert_alpha() Start_Menu = pygame.transform.scale(Start_Menu, (SCREEN_WIDTH, SCREEN_HEIGHT)) Background = pygame.image.load('Background.bmp').convert_alpha() Background = pygame.transform.scale(Background, (SCREEN_WIDTH, SCREEN_HEIGHT+10)) pygame.display.set_caption('Outlaw') Enemy = pygame.transform.smoothscale(pygame.image.load("Start_Enemy.bmp").convert_alpha(), (Enemy_width, Enemy_height)) player = pygame.transform.smoothscale(pygame.image.load("Player.bmp").convert_alpha(), (player_width, player_height)) start_image = pygame.image.load('Start_btn.bmp').convert_alpha() exit_image = pygame.image.load('Exit_btn.bmp').convert_alpha() correction_angle = 90 class Button(): # 删除重复定义的__init__方法 def __init__(self, x, y, image, scale): width = image.get_width() height = image.get_height() self.clicked = False self.image = pygame.transform.scale(image, (int(width * scale), int(height * scale))) self.rect = self.image.get_rect() self.rect.topleft = (x, y) def draw(self): action = False pos = pygame.mouse.get_pos() if self.rect.collidepoint(pos): if pygame.mouse.get_pressed()[0] == 1 and self.clicked == False: self.clicked = True action = True if pygame.mouse.get_pressed()[0] == 0: self.clicked = False screen.blit(self.image,(self.rect.x, self.rect.y)) return action clock = pygame.time.Clock() start_button = Button(START_BUTTON_X, START_BUTTON_Y, start_image, START_BUTTON_SIZE) exit_button = Button(EXIT_BUTTON_X, EXIT_BUTTON_Y, exit_image, EXIT_BUTTON_SIZE) # 游戏重置逻辑 def reset_game(): global Start, player_x, player_y, enemy_positions Start = False player_x = 450 player_y = 250 enemy_positions = [pos.copy() for pos in enemy_init_positions] run = True while run: clock.tick(FPS) if Start == False: screen.fill((SCREEN_COLOR)) screen.blit(Start_Menu,(0, 0)) if start_button.draw() == True and Start == False: print('START') Start = True player_x = 225 player_y = 400 if Start == True: screen.fill((SCREEN_COLOR)) screen.blit(Background,(0, 0)) # 生成玩家旋转后的实际碰撞盒 player_pos = player_x, player_y player_rect = player.get_rect(center = player_pos) mx, my = pygame.mouse.get_pos() dx, dy = mx - player_rect.centerx, my - player_rect.centery angle = math.degrees(math.atan2(-dy, dx)) - correction_angle rot_player_image = pygame.transform.rotate(player, angle) rot_player_rect = rot_player_image.get_rect(center = player_rect.center) enemy_rot_list = [] for enemy_pos in enemy_positions: enemy_rect = Enemy.get_rect(center = enemy_pos) dx, dy = player_x - enemy_rect.centerx, player_y - enemy_rect.centery angle = math.degrees(math.atan2(-dy, dx)) - correction_angle rot_enemy_image = pygame.transform.rotate(Enemy, angle) rot_enemy_rect = rot_enemy_image.get_rect(center = enemy_rect.center) enemy_rot_list.append((rot_enemy_image, rot_enemy_rect)) # 逐敌人做碰撞检测 for _, enemy_rect in enemy_rot_list: if rot_player_rect.colliderect(enemy_rect): print("PLAYER DEAD") reset_game() break # 检测完成后再绘制画面,避免死亡帧残留游戏内容 screen.blit(rot_player_image, rot_player_rect.topleft) for enemy_img, enemy_rect in enemy_rot_list: screen.blit(enemy_img, enemy_rect.topleft) # 敌人移动逻辑仅在游戏进行中生效 if Start: for enemy_pos in enemy_positions: if enemy_pos[0] < player_x: enemy_pos[0] += Enemy_Vel if enemy_pos[0] > player_x: enemy_pos[0] -= Enemy_Vel if enemy_pos[1] < player_y: enemy_pos[1] += Enemy_Vel if enemy_pos[1] > player_y: enemy_pos[1] -= Enemy_Vel keys = pygame.key.get_pressed() # 玩家移动逻辑仅在游戏进行中生效 if Start: if keys[pygame.K_a] and player_x>0: player_x -= player_vel if keys[pygame.K_d] and player_x<SCREEN_WIDTH: player_x += player_vel if keys[pygame.K_w] and player_y>0: player_y -= player_vel if keys[pygame.K_s] and player_y<SCREEN_HEIGHT: player_y += player_vel if exit_button.draw() == True: print('EXIT') run = False pygame.display.update() for event in pygame.event.get(): if event.type == pygame.QUIT: run = False pygame.quit()
如果需要更精准的像素级碰撞,可以把
colliderect()替换为pygame.sprite.collide_mask(),对于当前的简单俯视角游戏,矩形碰撞已经能满足需求。
内容的提问来源于stack exchange,提问作者LegoSuperBall
相关产品推荐
相关产品推荐

