一、基本几何体表示:点、线段、射线、AABB、OBB、球体、胶囊体、凸包

各位同学,咱们今天聊聊碰撞检测的“地基”——基本几何体表示。你想想看,游戏里那些复杂的模型,最终都要拆解成这些基础形状来做碰撞检测。我做了这么多年物理引擎,可以说:几何体表示选得好,碰撞检测就成功了一半

1.1 点(Point)

点是最简单的几何体。就是一个位置,没有大小,没有方向。在代码里,通常就是一个三维向量。

struct Point {
    float x, y, z;
};

嗯,这里要注意:虽然点很简单,但它是所有复杂几何体的基础。我个人习惯把点看作“零维的物体”,它只回答“我在哪”,不回答“我有多大”。

避坑指南: 我曾经在项目中直接用点的位置去做碰撞检测,结果发现浮点精度问题导致点永远无法精确落在某个面上。后来我养成了一个习惯:给点加一个极小的半径(比如 1e-6),当作“小球”来处理。

1.2 线段(Line Segment)

线段由两个端点定义。说白了就是两点之间的连线。它和射线、直线的区别在于:线段有明确的起点和终点。

struct Segment {
    Point start;
    Point end;
};

线段在碰撞检测里用处很大。比如角色武器的攻击判定,或者绳索的碰撞,都可以用线段来近似。我记得有一次做格斗游戏,拳头的攻击轨迹就是用线段来检测的,效果还不错。

1.3 射线(Ray)

射线有一个起点和一个方向,无限延伸。它和线段的区别就是没有终点。

struct Ray {
    Point origin;
    Vector3 direction;  // 单位向量
};

射线最常见的用途就是“拾取检测”——玩家点击屏幕,从摄像机发射一条射线,看它和哪个物体相交。我建议你在做射线检测时,一定要把方向向量归一化,否则计算距离时会出问题。

注意: 射线是无限长的!如果你需要有限长度的检测,请使用线段。我曾经见过有人用射线做子弹检测,结果子弹穿墙了——因为射线没有长度限制。

1.4 AABB(轴对齐包围盒)

AABB 是 Axis-Aligned Bounding Box 的缩写。它的特点是:所有边都和坐标轴平行。定义方式很简单:用最小点和最大点。

struct AABB {
    Point min;
    Point max;
};

AABB 最大的优点是检测速度快。两个 AABB 是否相交,只需要比较 6 个数值。我做过测试,AABB 的相交检测比 OBB 快大约 3-5 倍。

但 AABB 也有缺点:如果物体旋转了,AABB 会变大,导致检测不精确。你想想看,一个长条形的物体旋转 45 度,它的 AABB 会膨胀很多。

特性 AABB OBB
检测速度 中等
旋转适应性
内存占用 小(6个float) 中等(15个float)

1.5 OBB(有向包围盒)

OBB 是 Oriented Bounding Box 的缩写。它和 AABB 的区别在于:OBB 可以旋转,能更紧密地包裹物体。

struct OBB {
    Point center;
    Vector3 axes[3];  // 三个轴方向
    float extents[3]; // 三个轴上的半长
};

OBB 的碰撞检测比 AABB 复杂。需要用到分离轴定理(SAT)。我个人习惯在需要精确碰撞的物体上使用 OBB,比如车辆的碰撞检测。

核心要点: OBB 的碰撞检测虽然慢一些,但精度高。如果你的物体经常旋转,用 OBB 比 AABB 更合适。

1.6 球体(Sphere)

球体是最简单的三维几何体。只需要圆心和半径。

struct Sphere {
    Point center;
    float radius;
};

球体的碰撞检测超级快:两个球心距离小于半径之和,就说明碰撞了。我经常用球体做“粗略检测”——先判断两个物体是否可能碰撞,如果可能,再用更精确的几何体去检测。

1.7 胶囊体(Capsule)

胶囊体可以理解为:一个线段加上两端各一个半球。它非常适合模拟角色的身体。

struct Capsule {
    Point start;
    Point end;
    float radius;
};

为什么用胶囊体?因为角色走路时,身体会上下晃动,如果用球体,会漏掉一些碰撞;如果用 AABB,又太不精确。胶囊体刚好平衡了速度和精度。

我记得在做一个 FPS 游戏时,角色的碰撞体从 AABB 换成胶囊体后,玩家反馈“终于不会卡在门框上了”。

1.8 凸包(Convex Hull)

凸包是这几种几何体里最复杂的。它用一组顶点来定义,并且保证这个形状是凸的——也就是说,形状内任意两点连线都在形状内部。

struct ConvexHull {
    vector<Point> vertices;
    vector<int> indices;  // 三角形索引
};

凸包的碰撞检测通常用 GJK 算法(Gilbert-Johnson-Keerthi)。这个算法很巧妙,它通过迭代计算两个凸包之间的最近距离。我建议初学者先掌握球体和 AABB,再慢慢啃凸包。

重要提醒: 凸包必须是凸的!如果你传入一个凹的形状,GJK 算法会给出错误结果。我曾经在这个坑里爬了整整两天。

知识体系总览

下面这张图展示了这 8 种几何体的关系和应用场景:

基本几何体知识体系 点 (Point) 线段 (Segment) 射线 (Ray) AABB (轴对齐包围盒) OBB (有向包围盒) 球体 (Sphere) 胶囊体 (Capsule) 凸包 (Convex Hull) 应用场景 粗略检测 → 精确检测 → 物理响应 拾取检测 | 子弹碰撞 | 角色碰撞 | 车辆碰撞 | 武器判定

从这张图可以看出,几何体从简单到复杂,应用场景也越来越丰富。我个人建议的学习路径是:先掌握点和球体,再学 AABB 和 OBB,最后攻克凸包。别贪多,一步步来。

我的经验: 在实际项目中,80% 的碰撞检测用球体和 AABB 就能搞定。只有那 20% 的“疑难杂症”才需要上凸包。所以别一开始就追求最复杂的方案,够用就好。

好了,这一章的内容就到这里。记住:几何体是碰撞检测的基石,选对了,后面的工作就顺了。


专注资料整理