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

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

相关产品推荐
方舟 Agent Plan

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

最近更新时间:2026.08.28 22:06:26