Pygame自定义框架碰撞检测失效 物体穿透障碍物问题排查
Pygame自定义框架碰撞穿模问题排查与修复
核心问题原因
- 碰撞修正结果未同步:
PhysicsObject.move()方法处理碰撞时,仅修改了self.rect的碰撞盒位置,没有把修正后的坐标回写到self.x、self.y属性。下一帧计算位移时仍以旧坐标为基准累加速度,直接覆盖了碰撞阻挡的修正结果,最终导致穿模。 - 坐标锚点不统一:
Entity.display()渲染时用self.x-self.width/2、self.y-self.height/2作为绘制起点,默认把(x,y)当作实体中心点;但self.rect初始化时把(x,y)作为矩形左上角,造成视觉显示位置和实际碰撞盒位置完全错位,视觉上接触障碍物时碰撞判定还未触发。 - 语法与逻辑隐藏错误:
Entity.set_image()用image is str做类型判断不符合Python语法规则,后续加载图片资源会直接失效;blit_center()工具函数的坐标计算逻辑完全错误,无法实现居中绘制;set_size()修改实体尺寸后未同步更新碰撞盒大小,会出现尺寸变化后碰撞判定失效的问题。 - 主循环逻辑顺序错误:测试脚本中先执行绘制、再计算移动碰撞,会造成一帧的位置显示滞后。
修复方案
框架代码修复(gameFrame.py)
逐处修正上述问题:碰撞处理完成后同步坐标属性、统一坐标锚点为左上角(和Rect逻辑对齐)、修正错误语法与工具函数逻辑、补全尺寸修改后的碰撞盒同步,修复后完整代码如下:
import pygame def check_collisions(obj1, objects): collisions = [] for obj in objects: if obj.colliderect(obj1): collisions.append(obj) return collisions def blit_center(surface, target_surface, display): # 修正居中绘制计算逻辑 blit_x = target_surface.get_width()/2 - surface.get_width()/2 blit_y = target_surface.get_height()/2 - surface.get_height()/2 display.blit(surface, (blit_x, blit_y)) class PhysicsObject(object): def __init__(self, x, y, width, height, vel_x=0, vel_y=0): self.width = width self.height = height self.x = x self.y = y self.rect = pygame.Rect(self.x, self.y, self.width, self.height) self.velocity = pygame.Vector2(vel_x, vel_y) def move(self, environment): collisions = { 'top': False, 'bottom': False, 'left': False, 'right': False, } # 水平方向移动与碰撞检测 self.x += self.velocity.x self.rect.x = int(self.x) collisions_checked = check_collisions(self.rect, environment) for obj in collisions_checked: if self.velocity.x > 0: self.rect.right = obj.left collisions['right'] = True elif self.velocity.x < 0: self.rect.left = obj.right collisions['left'] = True # 同步修正后的x坐标 self.x = self.rect.x # 垂直方向移动与碰撞检测 self.y += self.velocity.y self.rect.y = int(self.y) collisions_checked = check_collisions(self.rect, environment) for obj in collisions_checked: if self.velocity.y > 0: self.rect.bottom = obj.top collisions['bottom'] = True elif self.velocity.y < 0: self.rect.top = obj.bottom collisions['top'] = True # 同步修正后的y坐标 self.y = self.rect.y return collisions class Entity(PhysicsObject): def __init__(self, x, y, width, height, vel_x=0, vel_y=0): super().__init__(x, y, width, height, vel_x, vel_y) self.surf = pygame.Surface((self.width, self.height)) self.surf.fill('red') self.colorkey = (0, 0, 0) self.surf.set_colorkey(self.colorkey) def set_image(self, image): # 修正类型判断语法 if isinstance(image, str): self.surf = pygame.image.load(image).convert_alpha() else: self.surf = image def set_color(self, color): self.surf.fill(color) def set_size(self, width=None, height=None): if width is not None: self.width = int(width) if height is not None: self.height = int(height) # 同步更新碰撞盒尺寸 self.rect.width = self.width self.rect.height = self.height def display(self, display): # 统一锚点为左上角,和碰撞盒坐标对齐 display.blit(self.surf, (self.x, self.y))
测试脚本调整
把移动碰撞的计算逻辑移到绘制之前,消除帧滞后问题,调整后主循环核心逻辑如下:
while True: # 主游戏循环 for event in pygame.event.get(): if event.type == pygame.QUIT: pygame.quit() sys.exit() display.fill([70, 120, 255]) # 绘制背景 keys = pygame.key.get_pressed() player.velocity.xy = 0, 0 if keys[K_w]: player.velocity.y = -5 if keys[K_s]: player.velocity.y = 5 if keys[K_a]: player.velocity.x = -5 if keys[K_d]: player.velocity.x = 5 # 先计算移动、碰撞,再执行绘制 player.move([obstacle.rect]) player.display(display) obstacle.display(display) pygame.display.update() clock.tick(60)
修改完成后运行测试,玩家移动到障碍物位置会被正常阻挡,不会出现穿模问题。
内容的提问来源于stack exchange,提问作者Luca-Fly
相关产品推荐
相关产品推荐

