如何在Pygame中实现精灵碰撞阻止穿透?
Pygame 物体碰撞穿透问题解决
我用Pygame开发游戏,目前遇到了物体碰撞穿透的问题。尝试过查找相关解决方案,但这些方案要么与我已实现的移动系统不兼容,要么完全无法理解。以下是我的代码实现:
import pygame, sys from pygame.math import Vector2 pygame.init() screen = pygame.display.set_mode((500,500)) clock = pygame.time.Clock() #Environment Variables gravity = -1 jumpForce = 20 moveSpeed = 10 #Game Objects playerPos = Vector2(230,230) playerVelo = Vector2(0,0) player = pygame.Rect(playerPos.x,playerPos.y, 20, 20) boxPos = Vector2(350,480) box = pygame.Rect(boxPos.x, boxPos.y, 20,20) def Clamp(var, minClamp, maxClamp): if minClamp > maxClamp: raise Exception("minClamp must be less than maxClamp") if var < minClamp: var = minClamp if var > maxClamp: var = maxClamp return var def Draw(): global player,box screen.fill((255,255,25)) player = pygame.Rect(playerPos.x,playerPos.y, 20, 20) pygame.draw.rect(screen,(0,100,255),player) box = pygame.Rect(boxPos.x, boxPos.y, 20,20) pygame.draw.rect(screen,(10,200,20),box) def PlayerVelocity(): global playerPos,playerVelo,player,box,boxPos if player.colliderect(box): playerPos = Vector2(boxPos.x,boxPos.y-20) print("balls") if playerPos.y < 499: playerVelo.y += gravity #if not pygame.Rect(playerPos.x+playerVelo.x,playerPos.y+playerVelo.y,20,20).colliderect(box): playerPos -= playerVelo 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_SPACE or event.key == pygame.K_UP: playerVelo.y = jumpForce print(playerVelo) keys = pygame.key.get_pressed() if keys[pygame.K_a] or keys[pygame.K_LEFT]: playerVelo.x = 1*moveSpeed elif keys[pygame.K_d] or keys[pygame.K_RIGHT]: playerVelo.x = -1*moveSpeed else: playerVelo.x = 0 #Draw things to the screen Draw() PlayerVelocity() playerPos.x = Clamp(playerPos.x, 0, 480) playerPos.y = Clamp(playerPos.y,0,480) pygame.display.update() clock.tick(30)
问题分析
你的碰撞逻辑存在几个关键问题,直接导致了穿透:
- 先移动玩家再检测碰撞,此时玩家已经部分/完全穿入物体,强行重置位置的方式不仅生硬,还会因为速度过快导致穿透
- 碰撞后没有重置对应方向的速度,比如碰到箱子后垂直速度仍在累加,会让玩家卡在物体上或者继续穿透
- 没有分方向处理碰撞,无法区分水平/垂直方向的碰撞响应
修改后的代码
import pygame, sys from pygame.math import Vector2 pygame.init() screen = pygame.display.set_mode((500,500)) clock = pygame.time.Clock() # 环境变量 gravity = -1 jumpForce = 20 moveSpeed = 10 # 游戏对象 playerPos = Vector2(230,230) playerVelo = Vector2(0,0) player = pygame.Rect(playerPos.x,playerPos.y, 20, 20) boxPos = Vector2(350,480) box = pygame.Rect(boxPos.x, boxPos.y, 20,20) def Clamp(var, minClamp, maxClamp): if minClamp > maxClamp: raise Exception("minClamp必须小于maxClamp") if var < minClamp: var = minClamp if var > maxClamp: var = maxClamp return var def Draw(): global player,box screen.fill((255,255,25)) player = pygame.Rect(playerPos.x,playerPos.y, 20, 20) pygame.draw.rect(screen,(0,100,255),player) box = pygame.Rect(boxPos.x, boxPos.y, 20,20) pygame.draw.rect(screen,(10,200,20),box) def PlayerVelocity(): global playerPos,playerVelo,player,box,boxPos # 先更新垂直方向重力 if playerPos.y < 480: # 地面是y=480(因为玩家高20,y=480时底部刚好贴地面) playerVelo.y += gravity # 预测水平移动后的位置 predicted_x = playerPos.x - playerVelo.x predicted_player_x = pygame.Rect(predicted_x, playerPos.y, 20, 20) # 检测水平碰撞 if not predicted_player_x.colliderect(box): playerPos.x = predicted_x else: # 碰到箱子后水平速度归零 playerVelo.x = 0 # 预测垂直移动后的位置 predicted_y = playerPos.y - playerVelo.y predicted_player_y = pygame.Rect(playerPos.x, predicted_y, 20, 20) # 检测垂直碰撞 if not predicted_player_y.colliderect(box) and predicted_y <= 480: playerPos.y = predicted_y else: # 碰到箱子或地面后垂直速度归零,防止继续穿透 playerVelo.y = 0 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_SPACE or event.key == pygame.K_UP: # 只有在玩家接触地面或箱子时才能跳跃 if playerPos.y >= 480 or player.colliderect(box): playerVelo.y = jumpForce keys = pygame.key.get_pressed() if keys[pygame.K_a] or keys[pygame.K_LEFT]: playerVelo.x = 1*moveSpeed elif keys[pygame.K_d] or keys[pygame.K_RIGHT]: playerVelo.x = -1*moveSpeed else: playerVelo.x = 0 # 先处理物理逻辑,再绘制 PlayerVelocity() Draw() playerPos.x = Clamp(playerPos.x, 0, 480) playerPos.y = Clamp(playerPos.y,0,480) pygame.display.update() clock.tick(60) # 提高帧率让移动更流畅
关键改动说明
- 预测碰撞:移动前先计算预期位置,检测是否会碰撞,只有不碰撞才执行移动,从根源避免穿透
- 分方向处理:把水平和垂直方向的碰撞分开检测,分别处理速度和位置,逻辑更清晰
- 碰撞后速度重置:碰到物体后对应方向的速度归零,防止玩家继续向碰撞方向移动
- 跳跃优化:只有当玩家接触地面或箱子时才能跳跃,避免空中无限跳
- 调整帧率:从30帧提高到60帧,让移动和碰撞检测更流畅
内容的提问来源于stack exchange,提问作者Liam Berube
相关产品推荐
相关产品推荐

