一句话:把物体一层一层装进大盒子里。检测时先看大盒子,大盒子没碰到,里面的东西就全部跳过。
它属于上次说的粗检测,作用是:物体很多时,少检测一大半。
一、生活里的例子:找快递
快递员送一个包裹,不会挨家挨户敲门问"是不是你的",而是:
省 → 市 → 区 → 小区 → 楼 → 门牌号每走一层,就排除掉一大片。“不在这个省”,这个省里几千万户就都不用看了。
碰撞检测的层次结构就是这个思路。
二、游戏例子:一颗子弹,8 个敌人
没有层次结构
子弹要和 8 个敌人挨个检测,一共8 次。
有层次结构
先把敌人分组,每组套一个大框,大框再套更大的框:
[整个场景] / \ [左半边] [右半边] / \ / \ [左上] [左下] [右上] [右下] ● ● ● ● ● ● ● ●子弹飞在右下角,检测过程:
| 步骤 | 检测 | 结果 |
|---|---|---|
| 1 | 左半边的框 | ❌ 没碰到 →里面 4 个敌人全部跳过 |
| 2 | 右半边的框 | ✅ 碰到了 → 往里看 |
| 3 | 右上的框 | ❌ 没碰到 → 里面 2 个跳过 |
| 4 | 右下的框 | ✅ 碰到了 → 往里看 |
| 5、6 | 右下的 2 个敌人 | 精确检测 |
一共6 次,比 8 次少一点,看起来没省多少。
但物体一多,差别就很大
| 敌人数量 | 挨个检测 | 层次结构(大约) |
|---|---|---|
| 8 | 8 次 | 6 次 |
| 1000 | 1000 次 | 20 次左右 |
| 100 万 | 100 万次 | 40 次左右 |
因为每往下走一层,就排除掉一半。
三、两种常见的分法
1. 按"物体"分组:包围盒层次(BVH)
就是上面的例子:把靠得近的物体装进同一个框,框再装进更大的框。
┌───────────────────────────┐ │ ┌─────────┐ ┌────────┐ │ │ │ ● ● │ │ ● ● │ │ │ └─────────┘ └────────┘ │ └───────────────────────────┘ 框住物体,框的大小跟着物体走Unity 的 2D 物理(Box2D)、3D 物理(PhysX)内部用的都是这种。
2. 按"空间"切:四叉树
把地图切成 4 块,哪块东西多,就把那块再切成 4 块:
┌───────┬───────┐ │ │ ● │ ● │ │ ● ├───┼───┤ ← 右上角东西多,再切一次 │ │ ● │ │ ├───────┼───────┤ │ │ │ │ │ ● │ └───────┴───────┘3D 里每次切成 8 块,叫八叉树。
| BVH | 四叉树 | |
|---|---|---|
| 怎么分 | 按物体分组 | 按地图切块 |
| 适合 | 物体会移动 | 地图固定、物体分布不均 |
四、另一种层次:一个角色身上也分层
这个在游戏里非常常见——爆头判定。
┌───────────┐ ← 第 1 层:整个人的大框 │ ┌───┐ │ │ │ 头 │ │ ← 第 2 层:身体各部位 │ ├───┤ │ │ │ 身 │ │ │ ├───┤ │ │ │ 腿 │ │ │ └───┘ │ └───────────┘子弹先检测整个人的大框:
- 没碰到 → 头、身、腿都不用查
- 碰到了 → 再查打中哪个部位
| 打中部位 | 伤害倍率 |
|---|---|
| 头 | × 2 |
| 身体 | × 1 |
| 腿 | × 0.7 |
《CS》《使命召唤》这类射击游戏都是这么做的。
五、代码长什么样
核心只有一个规则:大框没碰到就 return,碰到了才往里看。
classNode{publicRectbox;// 这一层的大框publicNodeleft,right;// 下一层publicList<GameObject>objects;// 最底层才有:真正的物体}voidQuery(Nodenode,Rectbullet,List<GameObject>result){// 大框都没碰到,里面的全部跳过if(!node.box.Overlaps(bullet))return;if(node.objects!=null){// 到了最底层:把这里的物体交给"细检测"result.AddRange(node.objects);}else{// 还没到底:继续往下一层看Query(node.left,bullet,result);Query(node.right,bullet,result);}}六、要注意的坑
| 问题 | 说明 |
|---|---|
| 物体移动了 | 框要跟着更新,否则会漏检 |
| 框画得太大 | 很多框都"碰到",就排除不掉东西,变慢 |
| 物体很少 | 十几个物体直接挨个检测更简单,没必要建树 |
七、和你有什么关系?
- 用 Unity:引擎已经内置了,不需要自己写。场景里放几千个 Collider 也不卡,就是因为它。
- 自己写引擎 / 物体特别多(弹幕游戏、大量单位的 RTS):才需要自己实现。
顺便区分一下:Unity 编辑器里也有一个Hierarchy 窗口,那是管理物体"父子关系"的列表,和这里说的碰撞层次结构不是一回事。
一句话总结
层次结构 = 一层层装盒子。先查大盒子,没碰到就整组跳过,碰到了才往里看。物体越多,省下的检测越多。