Skip to main content

Chapter 69: Collision Detection

碰撞检测(Collision Detection)解决三个连续问题:两个对象是否相交、它们在一个时间步内何时接触、接触发生在什么位置和法线方向。实时图形系统通常把这个问题放在物理模拟、角色控制、特效、动画和渲染同步之间处理。一个画面里看到的是角色模型、箱子、墙体、碎片和粒子,碰撞系统处理的则是 capsule、AABB、convex hull、triangle mesh、速度、层级过滤和接触点。

本章用一个贯穿帧作为材料:玩家角色以 capsule 形状在工业走廊中移动,场景包含静态墙体三角网格、可推动箱子、快速飞行弹丸、车辆轮胎、破碎碎片、布料条和粒子火花。读完本章后,读者应能把“穿墙、漏检、抖动、误碰、卡住、帧耗时升高”放回碰撞管线中的具体阶段,并能判断问题来自 primitive 选择、空间结构、连续碰撞、contact manifold、调试绘制或 profiling 证据。

核心结论是:稳定的实时碰撞检测是一条分阶段过滤管线。cheap primitive test 用来快速排除远离对象;broad phase 把候选对数量压到可处理规模;narrow phase 计算接触点、法线和穿透深度;continuous collision detection 覆盖高速运动;contact manifold 和 solver 决定后续位置修正与渲染结果。每个阶段都在牺牲一部分几何细节来换取可控成本,因此调试时要先确认哪一层丢失了必要信息。

碰撞检测和渲染的关系很直接:碰撞结果改变对象 transform,transform 再进入渲染 pass;调试绘制把 collision primitive、候选对、contact normal 和 TOI 输出到 overlay;profiling 说明 broad phase、narrow phase、CCD 和 solver 分别占用多少预算。图像问题经常从视觉端暴露,根因却位于几何代理、时间步或数据同步。

69.1 Collision Primitive Test Map

碰撞 primitive 是碰撞系统使用的简化几何代理。它的任务是用更少的参数表达可交互边界,并给碰撞管线提供可计算的距离、相交状态和接触信息。视觉网格可以有倒角、细孔和复杂材质,碰撞 primitive 通常只保留交互需要的轮廓。

在贯穿帧中,角色显示为带装备的高精度 mesh,碰撞层使用一个 capsule;墙体渲染为带管线和凹槽的模型,碰撞层使用静态 triangle mesh 或简化 convex pieces;箱子使用 OBB 或 convex hull;弹丸使用 sphere 或 swept sphere;粒子火花使用小 sphere 或 point radius。这个分离让渲染细节和物理成本分别受控。

Primitive状态数据典型测试输出质量适用位置
Spherecenter、radius中心距离平方与半径和比较法线稳定,接触点粗略弹丸、粒子、远距离代理
AABBmin、max三轴区间重叠计算极快,旋转后需要重新包围broad phase proxy、静态格子
OBBcenter、axis、extentSeparating Axis Theorem(SAT)投影测试贴合旋转盒,轴测试更多箱子、车辆部件、可旋转道具
Capsulesegment、radius线段到线段或线段到点距离角色滑动稳定,端部平滑角色控制器、骨骼肢体、管道
Convex Hull顶点、面、支持函数GJK 距离测试,EPA 求穿透能覆盖复杂凸形,接触生成成本较高碎片、石块、简化道具
Triangle Mesh顶点、索引、局部加速结构三角形级相交或距离测试几何贴合度高,动态成本高静态关卡、地面、墙体

Sphere 的工作方式最简单。两个 sphere 相交时满足 c0c12(r0+r1)2|c_0 - c_1|^2 \leq (r_0 + r_1)^2。这里的 c0c_0c1c_1 是两个中心点,r0r_0r1r_1 是半径。平方距离比较可以减少平方根开销,法线可以由中心差归一化得到。这个测试适合弹丸、粒子和远距离 LOD 代理,因为它对旋转完全不敏感。

