
不少做游戏的朋友问过我模型碰撞到底该用哪种类型说实话很多项目的碰撞问题最后追根溯源都不是碰撞器加没加对而是一开始选错了碰撞类型。这个问题在刚开始做3D项目时特别常见而且坑得很隐蔽。今天我就把这几年在Unity、Godot这些引擎里折腾碰撞系统的经验捋一捋讲讲模型碰撞里最常见的几种碰撞类型、它们各自的使用边界以及我在实际项目里踩过的那些典型的坑。这篇文章既适合刚接触物理系统的新手也给已经做了几个Demo但总在碰撞排查上浪费时间的朋友一个系统的排查思路。1. 碰撞类型的底层分工先搞懂Collider和Rigidbody谁说了算在讨论具体碰撞器类型之前我建议先把一个基本概念理清楚碰撞这件事从来不是Collider单独完成的它必须和Rigidbody配合。很多人出错就是因为把Collider当成模型的物理替身以为挂上碰撞器就万事大吉但实际运行时物体直接穿过了墙壁或者碰撞事件压根不触发。1.1 静态碰撞体与动态碰撞体的分界线在Unity里一个只有Collider、没有Rigidbody的物体叫静态碰撞体。它适合做地面、墙壁、障碍物这些不动的环境物体。而一个既有Collider又有Rigidbody的物体叫动态碰撞体它才真正参与物理模拟会受到重力、可以被力推动、能和其他物体发生碰撞响应。这两者的分界线非常关键。静态碰撞体理论上不应该移动——如果你在代码里直接改静态碰撞体Transform的position物理引擎不会自动更新它的内部加速结构严重时会出现碰撞区域和模型显示位置错位的问题。我见过有同学做移动平台功能直接在Update里改一个没有Rigidbody的方块的位置结果人物站上去纹丝不动就是这个原因。![注意]动态移动的平台、门、电梯这类物体必须挂Rigidbody哪怕你不需要它受重力也要把gravityScale或useGravity关掉让它保持动力学状态。这样可以确保物理引擎每帧正确更新碰撞体位置。1.2 Trigger模式只管检测不管阻挡Collider上还有个很容易理解的选项叫做isTrigger。普通碰撞是检测阻挡两个效果同时发生而Trigger只负责检测不产生任何物理阻挡。这个设计非常有用玩家靠近NPC可以触发对话、武器挥过敌人可以判定伤害但角色不能被一个不可见的气墙挡住。我在做关卡设计时经常把关卡边界空气墙和警戒区域用Trigger来做。空气墙用普通碰撞器警戒区域用Trigger配上OnTriggerEnter监听进入事件。这两个场景本质上都是不可见的区域判断但物理意义上完全不同一旦混用就会出现角色能走进去但用了Trigger却撞了个透明墙这种怪问题。1.3 物理材质碰撞类型之外的摩擦与弹性说完了Collider本身还要提一个跟碰撞类型紧密相关、但经常被忽略的东西物理材质。Collider决定了碰撞的形状物理材质决定了碰撞发生之后手感的摩擦系数和反弹系数。做动作游戏的朋友应该很有感触一个角色在斜坡上的滑动速度、一个球在碰撞后的弹跳高度这些都不是碰撞器类型能管的需要调物理材质的dynamicFriction动摩擦、staticFriction静摩擦和bounciness弹性参数。如果角色在斜坡上一直打滑大概率不是碰撞器选错了而是物理材质摩擦系数设置太低。2. 三类基础碰撞器的选型逻辑Box、Sphere、Capsule的边界与性能Unity的基础碰撞器类型在设计上非常精炼Box Collider、Sphere Collider、Capsule Collider。这三个基本形状覆盖了90%以上的碰撞需求而且它们的几何计算成本极低物理引擎做碰撞检测时最快最稳定。但问题在于很多人选形状的时候没想过为什么要这样选。2.1 三种基础碰撞器参数与适用场景对比我在实际项目里面对不同物体时心里基本有一个清晰的选型标准整理成表格方便大家参考碰撞器类型核心参数典型应用性能开销注意事项Box ColliderSize长宽高、Center中心偏移箱子、墙体、门、柱子、平台极低旋转后会扩大碰撞范围吗其实不会AABB在旋转后有计算修正但过大的旋转结合非均匀缩放会有误差Sphere ColliderRadius半径、Center球类、子弹、金币、小范围收集物极低半径是标量无法模拟非均等拉伸的形状Capsule ColliderHeight、Radius、Direction角色身体、敌人主体、摇杆碰撞极低方向X/Y/Z轴需和角色模型朝向一致否则碰撞体竖躺在地面上Box和Sphere是最便宜的碰撞计算Capsule本质上是一个圆柱加两个半球端点计算量也很小。选择优先级的顺序我一般是这样能用Sphere不用Capsule能用Capsule不用Box也不完全对——应该看形状匹配度。形状匹配度高代表物理表现自然而不是性能最优。2.2 角色碰撞为什么普遍选Capsule而不是Box很多刚入门的朋友会问我的角色模型明明是个人形直接用Mesh Collider包一个准确的碰撞体不行吗为什么都推荐用Capsule主要原因有两个。第一Capsule对角色运动的表达是对称且平滑的。Box边角明显角色在墙边移动时会因为Box的平面导致转角处卡顿而Capsule没有角沿墙移动时非常顺滑。第二Capsule的参数是Height和Radius这两个值在角色控制器里语义非常清晰Radius管能不能挤过这个缝隙Height管头顶会不会撞到天花板。你调Box的Size也能达到类似效果但语义不直观调起来容易糊。我在做第三人称控制器的时候Capsule的Height中心点一般放在角色骨盆位置或者稍微偏下Radius则根据角色肩宽决定。做FPS时Capsule会稍微高瘦一些用来减少从掩体后探出时被击中胸部区域的视觉偏差。2.3 判定手感与碰撞器大小的微妙偏差碰撞器大小不一定要和模型视觉100%重合这个偏差恰恰是做手感的地方。比如在动作游戏里玩家攻击敌人的判定我会让敌人的Capsule比视觉模型略大一圈这样玩家攻击更容易命中打击感会明显变好。反过来玩家角色自身的碰撞器要比视觉模型略小一圈尤其是有冲刺闪避技能的游戏小一点的碰撞器可以减少被边缘卡住或者被敌人判定命中的概率体感上会觉得操作更自由、更不容易受虐。这种碰撞比视觉大/小10%左右的经验值属于非常实用的调参技巧。但它也有副作用如果偏差太大玩家会明显看到模型悬空或陷入墙里的穿模现象。我一般把偏移控制在5%-10%以内视觉上基本无感。3. 复杂模型碰撞的两种路线网格碰撞器与复合碰撞组合树桩、岩石、雕塑、车辆外壳——这些形状不规则的物体该怎么加碰撞大部分人的第一反应是加Mesh Collider这没问题但这里藏着项目性能的大坑必须小心使用。3.1 Mesh Collider的正确使用姿势与Convex开关Mesh Collider的美妙之处在于它直接用模型的三角面片做碰撞检测碰撞精度最高。但代价也最明显碰撞检测的计算量跟模型面数直接挂钩。一个几万面的高模做碰撞体哪怕是静态物体也会让物理引擎每帧的碰撞阶段耗时成倍上升。更重要的是Unity中非Convex的Mesh Collider不能和刚体一起做动态碰撞。也就是说默认情况下你只能把Mesh Collider挂在静态环境物体上不能作为动态物体飞来飞去的碰撞体。如果你想让它参与动态物理模拟必须勾选Convex凸包选项。但勾选Convex后碰撞形状会变成模型外轮廓的凸包凹进去的部分比如山洞、拱门会被填平。我做过一个场景里面有个弧形石拱门。用Mesh Collider原始网格做静态碰撞没问题但桥上有一块可以推动的手推车拱形部件——好那它需要Rigidbody就得勾Convex一勾原本能推过拱门的空间直接被凸包填掉了一半。这个问题当时排查了很久才发现是Convex在作怪。3.2 复合碰撞器用简单形状拼出复杂碰撞既然Mesh Collider又慢又有凸包限制那项目中大量不规则物体怎么处理我的答案是优先使用复合碰撞器。所谓复合碰撞器就是在同一个物体下挂多个子物体每个子物体分别挂一个Box/Sphere/Capsule通过调整位置和大小拼出一个接近模型轮廓的碰撞组合。比如一个桌子桌面用一个大的Box四条腿用四个细长的Box然后统一调整位置碰撞效果就非常接近真实桌子而且计算成本极低。做车辆碰撞的时候我也是这么干的车身主体用一个大Box车头车尾配合模型的弧度稍微缩小一点四个轮子位置各加一个Sphere Collider。车辆翻滚、撞墙、过减速带的物理表现都很稳定完全不需要上Mesh Collider。3.3 天坑非均匀缩放与碰撞器形状失真有一个细节容易被忽略但极容易出bug如果父物体的Scale是非均匀的比如X轴拉长到10Y轴保持1子物体的Sphere Collider会变成椭球形物理引擎的Sphere碰撞计算是按标准球做的内部计算逻辑对非均匀缩放的支持在某些版本并不可靠会造成碰撞范围与视觉不一致。我记忆中遇到过这样的情况一个球体模型作为子物体放在一个不均匀缩放的父物体下运行游戏时子弹射向模型明明看着已经打中了中心区域却因为碰撞体实际是被拉扯过的椭球而没有命中。要避免这个问题要么缩放的偏移调整在碰撞器的参数上而不是挂在Transform上硬拉要么把需要物理的子物体放在一个Scale1的独立层级下。4. 一碰就穿模的场景什么时候必须用连续碰撞检测穿模这个问题几乎每个做物理的朋友都遇到过。子弹高速飞行直接穿过薄墙、角色快速冲刺穿过障碍物、FPS里狙击枪子弹穿过纸板——这些都不是碰撞器类型选错了而是碰撞检测模式没有针对高速物体做调整。4.1 离散碰撞检测为什么会让子弹穿墙所有碰撞检测都依赖一个采样原理机制每一帧物理引擎会把物体从上一帧的位置移动到当前位置然后判断这个位置和哪个碰撞器相交。问题在于如果物体移动速度足够快在这一帧的起始位置它还在墙的左侧在结束位置它已经跑到了墙的右侧。在物理引擎看来这两个采样点之间没有任何一个点在墙内部所以它完美地穿过了墙一点碰撞都不会触发。用图片来想象就是一个球在两张间隔很远的照片里第一张在墙左边第二张在墙右边墙只有10厘米厚而球一个帧间隔内运动了1米物理引擎的眼睛根本没捕捉到穿越瞬间。4.2 连续碰撞检测CCD的代价与正确打开方式连续碰撞检测Continuous Collision DetectionCCD解决的正是这个问题。它会额外计算物体在帧间隔内的运动路径然后拿这个路径与碰撞器做求交运算从而检测到高速下的穿透。但是CCD不是免费的。它的计算成本远高于离散检测而且部分引擎中只对Rigidbody的相对运动有效如果你的角色是用CharacterController或者直接修改Transform方式移动CCD可能不起作用。我在项目中一般这样规划角色自带的普通移动、近距离的敌人追击离散检测就够了没必要开CCD高速子弹、炮弹、投掷物开启CCD并配合collisionMode或CollisionDetectionMode.Continuous设置玩家冲刺穿透障碍物如果用的是Rigidbody.MovePosition实现位移必须开CCD如果用CharacterController则要修改移动逻辑分段移动或加射线检测4.3 高速碰撞的替代方案射线检测与分段移动CCD虽然有效但还不是所有高速运动的银弹。我做弹道类玩法时还会用射线检测Raycast作为补充每帧从子弹上一帧位置向当前帧位置发一条射线判断射线是否和墙面等阻挡物相交。射线检测本质上是对帧间路径的显式采样成本比CCD更可控而且可以直接给出命中点适合做精确的弹道模拟。在跳跃和角色下落场景里我也会做从上到下的射线检测或SphereCast来替代直接依赖重力驱动的碰撞响应以防止高速下坠时因为穿透而掉出地图。这种方法在2D平台跳跃里同样适用原理一致。5. 一碰就出事的实战排查从现象反推碰撞类型问题的完整链路遇到碰撞问题最怕的是一上来就改参数东试西试改了半天还找不到原因。下面我根据自己在项目里排查碰撞问题的真实经历整理了一条比较高效的排查链路。5.1 现象一物体直接掉出地面或者落下去又弹起来这种问题十有八九不是碰撞器类型选错的锅而是Rigidbody与Collider的组合状态不对。我在做一个跑酷Demo时玩家脚底的小土块莫名往下掉检查了半天发现土块的Collider是Box Collider没错Rigidbody也挂了重力也没问题。但有个致命细节——我把土块放到了一个动态移动的平台父物体下面而平台是用代码每帧改Transform.position移动的。由于父物体移动方式没有走Rigidbody物理接口子物体的世界坐标也被直接挪动物理引擎判断子物体的碰撞状态时出现了错乱于是土块穿过了地面。排查的方法是场景里暂时禁用脚本移动手动拖动物体测试如果正常了就是位移方式的问题。解决了之后我把平台的移动逻辑从直接修改Transform改为Rigidbody.MovePosition土块就稳了。5.2 现象二碰撞事件时有时无Trigger偶尔失灵做开关门、捡金币这类Trigger功能时偶尔出现进了区域不触发的情况。第一反应往往是去检查OnTriggerEnter的签名但其实更大概率是两个触发器之间有重叠区域或者物体的碰撞体类型和移动方式不匹配。我在一个潜行游戏里做敌人警戒圈用一个大大的Sphere Collider当Trigger挂在敌人身上跟着敌人移动。结果敌人巡逻时警戒圈时灵时不灵。排查后才发现问题敌人每帧用直接修改NavMeshAgent的nextPosition方式同步位置而Sphere Collider本身挂在敌人身体这个子物体上——这导致Collider的世界位置并没有被物理引擎完全感知到。改成让整个角色容器用NavMeshAgent自身的位移后Trigger就稳定了。5.3 现象三物体推不动、推歪、或者推着推着飞出去了场景里的可推动箱子和障碍物交互时表现怪异多半是Rigidbody参数或碰撞体形态互相干扰。比如Box Collider的高度和模型有细微偏差导致箱子微观悬浮摩擦力算不对推起来抖抖索索。解决的方法是先把碰撞体的尺寸和模型对齐到小数点后两位然后设置Rigidbody的interpolation为Interpolate或Extrapolate减少物理引擎和渲染帧之间的抖动感。推动手感还需要注意constraints里的旋转锁定防止箱子推着推着因为和墙角单点接触开始翻滚。5.4 常见碰撞类型问题速查表在多次排错之后我沉淀了一张简单的问题速查表方便快速定位问题方向现象最可能的原因优先检查项完全无碰撞直接穿透静止障碍物物体用了非物理位移或缺少Rigidbody移动方式是否走物理接口碰撞体是否开启层碰撞矩阵高速物体穿透薄墙碰撞检测模式为离散检测是否开启CCD或补充射线检测碰撞触发忽有忽无碰撞体跟随的物体位移方式不对是否直接改Transform而非物理位移角色被透明墙挡住Trigger和碰撞阻挡概念混淆检查isTrigger开关动态物体无法正常碰撞Mesh Collider未勾选Convex改为复合碰撞器用基础形状拼层碰撞矩阵Layer Collision Matrix也是我几乎每次排查都会顺手检查的项。在Unity的Edit Project Settings Physics里两个层如果不勾选并且之间能发生碰撞那他们之间的碰撞事件会整个被忽略这样的问题非常隐蔽因为你在场景里完全看不出碰撞器有什么问题。项目中非常建议从一开始就规划好层名的用途把玩家、敌人、环境、交互物分配到不同层避免后期碰撞矩阵一团糟。6. 碰撞性能预算一个场景里到底能塞多少碰撞器碰撞器类型选得对只是第一步。一个在线游戏关卡里可能有几百个物体需要碰撞交互每帧的碰撞检测开销如果失控浮点数运算量会直接拖垮帧率。这块我单独提出来讲是因为选型不只是精度问题更是性能问题。6.1 静态物体用多子碰撞体时的开销估算场景里墙壁、地面、柱子这类静态碰撞体数量再多都不太吃性能因为物理引擎会自动把静态碰撞体放入空间划分结构比如BVH、四叉树之类的加速结构里不会逐个和其他物体做穷举判断。真正吃性能的是动态碰撞体之间的碰撞。两个带Rigidbody的物体碰撞引擎需要实时计算它们的接触点、接触法线、摩擦力等。如果场景中有几十个动态物体同时运动动态物体之间的配对计算就会爆发式增长。我在做物理谜题关卡时有一个经验动态物体数量尽量控制在15个以内超过这个量物理计算的时间开销会明显上浮。如果确实需要大量动态物件比如堆箱子玩法就尽量让它们只有碰撞检测不参与完整物理响应使用IsKinematic或者RigidbodyType2D.Static之类的模式降低计算量。6.2 碰撞体数量与物理调优像做烘焙一样精简单体场景搭建阶段我常做一个动作把所有静态环境物体统一检查一遍凡是不会和玩家交互的装饰性碰撞体直接删掉Collider组件。有些朋友会在每一面墙、每一个柱子、每一个台阶上都加Cube Collider这其实是浪费物理开销。整面墙只需要一个大的Box Collider台阶可以用一个阶梯状的Box组合甚至一个斜面用单个Box就完全够用。另外在碰撞类型允许的情况下尽量用少量大碰撞体替代多个小碰撞体。物理引擎的两个物体之间存在接触对计算一体积内的小碰撞体数量过多会导致同一物体要和同一个墙面生成多个接触点白白增加计算量。6.3 何时可以不使用物理引擎自带的碰撞还需要知道一个反直觉的结论不是所有碰撞都必须依赖物理引擎。做2D俯视角游戏时很多物体的碰撞我们根本不需要引擎级响应只需要简单的距离判断和矩形相交判断自己写几个函数性能反而比引用物理引擎高得多。类似地UI交互里的点击碰撞、角色靠近某个触发点这些用距离计算解决会更轻量。物理学上的碰撞响应反弹、摩擦、翻滚才是物理引擎擅长且必要的部分。决定用哪种碰撞方案时心里先问一句这个物体需要真实的物理反馈吗如果不需要就别拖物理引擎下水。这个思路能帮助大家从整体上把关好项目性能避免盲目堆碰撞器。最后再分享一点我的实战体会做了这么多项目有种感受越来越深碰撞类型选型表面上是技术问题本质上其实是需求理解问题。在给一个模型配Collider之前先想清楚这个物体在游戏里承担什么功能、它需要什么级别的物理反馈、它会不会高速移动、它会和哪几类物体交互把这些想清楚碰撞类型的选择自然就浮出水面了。我自己的习惯是每次新建一个重要物体时先花30秒把这个清单过一遍然后在Scene视图里开线框模式Wireframe仔细确认碰撞体和模型轮廓的贴合程度再运行场景做几个经典动作验证跳上平台、快速冲刺、从侧面撞击基本能避免大多数后期返工。希望这些实战经验能给你的项目带来实际帮助。