做Python游戏开发,绕不开的话题就是碰撞检测。我最早在Pygame里写一个小射击游戏,角色贴图已经撞上了敌机,血条却纹丝不动,后来才发现,是我把碰撞检测实现成了“两者中心点距离小于某个值”,忽略了矩形边界重叠才是最直观的判定方式。那次兜圈子排查让我意识到,碰撞检测看似简单,但牵扯到形状选型、坐标精度、响应处理、性能优化一系列问题。这篇东西我就把自己的实践过程、代码思路和踩过的坑一起整理出来,给正准备动手写Python游戏,或者对碰撞检测实现细节有疑问的朋友做个参考。
1. 先搞清楚碰撞检测到底在解决什么问题
很多人第一次接触碰撞检测,以为就是一个“判断两个物体是否相交”的函数。实际在游戏项目里,碰撞检测从来不只是一个几何判断,而是“检测”和“响应”两个环节的合称。搞混这两个环节,就会出现我开头说的那种情况:检测到了,但游戏没反应;或者反应了,但角色卡在墙里疯狂抖动。
1.1 检测只是第一步,响应才是游戏逻辑的开始
几何相交判断解决的是“是不是碰上了”,比如两个矩形是否重叠、两个圆是否相交。这一步的结果通常是布尔值,True或者False。但游戏需要的是“碰上了之后要干什么”,这就是碰撞响应。
举个最简单的例子,玩家角色撞到墙壁,检测结果返回True,接下来需要做的事是把角色从墙里推出去,同时把速度置零。如果只做检测不做响应,角色就会卡进墙里;如果响应做得不对,比如每次都把角色往反方向推一大截,就会出现“角色在墙边抖个不停”的经典Bug。
我这里整理了一个简单的处理流程,供参考:
python复制# 伪代码:碰撞检测 + 响应
if player.rect.colliderect(wall.rect):
# 1. 得到重叠区域
overlap = player.rect.clip(wall.rect)
# 2. 判断从哪个方向推出
if player.rect.centerx < wall.rect.centerx:
player.rect.x -= overlap.width
else:
player.rect.x += overlap.width
# 3. 速度清零
player.vx = 0
实际项目中,响应逻辑往往比这复杂,要考虑地面摩擦力、跳起状态、是否处于无敌帧等。但核心思路不变:检测负责“发现问题”,响应负责“解决问题”。
1.2 碰撞检测在游戏循环中的执行时机
一个标准的2D游戏循环长这样:处理输入、更新逻辑、检测碰撞、渲染画面。碰撞检测要放在物体移动之后、渲染之前,这个顺序基本是固定的。
我在初期犯过的错误是把移动和碰撞检测放在同一个update函数里,导致物体移动完了立刻检测,但物体速度太快时,一帧内可能从目标的一侧直接穿到另一侧,碰撞检测根本没机会触发,这就是“隧穿效应”。
隧穿效应的本质是离散时间步长带来的采样盲区。物体速度是每帧几十像素,两帧之间物体位置是跳变的,如果碰撞对象很薄,比如一颗子弹宽度只有4像素,而物体移动速度是每帧20像素,那子弹完全可能“跳过”目标。
处理隧穿有几个常用思路:
- 限制单帧最大位移,比如每帧位移不超过物体宽度的1/2;
- 把一帧拆成多个子步长,逐步移动、逐步检测;
- 用射线检测或者连续碰撞检测(CCD),专门处理高速物体。
对于大多数2D小游戏,限制单帧最大位移就够用了。我自己写平台跳跃游戏时,把所有物体的最大速度限制在每帧10像素以内,再配合固定帧率的游戏循环,极少出现隧穿问题。千万别一上来就上物理引擎,先用最简单的方式跑通,遇到问题再升级。
需要模型API调用? 免费领10W Token,多模型网关一键接入 Claude、DeepSeek 等主流模型。
2. 矩形碰撞的三种写法与精度死角
矩形碰撞是2D游戏里最常用的碰撞形状,原因无他,实现简单、计算量小、足够覆盖绝大多数视觉表现。但矩形碰撞也分好几种,选择不对,精度和性能都会出问题。
2.1 AABB:最朴素也最常用的矩形碰撞
AABB的全称是Axis-Aligned Bounding Box,意思是边与世界坐标轴对齐的包围盒。它要求矩形不能旋转,四条边分别平行于x轴和y轴。
判断两个AABB是否相交,只需要检查它们在x轴和y轴上的投影是否都重叠。这个逻辑用代码表达很简单:
python复制def aabb_collide(ax, ay, aw, ah, bx, by, bw, bh):
# ax, ay 是矩形A的左上角坐标,aw, ah 是宽高
# 判断x轴方向是否重叠
if ax + aw <= bx or bx + bw <= ax:
return False
# 判断y轴方向是否重叠
if ay + ah <= by or by + bh <= ay:
return False
return True
注意这里用的是“不重叠就返回False”的排除法,比直接写重叠条件更直观,也不容易漏掉边界情况。两个矩形刚好边缘相切时,重叠宽度为0,算不算碰撞要看游戏需求,我通常算碰撞。
2.2 用Pygame内置Rect还是手写
如果做的是Pygame项目,pygame.Rect自带colliderect方法,几行代码就能解决碰撞检测:
python复制rect1 = pygame.Rect(10, 10, 50, 50)
rect2 = pygame.Rec