AABB(Axis-Aligned Bounding Box,轴对齐包围盒)用世界坐标轴上的三个区间表示对象范围。两个 AABB 在 x、y、z 三个轴上都存在重叠时,候选对象才进入后续测试。AABB 的优势在于计算和缓存布局简单,代价在于旋转对象需要每帧更新世界空间包围盒。把箱子的 OBB 转成更大的 AABB 用于 broad phase,是常见做法。

OBB(Oriented Bounding Box,有向包围盒)携带自身坐标轴,能更贴合旋转盒状物。OBB 测试常使用 SAT。SAT 的判断方式是寻找一条分离轴;只要某条候选轴上的两个投影区间分离,两个凸体就分离。三维 OBB 对 OBB 需要检查两个盒子的面法线和轴叉乘方向。它比 AABB 更精确,也需要更多 dot 运算和边界处理。

Capsule 可以看成一条线段加半径。角色使用 capsule 的原因是底部和顶部平滑,贴墙移动时接触法线连续,跨过小台阶时比 box 更稳定。capsule 与墙面、台阶、斜坡的测试通常转化为线段到三角形、线段到线段或线段到点的距离。角色在走廊中沿墙滑动时,capsule 的接触法线会被控制器用来消除朝墙速度分量,再保留沿墙切向速度。

Convex hull 用一组平面或顶点表达任意凸形。Gilbert-Johnson-Keerthi(GJK)算法用支持函数寻找两个凸体 Minkowski 差是否包含原点;Expanding Polytope Algorithm(EPA)常用于在相交后估算穿透深度和法线。它适合碎片和不规则道具,因为凸形能比 box 贴合更多轮廓。concave 模型通常拆成多个 convex hull,再由 compound shape 组合。

Triangle mesh 提供最高几何贴合度,适合静态关卡。它的成本来自三角形数量和动态更新。墙体、地面、楼梯和大型不可移动结构可以预构建 BVH 或其他三角形加速结构;可移动 concave 物体通常用 convex decomposition 或多个 primitive 组合。把动态碎片直接作为 triangle mesh 参与 narrow phase,会让候选对和接触生成成本迅速升高。

primitive 选择的判断顺序可以固定为四步:先看对象是否需要真实轮廓接触,再看它是否频繁移动或旋转,再看接触法线是否会影响角色或车辆手感,最后看 narrow phase 的预算。弹丸优先 sphere 或 capsule sweep;角色优先 capsule;普通箱体优先 OBB 或 convex hull;静态复杂场景优先 triangle mesh 加局部加速结构;粒子和火花优先 sphere radius。

这一节的关键是把可见模型和碰撞代理分开判断。画面里的箱子有倒角和贴图,碰撞系统看到的是 OBB 或 convex hull;画面里的角色有手臂、背包和鞋底,角色移动系统通常看一个 capsule;画面里的管线有细节孔洞,角色碰撞可能只看简化墙体。调试时先把这些代理画出来,很多“误碰”和“漏检”会直接暴露。

69.2 基于空间结构的碰撞加速技巧

空间结构解决的是候选对规模问题。若场景中有 nn 个可碰撞对象,直接两两测试会产生约 n(n1)/2n(n-1)/2 个组合。实时系统需要在每个 simulation step 中快速找出“可能接近”的对象对,再把这些候选交给 narrow phase。这个阶段称为 broad phase,输出应保持保守:它可以多给一些候选对,但要尽量降低漏掉真实接触的概率。

在贯穿帧里,角色、箱子、弹丸、碎片和粒子同时运动。角色只需要和附近墙体、地面、箱子、可交互机关测试;弹丸只需要和穿越路径附近的敌人、墙体和可破坏道具测试;粒子火花通常只和少数大型碰撞体或 SDF 场交互。空间结构把“全场对象”变成“局部候选对”。

Sweep and prune 使用轴向区间排序来寻找 AABB 重叠。每个对象在某条轴上有起点和终点,排序后扫描重叠区间,三个轴都重叠的对象对进入候选集合。它利用 temporal coherence:连续帧之间对象位移通常较小,排序列表接近有序,增量更新成本较低。车辆、箱子和角色在固定走廊中移动时,sweep and prune 能保持稳定性能。

