Pygame无重力2D玩家移动瓦片垂直碰撞穿模问题咨询
2D无重力瓦片碰撞问题修复方案
核心问题原因
当前的瞬移bug来自两个核心逻辑错误:
- 移动与碰撞的执行顺序错误:先完成了x、y两个方向的位移,再依次做x、y轴碰撞检测,斜向碰撞时y轴修正后的位置会和瓦片产生x方向的异常重叠,触发不必要的x轴位置修正。
- 布尔碰撞标记逻辑错误:标记未在每次检测前重置,且写在循环内会被后续未碰撞的瓦片覆盖状态,全局变量也会残留上一帧的状态导致逻辑异常。
修复步骤
1. 调整移动与碰撞的执行流程
把x、y方向的移动和碰撞检测拆成独立步骤,执行顺序改为:
- 仅计算并执行x方向位移
- 做x轴碰撞检测,修正x轴位置
- 仅计算并执行y方向位移
- 做y轴碰撞检测,修正y轴位置
2. 重构代码
调整Player类的移动逻辑,拆分x/y轴位移
# 新增单轴移动处理方法,不要同时处理两个轴的位移 def move_x(self): key_input = pygame.key.get_pressed() self.direction.x = 0 if key_input[pygame.K_a]: self.direction.x = -1 if key_input[pygame.K_d]: self.direction.x = 1 # 斜向速度判定 speed = self.velx_diag if self.direction.y != 0 else self.velx self.rect.x += self.direction.x * speed def move_y(self): key_input = pygame.key.get_pressed() self.direction.y = 0 if key_input[pygame.K_w]: self.direction.y = -1 if key_input[pygame.K_s]: self.direction.y = 1 # 斜向速度判定 speed = self.vely_diag if self.direction.x != 0 else self.vely self.rect.y += self.direction.y * speed
重构碰撞检测方法,移除全局布尔标记
def collisions(self, check_axis): player = self.player.sprite for cell in self.celle.sprites(): if not cell.rect.colliderect(player.rect): continue if check_axis == 'x': if player.direction.x < 0: player.rect.left = cell.rect.right elif player.direction.x > 0: player.rect.right = cell.rect.left elif check_axis == 'y': if player.direction.y < 0: player.rect.top = cell.rect.bottom elif player.direction.y > 0: player.rect.bottom = cell.rect.top
修改主循环的更新逻辑
把原来的movement()和collisions()调用替换为以下顺序:
self.player.sprite.move_x() self.collisions('x') self.player.sprite.move_y() self.collisions('y')
兼容方案(不想大改原有movement逻辑)
如果要保留原有movement方法,也可以用位置回溯的方式实现:
def collisions(self): player = self.player.sprite # 先单独处理x轴位移与碰撞 original_x = player.rect.x player.rect.x += player.direction.x * (player.velx_diag if player.direction.y else player.velx) for cell in self.celle.sprites(): if cell.rect.colliderect(player.rect): if player.direction.x < 0: player.rect.left = cell.rect.right elif player.direction.x > 0: player.rect.right = cell.rect.left # 再单独处理y轴位移与碰撞 original_y = player.rect.y player.rect.y += player.direction.y * (player.vely_diag if player.direction.x else player.vely) for cell in self.celle.sprites(): if cell.rect.colliderect(player.rect): if player.direction.y < 0: player.rect.top = cell.rect.bottom elif player.direction.y > 0: player.rect.bottom = cell.rect.top
内容的提问来源于stack exchange,提问作者irastol
相关产品推荐
相关产品推荐

