ARTICLE DETAIL

建站实战干货

来自一线的建站与推广经验沉淀,每一条都经过真实交付验证。

Unity物理交互核心:Collider、Rigidbody、Kinematic与Trigger的黄金法则

2026/8/9 14:05:17 拓冰建站 浏览量
Unity物理交互核心:Collider、Rigidbody、Kinematic与Trigger的黄金法则 1. 项目概述为什么你的物理交互总在“抽风”干了这么多年Unity开发我敢说超过一半的物理交互Bug根源都出在对Collider、Rigidbody、Kinematic和Trigger这四兄弟关系的误解上。你肯定遇到过角色明明穿墙了OnCollisionEnter却死活不触发一个本该静止的平台却因为一阵风或者一个微小的力开始鬼畜抖动两个物体明明重叠了却像幽灵一样互相穿过……这些问题往往不是代码逻辑的错而是你对Unity物理引擎底层规则的理解和引擎实际执行的规则出现了“认知偏差”。这篇指南就是来帮你校准这个偏差的。我不会只告诉你“Kinematic的刚体不会与Static Collider产生碰撞检测”这种结论我会带你拆解Unity物理引擎默认是NVIDIA PhysX处理这些组件时的“潜规则”和“黄金法则”。你会发现很多网上流传的“偏方”和“奇技淫巧”其实都源于对官方文档中一张核心表格的误读或忽视。我们将围绕这张碰撞检测矩阵表结合大量实战中的坑把原理掰开揉碎了讲清楚。无论你是正在为移动平台碰撞烦恼的初学者还是被复杂物理交互搞得焦头烂额的老手这篇文章都能帮你建立起一套清晰、可预测的物理交互心智模型让你从此告别“瞎调”实现精准控制。2. 核心四要素拆解Unity物理交互的基石在深入避坑之前我们必须统一语言精确理解每个核心组件在物理世界中的“人设”和“职责”。很多混乱都源于对这些基础概念的模糊认知。2.1 Collider物理世界的“形状定义者”Collider决定了游戏对象在物理世界中的形状和体积。它只是一个几何描述本身没有质量、不会运动也不参与物理模拟的动力学计算。你可以把它理解为一个“幽灵轮廓”。关键认知误区纠正误区“只要有Collider物体就能被撞到。”现实Collider单独存在时它只是一个“场景装饰品”。两个都只有Collider且没有Rigidbody的物体在物理引擎看来它们就是场景里两坨静止的几何体彼此之间不会产生任何碰撞检测事件OnCollisionXXX。它们会互相穿透就像不存在一样。物理引擎只关心“会动的东西”和“不会动的东西”之间或者“会动的东西”彼此之间的关系。实战心得对于永远静止的背景元素如地面、墙壁、不可移动的装饰物如果确定不需要任何碰撞检测比如仅仅用于阻挡玩家那么只挂一个Collider标记为Static是最省性能的。但一旦你需要知道“有什么东西碰到了这面墙”你就必须引入Rigidbody。2.2 Rigidbody物理世界的“参与者”与“受力者”Rigidbody是让游戏对象进入物理模拟世界的“门票”。它赋予了对象质量、阻力、重力感应等物理属性并使其受到力的影响如AddForce。物理引擎的核心就是计算每个拥有Rigidbody的物体在力作用下的运动状态。核心属性拆解Mass质量影响惯性力和碰撞后的运动。质量比为0的极端情况会导致计算不稳定。Drag/Angular Drag阻力/角阻力模拟空气阻力让运动自然停止。数值过小物体会滑动很久过大则像在泥沼中运动。Use Gravity使用重力是否受全局重力设置影响。Is Kinematic是否为运动学刚体这是本文的关键分歧点。勾选后该刚体将不受任何物理力的影响重力、爆炸力、碰撞力等。它的运动完全由你的代码通过直接修改Transform.position或动画系统驱动。它从物理世界的“被动响应者”变成了“主动规则制定者”。2.3 Kinematic从“棋子”到“棋盘”的转变这是最容易被误解的属性。很多人把Kinematic简单地理解为“不受物理影响”这没错但更关键的是它如何与其他物体交互。黄金法则一Kinematic刚体与碰撞检测一个被设置为Kinematic的Rigidbody在Unity默认的碰撞检测规则下Project Settings Physics不会与同样是Static无Rigidbody的Collider产生碰撞检测消息OnCollisionEnter/Stay/Exit。这就是开头那个论坛问题的根源。引擎认为“你Kinematic自己决定怎么动那些静止的Static东西也不会影响你所以我就不费心给你们做碰撞检测报告了。”但是这并不意味着它们会互相穿透。在物理模拟中Kinematic刚体仍然会推开施加力给其他非Kinematic的刚体Dynamic Rigidbody。它就像一个无敌的、自己会动的推土机能影响别人但别人和静止的景物无法通过标准的碰撞回调函数通知它。为什么需要它典型用例玩家角色用代码控制移动、移动平台用动画或脚本规定路径、需要精确穿透检测的子弹有时。你希望这些物体按照既定逻辑运动而不被物理力干扰比如不会被一个箱子轻易撞歪。2.4 Trigger从“碰撞”到“感知”的模式切换在Collider组件上勾选Is Trigger就彻底改变了这个Collider的使命。它从一个实心的、会阻挡的物理体积变成了一个虚化的、无形的感知区域。黄金法则二Trigger的交互逻辑穿透性Trigger不会产生物理阻挡。其他物体可以自由穿过它。检测性当另一个Collider无论它有没有Rigidbody是什么类型进入、停留或离开它的体积时只要双方至少有一个带了Rigidbody就会触发OnTriggerEnter/Stay/Exit回调函数。无物理信息Trigger回调函数传递的是对方Collider的引用而不是Collision对象。这意味着你无法直接获取碰撞点、法线、冲击力等详细的物理信息。常见应用场景收集品区域玩家进入一个区域后拾取金币。触发器机关进入某个区域后开门、刷怪。技能范围判定判断敌人是否在某个AOE技能范围内。注意Trigger和Collider是互斥的。一个Collider要么是触发器只检测不阻挡要么是碰撞器可阻挡检测取决于Rigidbody配置。不能同时承担两种角色。3. 碰撞检测矩阵Unity物理世界的“宪法”上面所有零散的规则最终都凝结在Unity官方文档的一张“碰撞检测矩阵”中。理解这张表是根治所有物理交互玄学问题的关键。我们不是背下来而是理解其背后的设计逻辑。矩阵的核心是两个物体能否产生碰撞/触发事件取决于它们各自的“身份”。这个身份由两个维度决定是否有Rigidbody如果有是Dynamic还是KinematicCollider是普通模式还是Trigger模式基于网络资料和官方文档我们可以总结出以下核心交互规则表适用于默认Physics设置物体A (发起方)物体B (接收方)是否物理阻挡是否触发 OnCollision是否触发 OnTrigger说明与典型场景Dynamic Rigidbody(普通刚体)Static Collider(无刚体静态碰撞器)是是取决于Trigger设置最经典场景玩家Dynamic撞到墙壁Static。有阻挡有碰撞事件。Dynamic RigidbodyDynamic Rigidbody是是取决于Trigger设置两个物理物体互撞如保龄球撞瓶子。双方都受物理力影响。Dynamic RigidbodyKinematic Rigidbody是是(B可收到)取决于Trigger设置Dynamic撞上移动平台Kinematic。平台不动如山代码控制但Dynamic会被弹开且平台能收到碰撞事件。Kinematic RigidbodyStatic Collider否(默认)否(默认)取决于Trigger设置经典大坑代码控制的角色Kinematic穿墙而过且收不到碰撞通知。这是很多新手困惑的根源。Kinematic RigidbodyDynamic Rigidbody是(A推动B)是(B可收到)取决于Trigger设置移动平台Kinematic推动一个箱子Dynamic。箱子被推动平台也能知道撞到了箱子。Kinematic RigidbodyKinematic Rigidbody否否取决于Trigger设置两个代码控制的物体物理引擎不管它们的交互默认互相穿过。任何带Rigidbody的物体任何带Collider的物体(一方或双方为Trigger)否否是(如果体积相交)Trigger交互的核心规则。只要体积重叠就触发OnTrigger事件与刚体类型无关。仅Collider (无Rigidbody)仅Collider (无Rigidbody)否否否两个纯静态物体物理引擎完全忽略它们的交互。这张表怎么用当你遇到碰撞检测问题时先对发生交互的两个物体进行“身份鉴定”然后查表。比如“我的玩家Kinematic为什么撞不到墙Static” 查表第4行立刻知道默认情况下既无阻挡也无事件问题定位完成。项目设置中的关键开关从Unity 2017.3开始在Edit - Project Settings - Physics中多了一个Contacts Generation接触点生成选项。其中有一个子选项叫Enable Kinematic-Static Pairs。取消勾选默认遵循上表Kinematic与Static不产生碰撞检测。勾选允许Kinematic刚体与Static Collider生成接触点从而可以触发OnCollision事件。但这会带来额外的性能开销因为引擎需要为这些原本被忽略的组合进行计算。除非确有必要否则不建议全局开启。4. 实战避坑指南从理论到代码的精准落地理解了宪法我们来看看在具体的开发场景中如何应用和规避这些规则。4.1 场景一制作一个受代码控制且能感知墙壁的玩家角色这是最常见的需求。玩家移动由输入控制但需要被墙壁阻挡并且我们可能需要在撞墙时播放声音或特效。错误做法经典坑给玩家添加Rigidbody勾选Is Kinematic为了让代码控制移动。给墙壁添加一个Box Collider。在玩家的脚本里写OnCollisionEnter函数。结果玩家穿墙而过OnCollisionEnter静默无声。原因分析根据矩阵表Kinematic RigidbodyvsStatic Collider默认无碰撞事件。正确解决方案根据需求选择方案A需要物理阻挡和碰撞事件推荐玩家Rigidbody不勾选Is Kinematic保持为Dynamic。在Rigidbody的Constraints约束中冻结所有位置Freeze Position X, Y, Z和旋转Freeze Rotation X, Y, Z。通过代码使用Rigidbody.MovePosition或Rigidbody.velocity来移动玩家而不是直接修改Transform.position。public class PlayerController : MonoBehaviour { public float moveSpeed 5f; private Rigidbody rb; void Start() { rb GetComponentRigidbody(); // 通过约束而非Kinematic来禁止物理影响 rb.constraints RigidbodyConstraints.FreezePositionZ | RigidbodyConstraints.FreezeRotation; } void FixedUpdate() { float h Input.GetAxis(Horizontal); float v Input.GetAxis(Vertical); Vector3 movement new Vector3(h, 0, v) * moveSpeed * Time.fixedDeltaTime; // 使用MovePosition它会在移动时考虑碰撞 rb.MovePosition(rb.position movement); // 或者使用velocity // rb.velocity new Vector3(h, 0, v) * moveSpeed; } // 现在这个函数会正常触发了 void OnCollisionEnter(Collision collision) { if (collision.gameObject.CompareTag(Wall)) { Debug.Log(撞到墙了); // 播放撞击音效... } } }为什么可行物体仍然是Dynamic刚体物理引擎会正常计算它与其他所有Collider包括Static的碰撞。但由于所有位置和旋转被冻结重力、碰撞力等都无法改变它的位置和旋转状态实现了和Kinematic类似的“不受物理力影响”的效果。同时碰撞检测流程完全正常。方案B只需要感知不需要物理阻挡例如2D顶视角游戏玩家和墙壁的Collider都设置为Is Trigger。玩家可以保持为Kinematic刚体。使用OnTriggerEnter来检测进入墙壁区域。void OnTriggerEnter(Collider other) { if (other.CompareTag(Wall)) { Debug.Log(进入墙壁区域可以在这里处理减速、扣血等逻辑); // 注意玩家会直接穿过去你需要自己写逻辑来回退位置或处理伤害 } }方案C必须用Kinematic且必须和Static碰撞特殊需求确保在Project Settings - Physics中勾选了Enable Kinematic-Static Pairs。玩家设置为Kinematic刚体。此时OnCollisionEnter可以与Static Collider正常触发。代价性能开销增加。4.2 场景二制作一个移动平台既能载人又能检测谁上来了移动平台通常由动画或脚本控制路径它不应该被角色推走但需要承载角色并且我们可能想知道平台上站了谁。正确做法平台添加Rigidbody并勾选Is Kinematic。这样它按既定路径移动不受角色影响。平台添加Collider如Box Collider。角色使用普通的Dynamic Rigidbody不冻结约束或只冻结部分。交互逻辑物理阻挡当平台Kinematic撞到角色Dynamic时根据矩阵表会发生物理阻挡平台推动角色。角色会被平台推着走实现“载人”效果。碰撞检测平台Kinematic能通过OnCollisionEnter收到与角色Dynamic碰撞的事件。角色也能收到与平台碰撞的事件。public class MovingPlatform : MonoBehaviour { private void OnCollisionEnter(Collision collision) { // 检测到有物体比如玩家站上了平台 if (collision.gameObject.CompareTag(Player)) { Debug.Log(${collision.gameObject.name} 登上了平台); // 可以将玩家设置为平台的子物体让玩家随平台移动 collision.transform.SetParent(transform); } } private void OnCollisionExit(Collision collision) { // 玩家离开平台 if (collision.gameObject.CompareTag(Player)) { Debug.Log(${collision.gameObject.name} 离开了平台); collision.transform.SetParent(null); } } }注意事项将玩家设为平台的子对象是让玩家随平台移动的简单方法。但更健壮的做法是在FixedUpdate中计算平台本帧的位移差然后手动施加给平台上所有角色这样可以避免缩放等父子关系带来的副作用。4.3 场景三技能范围检测与子弹碰撞这是一个Trigger的典型应用场景但也容易混淆。需求A一个圆形火焰AOE技能范围内的敌人持续受伤。做法创建一个Sphere Collider勾选Is Trigger挂在技能特效对象上。该对象不需要Rigidbody但建议加一个并设置为Kinematic以确保触发检测的稳定性。逻辑在OnTriggerStay中每帧或每隔一段时间对范围内的敌人通过Tag或Layer判断造成伤害。关键点因为Collider是Trigger敌人可以站在里面不会受到物理阻挡。需求B一发需要精确命中点和法线信息的子弹例如用于计算反弹轨迹。做法子弹使用Dynamic或Kinematic Rigidbody根据是否需要受重力影响Collider不勾选Is Trigger。逻辑使用OnCollisionEnter获取Collision对象从中提取contacts[0].point碰撞点和contacts[0].normal法线来进行反弹计算。为什么不用Trigger因为Trigger无法提供碰撞点和法线信息。需求C一发只需要知道是否命中不需要物理效果的子弹。做法1传统同需求B使用非Trigger Collider和OnCollisionEnter。简单直接。做法2高性能子弹使用Kinematic Rigidbody在每帧移动前使用Physics.SweepTest或Rigidbody.SweepTest进行扫描检测。这种方法可以提前获取碰撞信息并且比等待物理引擎回调更可控性能也更好常用于需要大量抛射体的游戏。public class Projectile : MonoBehaviour { public float speed 50f; private Rigidbody rb; private Vector3 previousPosition; void Start() { rb GetComponentRigidbody(); rb.isKinematic true; previousPosition transform.position; } void FixedUpdate() { Vector3 movement transform.forward * speed * Time.fixedDeltaTime; Vector3 newPosition previousPosition movement; // 使用SweepTest检测从上一帧位置到新位置之间是否会发生碰撞 if (rb.SweepTest(transform.forward, out RaycastHit hit, movement.magnitude)) { // 如果检测到碰撞处理命中逻辑 Debug.Log($击中: {hit.collider.name}, 点: {hit.point}, 法线: {hit.normal}); // 生成命中特效销毁子弹等... Destroy(gameObject); return; } // 没有碰撞则安全移动 rb.MovePosition(newPosition); previousPosition newPosition; } }5. 性能优化与高级技巧物理计算是性能消耗大户尤其是在移动平台或对象数量多的场景中。遵循以下法则可以显著提升效率。5.1 Layer与Collision Matrix精细控制谁和谁碰撞让所有物体都和所有其他物体进行碰撞检测是极其浪费的。Unity的Layer图层和Collision Matrix碰撞矩阵就是用来做精细化管理的。最佳实践定义清晰的Layer例如Player,Enemy,Bullet,Environment,TriggerZone,IgnoreRaycast等。编辑碰撞矩阵进入Edit - Project Settings - Physics找到Layer Collision Matrix。这是一个NxN的表格勾选表示这两个Layer的物体会进行物理碰撞计算取消则完全忽略。合理配置子弹(Bullet)不需要和环境(Environment)碰撞除非是穿墙弹可以取消勾选。敌人(Enemy)之间可能不需要互相碰撞避免堆叠卡住可以取消勾选。触发器区域(TriggerZone)可能只需要和玩家(Player)交互可以只勾选与Player的交叉格。这样做的好处物理引擎每帧需要检测的碰撞对数量大幅减少性能提升立竿见影。5.2 Static vs Dynamic Collider理解背后的优化Static Collider被标记为Static在Inspector顶部勾选或没有Rigidbody且位置不变的碰撞器在场景加载或静态批次处理时其几何信息会被优化并存入一个空间数据结构如BVH树中用于快速进行射线检测和与动态物体的碰撞检测。它的位置变化后需要重新进行静态批次处理否则碰撞会出错。Dynamic Collider附着在带有Rigidbody的对象上的碰撞器。引擎会持续跟踪其位置和旋转计算成本更高。经验法则对于永远不会移动的物体山体、建筑确保其为Static无Rigidbody。对于会移动的物体即使一开始静止也加上Rigidbody并设置为Kinematic或冻结约束不要依赖Static标记。5.3 复杂碰撞体与简单碰撞体Mesh Collider最精确但性能消耗最大。尽量少用尤其避免在移动物体上使用复杂的Mesh Collider。可以勾选Convex凸包来简化计算但非凸网格不能用于Dynamic-Dynamic碰撞。Primitive ColliderBox, Sphere, Capsule性能极佳。黄金法则永远优先使用基本碰撞体组合来近似复杂形状。例如一个人物可以用一个Capsule身体几个Sphere四肢来组合这比用一个Mesh Collider高效得多。Compound Colliders复合碰撞体在一个GameObject下添加多个子物体每个子物体有自己的基本Collider父物体有Rigidbody。这是构建复杂物理形状的标准且高效的方法。5.4 连续碰撞检测CCD对于高速移动的物体如子弹可能会在单帧内穿过另一个薄物体导致碰撞检测失败“隧道效应”。解决在Rigidbody组件上设置Collision Detection属性。Discrete离散默认性能好可能穿模。Continuous连续对Dynamic物体有效性能开销大。Continuous Dynamic连续动态对Dynamic和Kinematic都有效开销最大。建议只对少数高速运动的物体如炮弹、玩家角色启用Continuous或Continuous Dynamic不要全局启用。6. 常见问题排查清单QA当你遇到物理交互问题时请按以下清单逐一排查Q1OnCollisionEnter为什么没触发检查双方Collider确保两个物体都有Collider组件且没有勾选Is Trigger。检查Rigidbody确保至少一方拥有Rigidbody。如果双方都是纯Collider无Rigidbody永远不会触发。检查Kinematic与Static组合如果一方是Kinematic另一方是Static Collider无Rigidbody默认不会触发。考虑使用“冻结约束的Dynamic刚体”方案或开启Enable Kinematic-Static Pairs。检查Layer Collision Matrix确保这两个物体所在的Layer在碰撞矩阵中是勾选状态。检查脚本和函数名脚本必须挂载在拥有Rigidbody的物体上函数名拼写必须完全正确大小写敏感且为public或privatevoid。检查物体是否静止如果两个物体一开始就重叠在一起OnCollisionEnter可能不会在开始时调用。OnCollisionStay可能会被调用。Q2OnTriggerEnter为什么没触发检查Trigger设置确保至少一个相关的Collider勾选了Is Trigger。检查Rigidbody确保至少一方拥有Rigidbody任何类型均可。两个都是纯Trigger Collider无Rigidbody不会触发。检查Layer Collision Matrix同上Layer必须允许交互。检查脚本和函数名同上。Q3物体为什么抖动或者缓慢下陷检查碰撞体比例和位置确保碰撞体大小合适且与视觉模型对齐。未对齐的碰撞体可能导致接触点计算不稳定。检查质量比碰撞双方的质量Mass不要相差过于悬殊例如1000:1。尝试将质量比控制在10:1以内。检查堆叠多个物体堆叠时容易不稳定。可以适当增加Physics.defaultContactOffset项目设置中或物体的Collider.contactOffset但不要太大。使用Interpolate插值在Rigidbody上启用Interpolation插值可以平滑基于物理的运动减少抖动。通常对主角摄像机跟随的物体设置。Q4移动平台上的角色为什么滑落或不稳父子关系问题如果使用SetParent将角色设为平台子物体注意平台的旋转和缩放可能会意外影响角色。考虑使用CharacterController或手动计算位移差来移动角色。碰撞体形状确保角色的碰撞体如Capsule底部与平台表面有良好的接触。有时需要微调碰撞体的位置或高度。物理材质检查角色和平台的物理材质Physics Material。过低的摩擦力Dynamic Friction可能导致打滑。可以创建一个物理材质适当提高摩擦力。Q5性能突然下降如何定位物理开销使用Profiler打开Unity Profiler (Window Analysis Profiler)查看Physics.Process所占用的CPU时间。如果峰值很高说明物理计算是瓶颈。检查对象数量场景中Dynamic Rigidbody的数量是性能的关键。尽量减少同时活动的物理物体。检查碰撞矩阵确保没有不必要的Layer间碰撞被启用。检查复杂碰撞体用简单碰撞体替代Mesh Collider。将远处或不可见的物体设为休眠Rigidbody在静止一段时间后会进入休眠Sleeping状态停止计算。确保你的逻辑不会频繁唤醒它们。也可以手动设置Rigidbody.Sleep()或WakeUp()。物理交互的调试往往需要耐心和系统性的排查。记住核心的“身份矩阵”从组件的配置出发结合Profiler工具大部分“玄学”问题都能找到清晰的根源。最终稳定可靠的物理效果来自于对引擎规则的深刻理解和严谨的配置而不是漫无目的的参数调整。