BVH(Bounding Volume Hierarchy,包围体层次结构)把对象或三角形组织成树。每个节点保存一个包围体,遍历时先测试父节点,父节点分离时整棵子树被裁剪。静态关卡 triangle mesh 适合预构建 BVH;动态碎片可以使用 refit 更新节点包围体;变化剧烈时需要 rebuild 或局部重建。BVH 的价值在于一次分离测试能排除一组空间上聚集的三角形或对象。

Uniform grid 把空间切成固定大小的格子。对象写入覆盖的格子,同格或邻近格对象生成候选对。它适合大量尺寸接近、分布较均匀的对象,例如粒子、碎石和简单群体单位。格子尺寸过大会让每格候选过多,尺寸过小会让一个大对象覆盖大量格子。grid 的核心参数是 cell size,它应和主要对象半径或移动距离建立关系。

Spatial hashing 是稀疏 grid 的工程形态。它把三维格子坐标映射到 hash table,只存储实际被对象占用的格子。开放世界、稀疏粒子和大范围 projectile query 适合这种结构。代价是 hash 冲突、内存分配和跨格对象去重。工程实现中通常把 pair key 规范化,例如 (minId, maxId),再用 pair cache 去重和复用。

Pair cache 保存上一帧或上一 step 已经接近的对象对。它能减少重复生成、维持 resting contact 稳定,并帮助 solver 复用 contact point id。角色站在箱子上时,角色 capsule 与箱子 OBB 的 pair 会连续存在;缓存让接触点在多帧中保持一致,减少法线跳变和位置抖动。缓存同时需要失效规则,proxy 分离、对象休眠、层级变化或销毁时要清理对应 pair。

下面的图描述 broad phase 和 narrow phase 的分工边界。图中 broad phase 只承诺“候选可能相交”,精确接触由后续阶段计算。

这条路径的关键是 World AABB。无论对象内部使用 sphere、capsule、OBB、convex hull 还是 mesh,broad phase 通常先拿到世界空间 AABB 作为 proxy。Layer filter 在空间结构前后都能使用:角色层和装饰粒子层可以直接跳过,弹丸层只和命中目标层、墙体层、可破坏层交互。过滤越早,空间结构和 pair cache 承担的压力越小。

选择空间结构时,先看对象分布,再看运动模式,最后看更新成本。固定室内关卡和静态三角形使用 BVH;大量均匀小对象使用 grid 或 spatial hashing;位置变化平滑的刚体集合使用 sweep and prune;混合场景常把静态世界和动态对象分开维护,再在查询阶段组合候选。复杂引擎通常同时使用多种 broad phase,按层级和对象类型分桶处理。

GPU 参与 collision query 时,要额外确认数据回传路径。GPU compute 可以并行生成 AABB、grid bin 或候选对,但 CPU solver 如果需要同步读取结果,就会引入 fence 和 readback 等待。渲染型粒子碰撞可以把检测、响应和绘制都放在 GPU buffer 中完成;角色和刚体 gameplay 通常需要 CPU 可见结果。判断 GPU broad phase 是否合适,先看结果消费端在 CPU 还是 GPU。

69.3 连续碰撞检测与细粒度处理

离散碰撞检测在每个 step 的起点或终点采样对象状态。高速物体在两个采样点之间穿过薄墙时,终点可能已经越过墙体且没有重叠,这种现象叫 tunneling。连续碰撞检测(Continuous Collision Detection,CCD)把对象在时间步内的运动也纳入测试,目标是求出首次接触时间,即 time of impact(TOI)。

在贯穿帧中,角色正常移动可以用离散 capsule 测试,因为每帧位移相对半径较小;弹丸速度远高于半径和门板厚度,离散 sphere 测试容易漏掉命中;快速下落碎片撞到薄钢板时也会出现类似问题。CCD 的使用对象应集中在高速、细小、命中结果重要的对象上。

