做Python小游戏的人,多半会碰到同一个阶段:地图能画了,角色能动了,子弹能飞了,结果子弹穿过怪物身体的瞬间,心里一凉——“没打中?”或者更尴尬的,角色明明站在墙前面,却被弹到三米外。这些问题背后都指向同一个基础模块——碰撞检测。写游戏可以不追求极致的物理模拟,但碰撞检测如果写得不对、写得慢,整个游戏的体验会瞬间崩盘。这篇内容就围绕Python游戏里的碰撞检测实现展开,从最常见的AABB矩形检测,到圆形判定、像素级判定,再到空间分区优化,把2D游戏里真正用得上的方案一个个拆开讲清楚。
我默认你用的是Pygame,但里面讲到的数学和思路,换到Arcade、Panda3D甚至其他语言都完全通用。碰撞检测不是“能判断相交就行”的活儿,它同时牵扯到坐标系约定、提前判定、重叠修正和性能预算。这些点我会逐个展开,附上可直接抄的代码和踩坑记录,适合正在写小游戏、或者想给游戏加怪物交互、物理弹射、攻击判定但还没找到头绪的人参考。
1. 碰撞检测的数学基础:游戏物体到底是怎么“占地方”的
1.1 简化碰撞体的思路:不画像素,算形状
很多新手第一次写碰撞,想到的是“检查两张图片的像素有没有重叠”。这个思路没有错,但在2D游戏里,绝大多数情况根本不需要走到像素级那一步。原因很简单:像素级检测的计算量太大,一个100x100的角色每次碰撞都要比对上万像素,场景里同时存在几十个物体时性能瞬间崩盘。所以行业的通用做法是“近似”——用简单的几何形状来代替物体的真实轮廓,先做粗略判断,只在必要时才做精细判断。
最常见的三种替代形状:轴对齐矩形(AABB)、圆形、凸多边形。Pygame里的Rect对象本质就是矩形,天生适合AABB检测;圆形适合球体、角色、以中心点为基准的物体;凸多边形更复杂,但适合做旋转后的物体碰撞,在Python小游戏里用得相对少。这篇重点讲矩形和圆形,因为这两个覆盖了95%以上的2D需求。
1.2 坐标系与“碰撞发生”的判定本质
无论哪种形状,碰撞检测的本质都可以归结成一句话:判断两个数学区域是否存在交集。矩形和矩形相交,就是两个矩形的边界范围有没有重叠;圆和圆相交,就是两个圆心距离是否小于半径之和。
这里有个特别容易混淆的点:屏幕坐标系。在Pygame里,坐标原点在左上角,x轴向右,y轴向下。这意味着判断“两个矩形是否重叠”时,不能直接用中心点距离,而要分别比较x方向和y方向的区间。这也是很多新手第一个坑——用距离公式去判断矩形碰撞,结果在斜对角方向出现误判。矩形碰撞必须是分轴判断的,x轴满足条件且y轴满足条件,才算碰撞。
python复制def rects_collide(r1, r2):
# r1, r2 为 (x, y, width, height)
return (
r1[0] < r2[0] + r2[2] and
r1[0] + r1[2] > r2[0] and
r1[1] < r2[1] + r2[3] and
r1[1] + r1[3] > r2[1]
)
这个函数返回True代表碰撞。逻辑上就是从x和y两个轴分别检查区间是否重叠,而不是计算距离。理解了这一点,后面看Pygame的colliderect内部实现就不会觉得神秘了。
1.3 用Rect还是自己写坐标数学?
Pygame的Rect对象自带colliderect()方法,而且支持move()返回新矩形,内部性能也做过优化,绝大多数情况下直接用它就够了。我自己写项目时,90%的碰撞代码都是Rect.colliderect和Rect.collidepoint的排列组合。真正需要手写数学公式的场景是:没有用Pygame的图形库、自己管理坐标、或者要判断旋转矩形。
但无论用什么实现,理解上面的分轴判断逻辑都是必要的。因为一旦出现碰撞偏移、漏判、多判,最终都要回到最底层的数学去排查,而不是依赖某个API。
需要模型API调用? 免费领10W Token,多模型网关一键接入 Claude、DeepSeek 等主流模型。
2. AABB矩形碰撞实战:PyGame里的Rect用法与边界情况
2.1 Pygame Rect的常用碰撞API
Pygame的pygame.Rect有五个碰撞相关方法,我用了个遍,逐个说下适用场景:
| 方法 | 作用 | 常见用途 |
|---|---|---|
colliderect(rect) |
检测两个矩形是否重叠 | 子弹与怪物、玩家与墙壁 |
collidepoint(x, y) |
检测点是否在矩形内 | 鼠标点击选中、判定落点 |
collidelist(list) |
返回与列表第一个碰撞矩形下标 | 批量判断玩家撞到哪个障碍物 |
collidelistall(list) |
返回所有碰撞矩形下标列表 | 同时踩到多个触发器 |
collidedict(dict) |
与字典值检测碰撞,返回键 | 按对象ID管理碰撞组时很方便 |
实际项目里colliderect和collidelist用的最多。比如一个平台跳跃小游戏,玩家每一帧移动后,需要检测和所有地面、墙体的碰撞,写起来是:
python复制def move_player(player_rect, dx, dy, obstacles):
# x轴移动并检测
player_rect.x += dx
hit_list = player_rect.collidelistall(obstacles)
for index in hit_list:
rect = obstacles[index]
if dx > 0:
player_rect.right = rect.left
elif dx < 0:
player_rect.left = rect
