Pygame玩家碰撞与跳跃机制异常:多次跳跃可穿透地图
Pygame角色跳跃多次后穿透地图的问题解决
问题描述
在Pygame开发的游戏中,多次触发跳跃后玩家角色会穿透地图。推测问题出在player.y -= yvel代码行,但无法理解碰撞检测阻止移动的逻辑为何失效——该逻辑本应阻止所有引发碰撞的移动。
原代码:
import pygame import sys window = pygame.display.set_mode((768,768)) tile_map = [] for i in range(24): tile_map.append(pygame.Rect(i*32, 400, 32, 32)) for i in range(12): tile_map.append(pygame.Rect(i*32+256, 200, 32, 32)) player = pygame.Rect(300,300,32,32) xvel=0 yvel=0 a = False d = False s = False w = False space = False def collision_check(player,tm): for t in tm: if player.colliderect(t): return True return False while True: for event in pygame.event.get(): if event.type == pygame.QUIT: pygame.quit() sys.exit() if event.type == pygame.KEYDOWN: if event.key == pygame.K_a: a = True if event.key == pygame.K_d: d = True if event.key == pygame.K_w: w = True if event.key == pygame.K_s: s = True if event.key == pygame.K_SPACE: space = True if event.type == pygame.KEYUP: if event.key == pygame.K_a: a = False if event.key == pygame.K_d: d = False if event.key == pygame.K_w: w = False if event.key == pygame.K_s: s = False if event.key == pygame.K_SPACE: space = False window.fill(0) yvel += .1 if space: space = False yvel -= 5 if yvel > 5: yvel = 5 if xvel > 5: xvel = 5 if a: xvel = -1 elif d: xvel = 1 if w: yvel = -1 elif s: yvel = 1 player.y += yvel if collision_check(player, tile_map): player.y -= yvel yvel = 0 player.x += xvel if collision_check(player, tile_map): player.x -= xvel xvel = 0 pygame.draw.rect(window,(0,0,255),player) for t in tile_map: pygame.draw.rect(window,(255,0,0),t) pygame.display.update() pygame.time.Clock().tick(60)
问题根源
- 移动-回退的碰撞逻辑缺陷:当前逻辑是先移动玩家,再检测碰撞,若碰撞则回退移动量。但当
yvel过大时(比如多次跳跃叠加速度、重力累积),玩家单次移动距离超过tile厚度(32px),移动后会完全穿过tile,此时碰撞检测无法触发,自然不会回退。 - 无限制跳跃导致速度叠加:没有判断玩家是否在地面就允许跳跃,多次跳跃后
yvel会变得很大,加剧穿透问题。 - 按键直接覆盖速度:
W/S键直接设置yvel = -1/1,会覆盖跳跃和重力带来的速度,导致逻辑混乱。
修复方案
关键修改点
- 碰撞检测返回具体碰撞的tile,而非仅布尔值,以便精确调整玩家位置。
- 增加地面判断,仅允许在地面时跳跃,避免速度无限叠加。
- 限制最大下落速度,确保单次移动距离不超过tile厚度。
- 碰撞时根据移动方向将玩家贴在tile边缘,而非简单回退移动量。
- 优化移动逻辑,用加速度代替直接设置速度,避免覆盖跳跃/重力速度。
修复后代码
import pygame import sys window = pygame.display.set_mode((768,768)) tile_map = [] for i in range(24): tile_map.append(pygame.Rect(i*32, 400, 32, 32)) for i in range(12): tile_map.append(pygame.Rect(i*32+256, 200, 32, 32)) player = pygame.Rect(300,300,32,32) xvel=0 yvel=0 a = False d = False s = False w = False space = False on_ground = False # 标记是否在地面 def collision_check(player,tm): for t in tm: if player.colliderect(t): return t # 返回碰撞的tile对象 return None while True: for event in pygame.event.get(): if event.type == pygame.QUIT: pygame.quit() sys.exit() if event.type == pygame.KEYDOWN: if event.key == pygame.K_a: a = True if event.key == pygame.K_d: d = True if event.key == pygame.K_w: w = True if event.key == pygame.K_s: s = True if event.key == pygame.K_SPACE: space = True if event.type == pygame.KEYUP: if event.key == pygame.K_a: a = False if event.key == pygame.K_d: d = False if event.key == pygame.K_w: w = False if event.key == pygame.K_s: s = False if event.key == pygame.K_SPACE: space = False window.fill(0) # 重力累加 yvel += 0.5 # 限制最大下落速度,防止穿透tile if yvel > 8: yvel = 8 # 仅在地面时允许跳跃 if space and on_ground: space = False yvel = -10 # 左右移动:用加速度实现平滑移动 if a: xvel -= 0.3 elif d: xvel += 0.3 else: # 无按键时减速 xvel *= 0.8 # 限制左右最大速度 if abs(xvel) > 5: xvel = 5 if xvel > 0 else -5 # W/S作为微调(可选,若不需要可删除) if w: yvel -= 0.2 elif s: yvel += 0.2 on_ground = False # 先处理Y轴碰撞(重力优先级更高) player.y += yvel collided_tile = collision_check(player, tile_map) if collided_tile: # 根据移动方向调整玩家位置 if yvel > 0: # 下落时,将玩家底部贴在tile顶部 player.bottom = collided_tile.top on_ground = True elif yvel < 0: # 上升时,将玩家顶部贴在tile底部 player.top = collided_tile.bottom yvel = 0 # 处理X轴碰撞 player.x += xvel collided_tile = collision_check(player, tile_map) if collided_tile: if xvel > 0: player.right = collided_tile.left elif xvel < 0: player.left = collided_tile.right xvel = 0 pygame.draw.rect(window,(0,0,255),player) for t in tile_map: pygame.draw.rect(window,(255,0,0),t) pygame.display.update() pygame.time.Clock().tick(60)
内容的提问来源于stack exchange,提问作者URMOM.COM
相关产品推荐
相关产品推荐