TOI 通常把时间步写成 t[0,1]t \in [0, 1]t=0t=0 表示当前状态,t=1t=1 表示 step 结束状态。CCD 要回答:在这个区间内,两个运动形状的距离何时第一次达到零或达到接触阈值。对 sphere 和 capsule,可以用 swept sphere、segment sweep 或 shape cast 建模;对 convex hull,可以使用 conservative advancement 或凸体 cast;对 triangle mesh,通常先让 swept AABB 通过 BVH 找候选三角形,再对候选三角形做细测。

Swept volume 是把运动轨迹扫过的区域当成查询体。一个 sphere 沿直线移动时,扫掠体近似 capsule;一个 box 移动时,扫掠体会形成更复杂的凸体。工程中常用 shape cast 代替显式构造 swept volume:输入起点、方向、距离和形状,输出最近命中距离、法线和命中对象。角色移动的 capsule sweep、子弹的 sphere sweep、车辆轮胎的 raycast 或 wheel sweep 都属于这类思路。

Substep 是把一个大时间步拆成多个小时间步。它降低单次位移,能改善高速物体、堆叠刚体和约束系统的稳定性。substep 的成本与次数近似线性增长,并且会增加 broad phase 更新和 solver 迭代次数。固定帧率物理可以使用固定 dt 加 accumulator;渲染帧率变化时,用插值后的 render transform 展示物理状态,能减少画面抖动。

CCD 的预算应按影响结果分配。弹丸命中墙体决定 gameplay,优先启用 CCD;角色 dash 穿过薄门影响手感,可以用 capsule sweep;微小粒子火花穿过细杆通常只影响视觉,可以使用 depth buffer、SDF 或低成本 proxy;大量碎片同时爆炸时,可以只给高速大碎片启用 CCD,并对小碎片使用多步离散测试。

细粒度处理还包括初始重叠、接触阈值和法线稳定。若对象 step 开始时已经穿透,TOI 查询本身无法给出“首次接触”之前的时间,需要先做 depenetration 或把 pair 交给穿透修正。接触阈值太小会让物体在斜坡和台阶上反复失去接触;阈值太大又会产生过早命中。角色 capsule 贴墙移动时,通常会保留一个小 skin width,让查询提前返回并把对象保持在安全距离。

Tunneling 的排查顺序可以固定为:先计算单 step 位移与目标厚度、对象半径的比例;再确认 broad phase 使用的是终点 AABB 还是 swept AABB;接着检查 narrow phase 是否执行 shape cast 或 TOI;最后看响应是否把对象移动到 TOI 位置并裁剪剩余速度。只看到终点 transform 无法解释高速漏检,必须把时间区间画出来。

69.4 高效碰撞检测管线实现

高效碰撞管线要把数据组织成可维护的阶段。输入是 body transform、velocity、collision shape、collision layer、material 和 activity state;输出是 contact manifold、hit event、trigger event、debug primitive 和 profiler counter。每一层都应有明确输入输出,这样图像端看到异常时才能回溯。

Collision layer 是最早的成本控制点。它用 bit mask 表达“哪些类型可以互相碰撞”。角色层可以和静态场景、动态箱子、触发器交互;装饰粒子层可以只和少数大型场景代理交互;布料层可以和角色 capsule、场景 SDF、指定碰撞体交互。layer filter 的判断应在 broad phase candidate 生成前尽量执行,在 narrow phase 前再次执行以处理运行时状态变化。

Narrow phase 接收候选对,并调用与 primitive 组合对应的测试函数。sphere-sphere、capsule-triangle、OBB-OBB、convex-convex、mesh-convex 都是不同路径。输出应包含是否接触、contact normal、penetration depth、contact points、feature id 和 material pair。feature id 用来追踪接触点来自哪个面、边或顶点,pair cache 和 solver 会用它保持多帧稳定。

