Pygame玩家水平碰撞检测失效:角色无法移动或无限下落问题求解
问题排查
现有代码共存在5个核心问题导致碰撞异常:
- 碰撞检测未拆分水平、垂直方向,两个方向的碰撞修正互相干扰,是角色无法移动、无限下落的核心原因
- 注释的水平碰撞逻辑完全错误:误将玩家自身x坐标作为碰撞方向判断条件,左右碰撞的修正逻辑写反
- 跳跃逻辑错误:跳跃按键检测放在碰撞遍历循环中,且只要有一个方块未碰撞就会将
able_jump设为False,导致跳跃判定失效 - 地图类缩进错误:
__init__、draw方法未缩进至Map类内部,运行直接报错;且blocks列表仅在draw时生成,碰撞检测调用时机早于绘制时会出现空列表,无碰撞判定导致无限下落 - 碰撞修正后未同步位置到
pos变量,修正结果在下一帧移动时被覆盖,看起来像角色被卡住无法移动
修正代码
1. 修正后地图类代码
import csv import pygame from pygame.sprite import Sprite class Map(Sprite): # 修正类内方法缩进 def __init__(self, game): super().__init__() self.screen = game.screen self.settings = game.settings mapname = r'practice\map.csv' infinitemapname = r'practice\infinitemap.csv' self.full_map = [] with open(mapname) as f: reader = csv.reader(f,delimiter=',') for row in reader: self.full_map.append(list(row)) self.dirt_block = pygame.image.load('block.png') # 提前生成blocks列表,避免绘制前碰撞检测为空 self.blocks = [] for y, line in enumerate(self.full_map): for x, c in enumerate(line): if c == "0": center = ((x*10)+4, (y*10)+4) self.blocks.append(self.dirt_block.get_rect(center=center)) def draw(self): """This method draws the map.""" for y, line in enumerate(self.full_map): for x, c in enumerate(line): if c == "0": self.screen.blit(self.dirt_block, (x * 10-self.settings.scroll[0], y * 10-self.settings.scroll[1]))
2. 修正后碰撞检测代码
def collide(self): # 先单独处理水平方向碰撞 self.player.rect.x = self.player.pos.x self.player.able_jump = False for each in self.map.blocks: if self.player.rect.colliderect(each): # 向右移动碰撞修正 if self.player.vel.x > 0: self.player.rect.right = each.left self.player.vel.x = 0 # 向左移动碰撞修正 if self.player.vel.x < 0: self.player.rect.left = each.right self.player.vel.x = 0 # 同步修正后的水平位置到pos变量 self.player.pos.x = self.player.rect.x # 再单独处理垂直方向碰撞 self.player.rect.y = self.player.pos.y for each in self.map.blocks: if self.player.rect.colliderect(each): # 下落着陆碰撞 if self.player.vel.y > 0: self.player.rect.bottom = each.top self.player.vel.y = 0 self.player.able_jump = True # 向上跳碰到顶部方块 if self.player.vel.y < 0: self.player.rect.top = each.bottom self.player.vel.y = 0 # 同步修正后的垂直位置到pos变量 self.player.pos.y = self.player.rect.y # 跳跃检测移出碰撞循环,避免多方块碰撞时判定被覆盖 jump = pygame.key.get_pressed() if jump[pygame.K_SPACE] and self.player.able_jump: self.player.vel.y = -self.settings.jump_height
3. 修正后玩家移动代码
仅需删除原代码末尾多余的self.able_jump = False即可,其余逻辑保留:
def update(self): """Controls the player's position.""" self.acc = vec(0,0.5) moving = pygame.key.get_pressed() if moving[pygame.K_LEFT]: self.acc.x = -self.settings.acceleration if moving[pygame.K_RIGHT]: self.acc.x = self.settings.acceleration self.acc.x += self.vel.x * self.settings.friction self.vel += self.acc self.pos += self.vel + 0.5 * self.acc if self.pos.x > self.settings.screen_width: self.pos.x = 0 if self.pos.x < 0: self.pos.x = self.settings.screen_width if self.pos.y > (self.settings.screen_height + 400): self.pos.y = 0 self.rect.midbottom = self.pos
调用顺序要求
主循环执行顺序必须严格按照:玩家update计算位置 → 碰撞检测修正位置 → 绘制所有元素,否则会出现时序类的碰撞异常。
内容的提问来源于stack exchange,提问作者AgiriDeep
相关产品推荐
相关产品推荐

