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

Pygame平台游戏:如何检测精灵与矩形的碰撞侧边?

平台游戏碰撞检测失效问题

我正在开发一款平台游戏,需要实现碰撞检测功能以判断精灵与矩形碰撞的具体侧边,从而实现击杀敌人、防止坠落等逻辑。我尝试在类内使用常规的sprite.collide_any实现碰撞让玩家静止,但未成功,玩家仍会穿过方块,无碰撞检测效果。

以下是我的代码:

import pygame
from sys import exit
import random

class Player(pygame.sprite.Sprite):
    def __init__(self):
        super().__init__()
        player_Left1 = pygame.image.load('Player/Player_Left.png').convert_alpha()
        player_Right1 = pygame.image.load('Player/Player_Right.png').convert_alpha()
        player_Left2 = pygame.image.load('Player/Player_Left2.png').convert_alpha()
        player_Right2 = pygame.image.load('Player/Player_Right2.png').convert_alpha()
        self.player_Right = [player_Right1,player_Right2]
        self.player_Left = [player_Left1,player_Left2]
        self.player_index=0
        self.player_Jump_Left = pygame.image.load('Player/Player_Left_Up.png').convert_alpha()
        self.player_Jump_Right = pygame.image.load('Player/Player_Right_Up.png').convert_alpha()
        self.player_Fall_Left = pygame.image.load('Player/Player_Left_Down.png').convert_alpha()
        self.player_Fall_Right = pygame.image.load('Player/Player_Right_Down.png').convert_alpha()
        self.Right = True
        self.Left = False
        self.moving = False
        self.falling = False
        self.image = self.player_Right[self.player_index]
        self.rect  = self.image.get_rect(topleft = (50,336))
        self.gravity = 0
        self.ground = 336

    def player_input(self):
        keys = pygame.key.get_pressed()
        if keys[pygame.K_RIGHT]:
            self.rect.x +=2
            self.Right = True
            self.Left = False
            self.moving = True
        if keys[pygame.K_LEFT]:
            self.rect.x -=2
            self.Right = False
            self.Left = True
            self.moving = True
        if (keys[pygame.K_UP] or keys[pygame.K_SPACE]) and self.rect.top >= 336:
            self.gravity = -17
        elif not keys[pygame.K_RIGHT] and not keys[pygame.K_LEFT] and not keys[pygame.K_UP] or keys[pygame.K_SPACE]:
            self.moving = False

    def apply_gravity(self):
        self.gravity +=1
        self.rect.y += self.gravity
        if self.rect.top >= self.ground:
            self.rect.top = self.ground

    def animation_state(self):
        if self.rect.top < self.ground and self.Right and not self.falling:
            self.image = self.player_Jump_Right
            
        if self.rect.top < self.ground and self.Left:
            self.image=self.player_Jump_Left

        if self.rect.top <= self.ground-136:
            self.falling = True
        if self.falling:
            if self.Right:
                self.image = self.player_Fall_Right
            if self.Left:
                self.image = self.player_Fall_Left
        
        if self.rect.top >=self.ground and self.Right and self.moving:
            self.falling = False
            self.player_index += 0.1
            if self.player_index >= len(self.player_Right):self.player_index = 0
            self.image = self.player_Right[int(self.player_index)]
            
        if self.rect.top >=self.ground and self.Left and self.moving:
            self.falling = False
            self.player_index += 0.1
            if self.player_index >= len(self.player_Left):self.player_index = 0
            self.image = self.player_Left[int(self.player_index)]
            
        elif not self.moving and self.rect.top >=self.ground:
            if self.Right:
                self.image=self.player_Right[0]
            if self.Left:
                self.image=self.player_Left[0]
            self.falling = False

    def blockColl(self, sprite1, sprite2):
        if pygame.sprite.spritecollide(sprite1,sprite2,False):
            gravity=0
    
    def update(self):
        self.player_input()
        self.apply_gravity()
        self.animation_state()
        self.blockColl(self,Block)


class Block(pygame.sprite.Sprite):
    def __init__(self):
        super().__init__()
        self.image = pygame.image.load("Blocks/Block.png")
        self.rect  = self.image.get_rect(topleft = (100,336))


pygame.init()
screen = pygame.display.set_mode((500,550))
pygame.display.set_caption('Platformer')
clock = pygame.time.Clock()

player = pygame.sprite.GroupSingle()
player.add(Player())

blocks = pygame.sprite.GroupSingle()
blocks.add(Block())

while True:
    for event in pygame.event.get():
        if event.type == pygame.QUIT:
            pygame.quit()
            exit()
    screen.fill((34, 153, 227))
    
    player.draw(screen)
    player.update()

    blocks.draw(screen)
    blocks.update()
    
    pygame.display.update()
    clock.tick(60)

问题原因及修复方案

1. 碰撞检测调用错误

你在update方法里调用blockColl时,传入的是Block类而非blocks精灵组,导致碰撞检测根本没有生效。需要修改为:

self.blockColl(self, blocks)

2. 碰撞后处理逻辑无效

原blockColl方法中只是定义了局部变量gravity=0,并没有修改玩家实例的self.gravity,而且只处理了重力,完全没顾及水平方向的碰撞。需要重新编写碰撞逻辑,根据碰撞的具体侧边调整玩家位置:

def blockColl(self, blocks_group):
    collisions = pygame.sprite.spritecollide(self, blocks_group, False)
    if collisions:
        for block in collisions:
            # 顶部碰撞:玩家落在方块上
            if self.rect.bottom > block.rect.top and self.rect.top < block.rect.top and self.gravity > 0:
                self.rect.bottom = block.rect.top
                self.gravity = 0
                self.ground = self.rect.top  # 更新地面位置,让玩家站在方块上
            # 底部碰撞:玩家从上方撞到方块底部
            elif self.rect.top < block.rect.bottom and self.rect.bottom > block.rect.bottom and self.gravity < 0:
                self.rect.top = block.rect.bottom
                self.gravity = 0
            # 右侧碰撞:玩家从左往右撞方块
            elif self.rect.right > block.rect.left and self.rect.left < block.rect.left:
                self.rect.right = block.rect.left
            # 左侧碰撞:玩家从右往左撞方块
            elif self.rect.left < block.rect.right and self.rect.right > block.rect.right:
                self.rect.left = block.rect.right

3. 执行顺序错误

原代码先更新玩家位置、动画,再做碰撞检测,会导致玩家已经穿过方块后才触发检测。正确的顺序应该是:处理输入→应用重力→碰撞修正→更新动画:

def update(self):
    self.player_input()
    self.apply_gravity()
    self.blockColl(blocks)  # 先做碰撞位置修正
    self.animation_state()

额外优化点

  • 跳跃判定不要固定依赖self.rect.top >= 336,应该结合碰撞状态判断是否在地面,这样玩家站在方块上也能跳跃。
  • 避免依赖全局的blocks精灵组,可以在初始化玩家时传入该组,提升代码模块化程度。

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

相关产品推荐
方舟 Agent Plan

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

最近更新时间:2026.08.09 20:15:32