Contact manifold 是同一对象对的接触点集合。一个箱子落在地面上时,单个接触点会导致旋转不稳定,多个接触点可以形成支撑面。manifold 通常限制点数,例如保留 2 到 4 个代表性 contact points,并维护每个点的 normal、depth、local position 和 warm-start impulse。它是 narrow phase 和 solver 之间的接口。

下面的简化 C++ 风格伪代码展示管线分层。代码只表达数据流,具体 shape test 和 solver 细节由引擎实现决定。

void StepCollision(World& world, float dt) {
for (Body& body : world.activeBodies) {
body.worldAabb = ComputeWorldAabb(body.shape, body.transform);
body.sweptAabb = ExpandByMotion(body.worldAabb, body.velocity * dt);
world.broadPhase.UpdateProxy(body.id, body.sweptAabb, body.layerMask);
}

CandidatePairs pairs = world.broadPhase.BuildPairs(world.layerMatrix);
world.contacts.BeginFrame();

for (const Pair& pair : pairs) {
const Body& a = world.GetBody(pair.a);
const Body& b = world.GetBody(pair.b);

if (!CanCollide(a, b, world.layerMatrix)) {
continue;
}

ContactManifold manifold = TestShapes(a, b, dt);
if (manifold.pointCount > 0) {
world.contacts.Store(pair, manifold);
}
}

world.solver.Resolve(world.contacts, dt);
world.debugDraw.Submit(world.contacts, world.broadPhase.DebugStats());
}

这段代码有三个关键点。第一,broad phase 使用 sweptAabb,让高速物体在一个 step 内穿过的区域也进入候选。第二,layer filter 在 broad phase 和 narrow phase 都出现,保证运行时切换层级时仍能收敛。第三,ContactManifold 独立保存,solver、event system 和 debug draw 都从同一份结果读取,减少各系统对碰撞细节的重复推断。

完整管线可以按以下路径理解:

Render Transform 在这里位于 solver 之后。渲染系统最终看到的位置应来自物理更新后的 transform,或者来自物理 transform 的插值结果。若渲染直接使用输入 transform,就会出现“物理已经挡住,画面仍穿过去”的视觉错位。网络同步、动画 root motion 和物理 transform 共同驱动角色时,也要明确最终写入顺序。

Debug draw 应覆盖三类信息。第一类是 primitive:角色 capsule、箱子 OBB、墙体 mesh proxy、弹丸 swept path。第二类是 pair:broad phase candidate、narrow phase confirmed contact、trigger pair。第三类是 contact:contact point、normal、penetration depth、TOI fraction。颜色和图例应固定,调试者才能在视频录制中追踪同一类问题。

Profiling counter 应按阶段记录。常用计数包括 active body 数、proxy update 数、broad phase pair 数、narrow phase test 数、CCD cast 数、contact manifold 数、solver island 数、solver iteration 数和每阶段耗时。复杂场景出现掉帧时,先看 pair 数是否突增;pair 数稳定而耗时上升时,再看 narrow phase primitive 组合是否变重;CCD cast 数突增时,检查高速对象阈值和层级过滤。

管线实现的稳定性来自固定边界。shape 只负责几何查询;broad phase 只负责候选;narrow phase 只负责接触数据;solver 只负责响应;debug draw 只读取状态。模块边界稳定后,角色、车辆、碎片、布料和粒子可以共享同一套证据结构,各系统只扩展自己的 shape、layer 和响应策略。

69.5 复杂场景物体碰撞调试策略

复杂场景的碰撞问题通常表现为视觉症状:角色在墙角抖动,车辆轮胎陷入地面,碎片在斜坡上弹飞,布料穿过角色手臂,粒子火花从墙面背后出现,弹丸穿过薄门。调试要把症状映射到管线阶段,而非直接修改最终位置。最终位置只是结果,primitive、候选对、接触法线、TOI 和 solver 才是根因入口。

角色问题优先看 capsule 与场景代理。角色卡在门框上时,先画出 capsule 半径、skin width、脚底位置和地面法线。若 capsule 过宽,视觉模型看似能通过,碰撞代理会被门框挡住;若台阶检测没有把小高度差转成 step up,角色会在低边缘抖动;若墙面 triangle normal 跳变,滑动方向会在相邻三角形之间来回变化。稳定做法是用简化场景碰撞、连续 capsule sweep 和接触法线合并。

