Pygame精灵碰撞检测实现:双可控精灵碰撞穿模及卡死崩溃问题求解
问题原因
- 执行顺序倒置:现有代码先做碰撞检测,后执行精灵移动,碰撞检测完全针对上一帧的位置,逻辑完全不生效
- 死循环触发逻辑:当精灵已经处于碰撞状态时,while循环会不断把位置回退到碰撞前的坐标,碰撞状态始终不解除,程序直接卡死在循环里
- 回滚逻辑不合理:同时回滚两个精灵的位置,会出现双方同时按移动键时完全没法靠近、或者单方向移动也会被拽回的问题
修复方案
调整主循环的执行顺序为:「获取按键 → 保存两个精灵的上一帧坐标 → 执行两个精灵的移动逻辑 → 碰撞检测 → 碰撞时回滚位置 → 绘制画面」,同时把while循环改成单次if判断即可,一帧只需要做一次碰撞检测,回滚到上一帧位置就可以解除碰撞。
如果要更好的交互体验,也可以只回滚主动移动导致碰撞的精灵,最简单的通用实现是两个精灵各自回滚自己的上一帧位置,谁触发碰撞谁退回去。
修正后代码
import pygame from pygame.locals import * displayWidth = 500 displayHeight = 500 display = pygame.display.set_mode((displayWidth,displayHeight)) clock = pygame.time.Clock() class Player(pygame.sprite.Sprite): def __init__(self,image,x,y): super().__init__() self.image = image self.rect = self.image.get_rect() self.rect.x = x self.rect.y = y def move(self, keyPress,kup,kdown,kleft,kright): if keyPress[kup]: self.rect.move_ip(0,-5) if keyPress[kdown]: self.rect.move_ip(0,5) if keyPress[kleft]: self.rect.move_ip(-5,0) if keyPress[kright]: self.rect.move_ip(5,0) if self.rect.left < 0: self.rect.left = 0 if self.rect.right > displayWidth: self.rect.right = displayWidth if self.rect.top < 0: self.rect.top = 0 if self.rect.bottom > displayHeight: self.rect.bottom = displayHeight pygame.init() pygame.display.set_caption("Collision Detection") # 替换为自己的本地图片路径即可 sprite1 = Player(pygame.image.load("Images/down.png"),0,0) enemy1 = Player(pygame.image.load("Images/up.png"),100,100) player_list = pygame.sprite.Group() enemy_list = pygame.sprite.Group() player_list.add(sprite1) enemy_list.add(enemy1) gameRunning = True while gameRunning == True: for event in pygame.event.get(): if event.type == pygame.QUIT: gameRunning = False display.fill([0,0,0]) keyPress = pygame.key.get_pressed() # 先保存移动前的坐标 p_prev_x = sprite1.rect.x p_prev_y = sprite1.rect.y e_prev_x = enemy1.rect.x e_prev_y = enemy1.rect.y # 执行移动逻辑 sprite1.move(keyPress,K_UP,K_DOWN,K_LEFT,K_RIGHT) enemy1.move(keyPress,K_w,K_s,K_a,K_d) # 碰撞后回滚坐标 if pygame.sprite.collide_rect(sprite1,enemy1): sprite1.rect.x = p_prev_x sprite1.rect.y = p_prev_y enemy1.rect.x = e_prev_x enemy1.rect.y = e_prev_y # 最后绘制画面 player_list.draw(display) enemy_list.draw(display) pygame.display.flip() clock.tick(60) pygame.quit()
内容的提问来源于stack exchange,提问作者dianacx
相关产品推荐
相关产品推荐

