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

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)

问题根源

  1. 移动-回退的碰撞逻辑缺陷:当前逻辑是先移动玩家,再检测碰撞,若碰撞则回退移动量。但当yvel过大时(比如多次跳跃叠加速度、重力累积),玩家单次移动距离超过tile厚度(32px),移动后会完全穿过tile,此时碰撞检测无法触发,自然不会回退。
  2. 无限制跳跃导致速度叠加:没有判断玩家是否在地面就允许跳跃,多次跳跃后yvel会变得很大,加剧穿透问题。
  3. 按键直接覆盖速度: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

相关产品推荐
方舟 Agent Plan

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

最近更新时间:2026.08.11 16:15:43