车辆问题优先看轮胎查询和悬挂方向。许多车辆系统使用 raycast wheel 或 wheel sweep 判断地面距离、法线和摩擦。轮胎陷入地面时,检查 wheel query 是否命中渲染地面对应的 collision layer,悬挂 rest length 是否与模型比例一致,地面 triangle 法线是否平滑,车体底盘是否与地面产生额外接触。车辆调试 overlay 应同时画出轮胎半径、悬挂线、命中点和法线。

碎片问题优先看 primitive 复杂度和 pair 数。破碎系统会在短时间内生成大量动态 convex pieces,broad phase pair 数和 narrow phase test 数可能一起上升。碎片在地面上 jitter 时,检查 convex hull 是否过小、center of mass 是否合理、contact manifold 是否保留多个支撑点、sleep threshold 是否过低。碎片只是视觉装饰时,可以降低碰撞层级、减少参与 solver 的数量,或者在远处切换到只渲染的粒子代理。

布料和粒子问题优先看近似模型。布料通常使用粒子、边、三角面和约束;与角色交互时常用 capsule、sphere 或 SDF 代理。布料穿过手臂时,检查角色骨骼 capsule 是否随动画同步更新,布料 substep 是否覆盖最大位移,碰撞半径是否匹配视觉厚度。粒子火花和烟尘通常使用低成本 sphere、depth collision 或 SDF 采样,调试重点是碰撞场是否与渲染相机、世界坐标和时间步一致。

漏检和抖动可以用同一套表定位。

视觉症状优先检查阶段关键证据常见修正方向
高速弹丸穿过薄墙CCDswept path、TOI、swept AABB启用 sphere sweep,扩大候选范围,按 TOI 裁剪位移
角色在墙角抖动Narrow phase / solvercapsule、contact normals、penetration depth合并法线,调整 skin width,简化墙角代理
箱子站不稳Contact manifoldcontact point 数、feature id、warm start保留支撑点,稳定 manifold 裁剪
粒子碰撞拖慢帧Broad phase / layerpair count、grid occupancy、layer mask调整 cell size,减少交互层,使用 GPU 局部碰撞
布料穿过角色Animation sync / substepbone capsule、cloth dt、relative velocity同步代理,增加布料子步,扩大碰撞半径
车辆陷入地面Wheel querywheel ray、hit normal、suspension length调整查询长度,检查地面层,平滑地面法线

冻结一帧是高效调试的起点。记录 simulation state、random seed、输入事件和 dt,让同一帧可以重复运行。随后打开 debug draw:先看 primitive 是否包住视觉对象,再看 broad phase 是否生成目标 pair,再看 narrow phase 是否输出 contact,再看 solver 是否应用修正。这个顺序能把“画面感觉不对”转成可验证数据。

坐标和时间同步要放在早期检查。渲染模型可能带有骨骼偏移、root motion、缩放和插值;物理 body 可能使用固定 step 和上一帧 transform。若视觉模型和 collision shape 的世界矩阵不同步,所有几何测试都会看似随机。排查时画出渲染骨架、物理 capsule、velocity vector、上一帧位置和当前 step 目标位置,先确认它们处在同一坐标空间和同一时间点。

性能调试要从数量级入手。复杂场景中最常见的成本上升来自 pair explosion:一个大 AABB 覆盖过多 grid cell,一个爆炸生成数百碎片,一个布料自碰撞产生大量候选,一组粒子进入同一狭小空间。先看 broad phase pair 数,再看 narrow phase primitive 组合,再看 CCD cast 和 solver island。计数器比单次耗时更稳定,因为耗时会受硬件、线程调度和工具采样影响。

本章的最终判断方式可以收束为一条链:视觉症状 → collision primitive → broad phase 候选 → narrow phase contact → CCD 时间区间 → solver 响应 → render transform。每次碰撞排查都沿这条链前进,找到第一处证据缺失或不稳定的位置,再修改对应阶段。这样处理后,角色、车辆、碎片、布料和粒子可以在同一套工程语言中分析。

最小自检任务

给定一个实时场景:玩家 capsule 半径为 0.45 米,以 7 米每秒向前冲刺;走廊中有厚度 0.08 米的金属门板、三个可推动箱子、一个 70 米每秒的 sphere 弹丸、若干小碎片和布料旗帜。画面中出现三个问题:弹丸穿过门板,角色在两个箱子夹角处抖动,碎片爆炸时碰撞阶段耗时从 0.4 毫秒升到 4 毫秒。请按本章管线说明优先检查哪些数据,并给出每个问题最可能的修正方向。

答案要点

弹丸穿过门板时,先计算单 step 位移和门板厚度的比例,再画出弹丸 swept path 和 swept AABB。70 米每秒的 sphere 在 60 FPS 下单 step 位移约 1.17 米,远大于 0.08 米门板厚度,应使用 sphere sweep 或 CCD TOI,并让 broad phase 使用 swept AABB 生成候选。响应阶段要把弹丸移动到 TOI 命中位置,再触发命中事件或反弹逻辑。

角色在两个箱子夹角处抖动时,先画出角色 capsule、箱子 OBB、contact normal、penetration depth 和每帧速度。若两个箱子的接触法线在相邻帧来回切换,控制器会反复修正到不同方向。修正方向是合并稳定法线,保留 skin width,限制每帧 depenetration,必要时用角色专用 sweep 和滑动逻辑处理夹角。

碎片爆炸导致碰撞耗时升高时,先看 active body 数、broad phase pair 数、narrow phase test 数、CCD cast 数和 solver island 数。若 pair 数突然上升,检查碎片 AABB 是否过大、grid cell size 是否失配、碎片层是否与过多对象交互。修正方向是降低装饰碎片碰撞层级,限制参与 solver 的碎片数量,远处碎片切换为只渲染代理,或者把大量小碎片改成粒子近似碰撞。

本章知识点总结

  • 碰撞定义:碰撞检测要回答是否相交、何时接触以及接触点和法线位置。
  • 代理分离:渲染 mesh 和 collision primitive 应分开设计,交互需求决定碰撞形状。
  • Primitive 选择:sphere、AABB、OBB、capsule、convex hull 和 triangle mesh 分别对应不同精度、稳定性和成本。
  • Capsule 角色:capsule 的端部平滑,适合角色贴墙滑动、台阶接触和动画代理。
  • 静态 Mesh:triangle mesh 适合静态关卡,动态复杂物体更适合 convex decomposition 或 primitive 组合。
  • Broad Phase:broad phase 用保守 proxy 生成候选对,把全量两两测试压到局部对象集合。
  • 空间结构:sweep and prune 利用时间连续性,BVH 适合层次裁剪,grid 和 spatial hashing 适合大量局部对象。
  • Pair Cache:pair cache 复用上一帧接近关系,能提升 resting contact 和 solver warm start 的稳定性。
  • CCD 目标:连续碰撞检测把时间区间纳入测试,用 TOI 处理高速物体 tunneling。
  • Swept 查询:sphere sweep、capsule sweep、shape cast 和 swept AABB 是高速对象进入候选和命中的关键路径。
  • Contact Manifold:contact manifold 保存接触点、法线、深度和 feature id,是 narrow phase 与 solver 的接口。
  • Layer Filter:collision layer 应尽早过滤无关交互,减少 broad phase、narrow phase 和 solver 压力。
  • Debug Draw:调试绘制要同时覆盖 primitive、candidate pair、contact normal、TOI 和速度方向。
  • Profiling 顺序:性能排查先看 pair 数,再看 narrow phase 组合、CCD cast 和 solver island。
  • 排查链路:视觉症状应沿 primitive、候选对、接触数据、时间区间、响应和 render transform 逐层定位。